diff --git a/charybdis/console.cc b/charybdis/console.cc index a8fb03855..009a1c59c 100644 --- a/charybdis/console.cc +++ b/charybdis/console.cc @@ -360,44 +360,42 @@ try const auto &what(token.at(0)); const auto &host{token.at(1)}; const auto &port{token.at(2)}; - const net::hostport hostport - { - host, port - }; - switch(hash(what)) { - case hash("ips"): + case hash("A"): { - ctx::future> future; - net::resolve{hostport, future}; - const auto ips{future.get()}; - for(const auto &ip : ips) - std::cout << ip << std::endl; + const net::hostport hostport + { + host, port + }; - break; - } + auto future + { + net::resolve(hostport) + }; - case hash("ip"): - { - ctx::future future; - net::resolve{hostport, future}; std::cout << future.get() << std::endl; break; } - case hash("ptr"): + case hash("PTR"): { - net::ipport in[1] + const net::hostport hostport { - net::hostport { host, port }, + host, port }; - std::string out[1]; - net::resolve{in, out}; - for(const auto &ptr : out) - std::cout << ptr << std::endl; + const net::ipport ipport + { + hostport + }; + auto future + { + net::resolve(ipport) + }; + + std::cout << future.get() << std::endl; break; } } diff --git a/include/ircd/net/resolve.h b/include/ircd/net/resolve.h index 2ccd1acff..4429ec3ba 100644 --- a/include/ircd/net/resolve.h +++ b/include/ircd/net/resolve.h @@ -24,25 +24,30 @@ namespace ircd::net { - struct resolve; + struct resolve extern resolve; } /// DNS resolution suite. /// -/// There are plenty of ways to resolve plenty of things. Still more to come. +/// This is a singleton class; public usage is to make calls on the singleton +/// object like `ircd::net::resolve()` etc. +/// struct ircd::net::resolve { + // Internal resolver service instance + struct resolver static resolver; + + public: using callback_one = std::function; - using callback_many = std::function)>; + using callback_many = std::function)>; using callback_reverse = std::function; - resolve(const hostport &, callback_one); - resolve(const hostport &, callback_many); - resolve(const ipport &, callback_reverse); + // Callback-based interface + void operator()(const hostport &, callback_one); + void operator()(const hostport &, callback_many); + void operator()(const ipport &, callback_reverse); - resolve(const hostport &, ctx::future &); - resolve(const hostport &, ctx::future> &); - - resolve(const vector_view &in, const vector_view &out); - resolve(const vector_view &in, const vector_view &out); + // Future-based interface + ctx::future operator()(const hostport &); + ctx::future operator()(const ipport &); }; diff --git a/ircd/net.cc b/ircd/net.cc index 0adcca052..ecffbce13 100644 --- a/ircd/net.cc +++ b/ircd/net.cc @@ -21,16 +21,23 @@ #include +/// Internal pimpl wrapping an instance of boost's resolver service. This +/// class is a singleton with the instance as a static member of +/// ircd::net::resolve. This service requires a valid ircd::ios which is not +/// available during static initialization; instead it is tied to net::init. +struct ircd::net::resolver +:std::unique_ptr +{ + resolver() = default; + ~resolver() noexcept = default; +}; + /////////////////////////////////////////////////////////////////////////////// // // net/net.h // -namespace ircd::net -{ - ip::tcp::resolver *resolver; -} - +/// Network subsystem log facility with dedicated SNOMASK. struct ircd::log::log ircd::net::log { @@ -38,19 +45,16 @@ ircd::net::log }; /// Network subsystem initialization -/// ircd::net::init::init() { - net::resolver = new ip::tcp::resolver{*ircd::ios}; + assert(ircd::ios); + resolve::resolver.reset(new ip::tcp::resolver{*ircd::ios}); } /// Network subsystem shutdown -/// ircd::net::init::~init() { - assert(net::resolver); - delete net::resolver; - net::resolver = nullptr; + resolve::resolver.reset(nullptr); } // @@ -1375,60 +1379,46 @@ namespace ircd::net void _resolve(const ipport &, resolve_callback); } -ircd::net::resolve::resolve(const vector_view &in, - const vector_view &out) +/// Singleton instance of the public interface ircd::net::resolve +decltype(ircd::net::resolve) +ircd::net::resolve { - assert(in.size() == out.size()); +}; - size_t a{0}, b{0}; - ctx::ctx &c{ctx::cur()}; - const size_t count{std::min(in.size(), out.size())}; - for(; a < count; ++a) resolve +/// Singleton instance of the internal boost resolver wrapper. +decltype(ircd::net::resolve::resolver) +ircd::net::resolve::resolver +{ +}; + +/// Resolve a numerical address to a hostname string. This is a PTR record +/// query or 'reverse DNS' lookup. +ircd::ctx::future +ircd::net::resolve::operator()(const ipport &ipport) +{ + ctx::promise p; + ctx::future ret{p}; + operator()(ipport, [p(std::move(p))] + (std::exception_ptr eptr, std::string ptr) + mutable { - in[a], [&b, &c, &count, &ret(out[a])] - (std::exception_ptr eptr, std::string hostname) - noexcept - { - ret = std::move(hostname); - if(++b >= count) - notify(c); - } - }; + if(eptr) + p.set_exception(std::move(eptr)); + else + p.set_value(ptr); + }); - while(b < count) - ctx::wait(); + return ret; } -ircd::net::resolve::resolve(const vector_view &in, - const vector_view &out) -{ - assert(in.size() == out.size()); - - size_t a{0}, b{0}; - ctx::ctx &c{ctx::cur()}; - const size_t count{std::min(in.size(), out.size())}; - for(; a < count; ++a) resolve - { - in[a], [&b, &c, &count, &ret(out[a])] - (std::exception_ptr eptr, const ipport &ip) - noexcept - { - ret = ip; - if(++b >= count) - notify(c); - } - }; - - while(b < count) - ctx::wait(); -} - -ircd::net::resolve::resolve(const hostport &hostport, - ctx::future &future) +/// Resolve a hostname (with service name/portnum) to a numerical address. This +/// is an A or AAAA query (with automatic SRV query) returning a single result. +ircd::ctx::future +ircd::net::resolve::operator()(const hostport &hostport) { ctx::promise p; - future = ctx::future{p}; - resolve(hostport, [p(std::move(p))] + ctx::future ret{p}; + operator()(hostport, [p(std::move(p))] (std::exception_ptr eptr, const ipport &ip) mutable { @@ -1437,26 +1427,15 @@ ircd::net::resolve::resolve(const hostport &hostport, else p.set_value(ip); }); + + return ret; } -ircd::net::resolve::resolve(const hostport &hostport, - ctx::future> &future) -{ - ctx::promise> p; - future = ctx::future>{p}; - resolve(hostport, [p(std::move(p))] - (std::exception_ptr eptr, const vector_view &ips) - mutable - { - if(eptr) - p.set_exception(std::move(eptr)); - else - p.set_value(std::vector(begin(ips), end(ips))); - }); -} - -ircd::net::resolve::resolve(const ipport &ipport, - callback_reverse callback) +/// Lower-level PTR query (i.e "reverse DNS") with asynchronous callback +/// interface. +void +ircd::net::resolve::operator()(const ipport &ipport, + callback_reverse callback) { _resolve(ipport, [callback(std::move(callback))] (std::exception_ptr eptr, ip::tcp::resolver::results_type results) @@ -1473,24 +1452,11 @@ ircd::net::resolve::resolve(const ipport &ipport, }); } -ircd::net::resolve::resolve(const hostport &hostport, - callback_one callback) -{ - resolve(hostport, [callback(std::move(callback))] - (std::exception_ptr eptr, const vector_view &ips) - { - if(eptr) - return callback(std::move(eptr), {}); - - if(ips.empty()) - return callback(std::make_exception_ptr(nxdomain{}), {}); - - callback(std::move(eptr), ips.at(0)); - }); -} - -ircd::net::resolve::resolve(const hostport &hostport, - callback_many callback) +/// Lower-level A or AAAA query (with automatic SRV query) with asynchronous +/// callback interface. This returns only one result. +void +ircd::net::resolve::operator()(const hostport &hostport, + callback_one callback) { static const ip::tcp::resolver::flags flags{}; _resolve(hostport, flags, [callback(std::move(callback))] @@ -1499,21 +1465,35 @@ ircd::net::resolve::resolve(const hostport &hostport, if(eptr) return callback(std::move(eptr), {}); - static const size_t max{32}; - const size_t count - { - results.size() - }; + if(results.empty()) + return callback(std::make_exception_ptr(nxdomain{}), {}); - ipport vector[max]; - std::transform(begin(results), end(results), vector, [] + const auto &result(*begin(results)); + callback(std::move(eptr), make_ipport(result)); + }); +} + +/// Lower-level A+AAAA query (with automatic SRV query). This returns a vector +/// of all results in the callback. +void +ircd::net::resolve::operator()(const hostport &hostport, + callback_many callback) +{ + static const ip::tcp::resolver::flags flags{}; + _resolve(hostport, flags, [callback(std::move(callback))] + (std::exception_ptr eptr, ip::tcp::resolver::results_type results) + { + if(eptr) + return callback(std::move(eptr), {}); + + std::vector vector(results.size()); + std::transform(begin(results), end(results), begin(vector), [] (const auto &entry) { return make_ipport(entry.endpoint()); }); - assert(!eptr); - callback(std::move(eptr), vector); + callback(std::move(eptr), std::move(vector)); }); } @@ -1543,8 +1523,8 @@ ircd::net::_resolve(const hostport &hostport, // This base handler will provide exception guarantees for the entire stack. // It may invoke callback twice in the case when callback throws unhandled, // but the latter invocation will always have an the eptr set. - assert(bool(ircd::net::resolver)); - resolver->async_resolve(host, port, flags, [callback(std::move(callback))] + assert(bool(ircd::net::resolve::resolver)); + resolve::resolver->async_resolve(host, port, flags, [callback(std::move(callback))] (const error_code &ec, ip::tcp::resolver::results_type results) noexcept { @@ -1568,8 +1548,8 @@ void ircd::net::_resolve(const ipport &ipport, resolve_callback callback) { - assert(bool(ircd::net::resolver)); - resolver->async_resolve(make_endpoint(ipport), [callback(std::move(callback))] + assert(bool(ircd::net::resolve::resolver)); + resolve::resolver->async_resolve(make_endpoint(ipport), [callback(std::move(callback))] (const error_code &ec, ip::tcp::resolver::results_type results) noexcept { @@ -1743,8 +1723,11 @@ ircd::net::make_endpoint(const ipport &ipport) ircd::net::ipport::ipport(const hostport &hp) { - ctx::future future; - resolve{hp, future}; + ctx::future future + { + resolve(hp) + }; + *this = future.get(); }