From 0eb16673eee705db33fd017d084df34d26613309 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Fri, 29 Dec 2017 15:55:26 -0700 Subject: [PATCH] ircd::net: Add resolve interface; Update remote interface. --- charybdis/console.cc | 37 +++ include/ircd/net/net.h | 16 +- include/ircd/net/remote.h | 122 ++++++---- ircd/client.cc | 12 +- ircd/net.cc | 473 +++++++++++++++----------------------- 5 files changed, 306 insertions(+), 354 deletions(-) diff --git a/charybdis/console.cc b/charybdis/console.cc index b64930f5a..1b2a772a9 100644 --- a/charybdis/console.cc +++ b/charybdis/console.cc @@ -353,6 +353,43 @@ try break; } + case hash("resolve"): + { + const auto args(tokens_after(line, " ", 0)); + const params token{args, " ", {"what", "host", "port"}}; + 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"): + { + ctx::future> future; + net::resolve{hostport, future}; + const auto ips{future.get()}; + for(const auto &ip : ips) + std::cout << ip << std::endl; + + break; + } + + case hash("ip"): + { + ctx::future future; + net::resolve{hostport, future}; + std::cout << future.get() << std::endl; + break; + } + } + + break; + } + case hash("reconnect"): { handle_line("disconnect"); diff --git a/include/ircd/net/net.h b/include/ircd/net/net.h index 4a0f99799..6541f8652 100644 --- a/include/ircd/net/net.h +++ b/include/ircd/net/net.h @@ -68,8 +68,6 @@ namespace ircd::net size_t available(const socket &) noexcept; ipport local_ipport(const socket &) noexcept; ipport remote_ipport(const socket &) noexcept; - hostport local_hostport(const socket &) noexcept; - hostport remote_hostport(const socket &) noexcept; size_t write(socket &, const ilist &); // write_all size_t write(socket &, const iov &); // write_all @@ -87,17 +85,13 @@ namespace ircd::net const_raw_buffer peer_cert_der(const mutable_raw_buffer &, const socket &); } -namespace ircd -{ - using net::socket; - using net::hostport; - using net::host; - using net::port; - using net::string; -} - struct ircd::net::init { init(); ~init() noexcept; }; + +namespace ircd +{ + using net::socket; +} diff --git a/include/ircd/net/remote.h b/include/ircd/net/remote.h index 35e96c563..d9edd249c 100644 --- a/include/ircd/net/remote.h +++ b/include/ircd/net/remote.h @@ -22,15 +22,20 @@ #pragma once #define HAVE_IRCD_NET_REMOTE_H +// Forward declarations for boost because it is not included here. +namespace boost::asio::ip +{ + struct address; +}; + namespace ircd::net { - struct remote; - struct ipport; struct hostport; + struct resolve; + struct ipport; + struct remote; - const uint16_t &port(const hostport &); const uint16_t &port(const ipport &); - uint16_t &port(hostport &); uint16_t &port(ipport &); bool is_v6(const ipport &); @@ -43,17 +48,20 @@ namespace ircd::net uint32_t &host4(ipport &); auto &host(hostport &); - string_view string(const uint32_t &, const mutable_buffer &buf); - string_view string(const uint128_t &, const mutable_buffer &buf); - string_view string(const hostport &, const mutable_buffer &buf); - string_view string(const ipport &, const mutable_buffer &buf); - string_view string(const remote &, const mutable_buffer &buf); + string_view string(const mutable_buffer &out, const uint32_t &); + string_view string(const mutable_buffer &out, const uint128_t &); + string_view string(const mutable_buffer &out, const hostport &); + string_view string(const mutable_buffer &out, const ipport &); + string_view string(const mutable_buffer &out, const remote &); +} - std::string string(const uint32_t &); - std::string string(const uint128_t &); - std::string string(const hostport &); - std::string string(const ipport &); - std::string string(const remote &); +namespace ircd +{ + using net::hostport; + using net::ipport; + using net::remote; + using net::host; + using net::port; } /// This structure holds a hostname and port usually fresh from user input @@ -64,15 +72,45 @@ namespace ircd::net /// then 8448 is assumed. /// struct ircd::net::hostport -:std::pair { - static const hostport null; + string_view host {"0.0.0.0"}; + string_view port {"8448"}; + uint16_t portnum {0}; - hostport(std::string s, const uint16_t &port = 8448); + hostport(const string_view &host, const string_view &port) + :host{host} + ,port{port} + {} + + hostport(const string_view &host, const uint16_t &portnum) + :host{host} + ,port{} + ,portnum{portnum} + {} + + hostport(const string_view &amalgam) + :host{rsplit(amalgam, ':').first} + ,port{rsplit(amalgam, ':').second} + {} + + hostport() = default; friend std::ostream &operator<<(std::ostream &, const hostport &); }; +/// DNS resolution section +/// +struct ircd::net::resolve +{ + using callback_one = std::function; + using callback_many = std::function)>; + + resolve(const hostport &, callback_one); + resolve(const hostport &, callback_many); + resolve(const hostport &, ctx::future &); + resolve(const hostport &, ctx::future> &); +}; + /// This lightweight structure holds an IP address and port in native byte /// form. It is usually returned from the result of a resolution but may also /// serve as input to a reverse resolution. Either way, this allocation-free @@ -94,16 +132,13 @@ struct ircd::net::ipport operator bool() const; bool operator!() const { return !static_cast(*this); } + // DNS lookup! May yield ircd::ctx! + ipport(const hostport &); + // Trivial constructors ipport(const uint32_t &ip, const uint16_t &port); ipport(const uint128_t &ip, const uint16_t &port); - - // DNS lookup! May yield ircd::ctx! - ipport(const std::string &hostname, const std::string &port); - ipport(const std::string &hostname, const uint16_t &port); - ipport(const string_view &hostname, const string_view &port = "8448"); - ipport(const string_view &hostname, const uint16_t &port); - ipport(const hostport &); + ipport(const boost::asio::ip::address &, const uint16_t &port); ipport(); friend std::ostream &operator<<(std::ostream &, const ipport &); @@ -117,22 +152,21 @@ struct ircd::net::ipport struct ircd::net::remote :ircd::net::ipport { - static const remote null; - std::string hostname; operator bool() const; bool operator!() const { return !static_cast(*this); } bool resolved() const; - remote(std::string hostname, const std::string &port); - remote(std::string hostname, const uint16_t &port); - remote(std::string hostname); - explicit remote(const string_view &hostname, const string_view &port); - explicit remote(const string_view &hostname, const uint16_t &port); - remote(const string_view &hostname); - explicit remote(const ipport &); - remote(hostport); + explicit remote(const ipport &ipp) + :ipport{ipp} + {} + + remote(const hostport &hp) + :ipport{hp} + ,hostname{std::string{hp.host}} + {} + remote() = default; friend std::ostream &operator<<(std::ostream &, const remote &); @@ -157,8 +191,8 @@ ircd::net::ipport::ipport() { std::get(*this) = 0; std::get(*this) = 0; - //auto &ip(std::get(*this)); - //std::fill(begin(ip), end(ip), 0x00); + auto &ip(std::get(*this)); + std::fill(begin(ip), end(ip), 0x0); } inline @@ -229,13 +263,13 @@ ircd::net::is_v4(const ipport &ipp) inline auto & ircd::net::host(hostport &hp) { - return hp.first; + return hp.host; } inline const auto & ircd::net::host(const hostport &hp) { - return hp.first; + return hp.host; } inline uint16_t & @@ -249,15 +283,3 @@ ircd::net::port(const ipport &ipp) { return std::get(ipp); } - -inline uint16_t & -ircd::net::port(hostport &hp) -{ - return hp.second; -} - -inline const uint16_t & -ircd::net::port(const hostport &hp) -{ - return hp.second; -} diff --git a/ircd/client.cc b/ircd/client.cc index 840fb6763..268f05370 100644 --- a/ircd/client.cc +++ b/ircd/client.cc @@ -105,22 +105,22 @@ noexcept } } -ircd::hostport +ircd::ipport ircd::local(const client &client) { if(!client.sock) - return hostport::null; + return {}; - return net::local_hostport(*client.sock); + return net::local_ipport(*client.sock); } -ircd::hostport +ircd::ipport ircd::remote(const client &client) { if(!client.sock) - return hostport::null; + return {}; - return net::remote_hostport(*client.sock); + return net::remote_ipport(*client.sock); } ircd::http::response::write_closure diff --git a/ircd/net.cc b/ircd/net.cc index 943a13823..0065bcf6c 100644 --- a/ircd/net.cc +++ b/ircd/net.cc @@ -29,6 +29,8 @@ namespace ircd::net { ip::tcp::resolver *resolver; + + ipport make_ipport(const boost::asio::ip::tcp::endpoint &); } struct ircd::log::log @@ -682,46 +684,12 @@ ircd::net::sslv23_client boost::asio::ssl::context::method::sslv23_client }; -ircd::net::hostport -ircd::net::local_hostport(const socket &socket) -noexcept try -{ - const auto &ep(socket.local()); - return { host(ep), port(ep) }; -} -catch(...) -{ - return { std::string{}, 0 }; -} - -ircd::net::hostport -ircd::net::remote_hostport(const socket &socket) -noexcept try -{ - const auto &ep(socket.remote()); - return { host(ep), port(ep) }; -} -catch(...) -{ - return { std::string{}, 0 }; -} - ircd::net::ipport ircd::net::local_ipport(const socket &socket) noexcept try { const auto &ep(socket.local()); - const auto &a(addr(ep)); - - ipport ret; - if(a.is_v6()) - { - std::get(ret) = a.to_v6().to_bytes(); - std::reverse(std::get(ret).begin(), std::get(ret).end()); - } - else host4(ret) = a.to_v4().to_ulong(); - - return ret; + return make_ipport(ep); } catch(...) { @@ -733,18 +701,7 @@ ircd::net::remote_ipport(const socket &socket) noexcept try { const auto &ep(socket.remote()); - const auto &a(addr(ep)); - - ipport ret; - if(a.is_v6()) - { - std::get(ret) = a.to_v6().to_bytes(); - std::reverse(std::get(ret).begin(), std::get(ret).end()); - } - else host4(ret) = a.to_v4().to_ulong(); - - port(ret) = port(ep); - return ret; + return make_ipport(ep); } catch(...) { @@ -1031,7 +988,7 @@ try if(sd.is_open()) log.debug("socket(%p): disconnect: %s type:%d user: in:%zu out:%zu", (const void *)this, - string(remote_ipport(*this)), + ircd::string(remote_ipport(*this)), uint(type), in.bytes, out.bytes); @@ -1520,89 +1477,21 @@ ircd::net::string(const mutable_buffer &buf, // host / port utils // -std::ostream & -ircd::net::operator<<(std::ostream &s, const hostport &t) -{ - char buf[256]; - s << string(t, buf); - return s; -} - -std::ostream & -ircd::net::operator<<(std::ostream &s, const ipport &t) -{ - char buf[256]; - s << string(t, buf); - return s; -} - -std::ostream & -ircd::net::operator<<(std::ostream &s, const remote &t) -{ - char buf[256]; - s << string(t, buf); - return s; -} - -namespace ircd::net -{ - template std::string _string(const T &t); -} - -template -std::string -ircd::net::_string(const T &t) -{ - std::string ret(256, char{}); - ret.resize(net::string(t, mutable_buffer{ret}).size()); - return ret; -} - -std::string -ircd::net::string(const uint32_t &t) -{ - return _string(t); -} - -std::string -ircd::net::string(const uint128_t &t) -{ - return _string(t); -} - -std::string -ircd::net::string(const hostport &t) -{ - return _string(t); -} - -std::string -ircd::net::string(const ipport &t) -{ - return _string(t); -} - -std::string -ircd::net::string(const remote &t) -{ - return _string(t); -} - ircd::string_view -ircd::net::string(const uint32_t &ip, - const mutable_buffer &buf) +ircd::net::string(const mutable_buffer &buf, + const uint32_t &ip) { const auto len { - fmt::sprintf(buf, "%s", ip::address_v4{ip}.to_string()) + ip::address_v4{ip}.to_string().copy(data(buf), size(buf)) }; return { data(buf), size_t(len) }; } ircd::string_view -ircd::net::string(const uint128_t &ip, - const mutable_buffer &buf) +ircd::net::string(const mutable_buffer &buf, + const uint128_t &ip) { const auto &pun { @@ -1616,27 +1505,32 @@ ircd::net::string(const uint128_t &ip, const auto len { - fmt::sprintf(buf, "%s", ip::address_v6{punpun}.to_string()) + ip::address_v6{punpun}.to_string().copy(data(buf), size(buf)) }; return { data(buf), size_t(len) }; } ircd::string_view -ircd::net::string(const hostport &pair, - const mutable_buffer &buf) +ircd::net::string(const mutable_buffer &buf, + const hostport &hp) { const auto len { - fmt::sprintf(buf, "%s:%u", pair.first, pair.second) + fmt::sprintf + { + buf, "%s:%s", + hp.host, + hp.portnum? lex_cast(hp.portnum) : hp.port + } }; return { data(buf), size_t(len) }; } ircd::string_view -ircd::net::string(const ipport &ipp, - const mutable_buffer &buf) +ircd::net::string(const mutable_buffer &buf, + const ipport &ipp) { const auto len { @@ -1657,8 +1551,8 @@ ircd::net::string(const ipport &ipp, } ircd::string_view -ircd::net::string(const remote &remote, - const mutable_buffer &buf) +ircd::net::string(const mutable_buffer &buf, + const remote &remote) { const auto &ipp { @@ -1675,147 +1569,52 @@ ircd::net::string(const remote &remote, const auto len{strlcpy(data(buf), remote.hostname, size(buf))}; return { data(buf), size_t(len) }; } - else return string(ipp, buf); + else return string(buf, ipp); } // // remote // -const ircd::net::remote -ircd::net::remote::null -{}; - -ircd::net::remote::remote(hostport hp) -:remote{std::move(hp.first), hp.second} -{ -} - -ircd::net::remote::remote(const string_view &host) -:remote -{ - std::string(host), "8448"s -} -{ -} - -ircd::net::remote::remote(const string_view &host, - const uint16_t &port) -:remote -{ - std::string(host), std::string(lex_cast(port)) -} -{ -} - -ircd::net::remote::remote(const string_view &host, - const string_view &port) -:remote -{ - std::string(host), std::string(port) -} -{ -} - -ircd::net::remote::remote(std::string host) -:remote -{ - std::move(host), "8448"s -} -{ -} - -ircd::net::remote::remote(std::string host, - const uint16_t &port) -:ipport{host, port} -,hostname{std::move(host)} -{ -} - -ircd::net::remote::remote(std::string host, - const std::string &port) -:ipport{host, port} -,hostname{std::move(host)} -{ -} - -ircd::net::remote::remote(const ipport &ipp) -:ipport{ipp} +std::ostream & +ircd::net::operator<<(std::ostream &s, const remote &t) { + char buf[256]; + s << string(buf, t); + return s; } // // ipport // +std::ostream & +ircd::net::operator<<(std::ostream &s, const ipport &t) +{ + char buf[256]; + s << string(buf, t); + return s; +} + +ircd::net::ipport +ircd::net::make_ipport(const boost::asio::ip::tcp::endpoint &ep) +{ + return ipport + { + ep.address(), ep.port() + }; +} + ircd::net::ipport::ipport(const hostport &hp) -:ipport -{ - hp.first, std::string(lex_cast(port(hp))) -} { + ctx::future future; + resolve{hp, future}; + *this = future.get(); } -ircd::net::ipport::ipport(const string_view &host, +ircd::net::ipport::ipport(const boost::asio::ip::address &address, const uint16_t &port) -:ipport { - std::string(host), std::string(lex_cast(port)) -} -{ -} - -ircd::net::ipport::ipport(const string_view &host, - const string_view &port) -:ipport -{ - std::string(host), std::string(port) -} -{ -} - -ircd::net::ipport::ipport(const std::string &host, - const uint16_t &port) -:ipport -{ - host, std::string{lex_cast(port)} -} -{ -} - -ircd::net::ipport::ipport(const std::string &host, - const std::string &port) -:ipport -{ - uint32_t{0}, - lex_cast(port) -} -{ - assert(resolver); - const ip::tcp::resolver::query query - { - host, port - }; - - auto epit - { - resolver->async_resolve(query, yield_context{to_asio{}}) - }; - - static const ip::tcp::resolver::iterator end; - if(epit == end) - throw nxdomain("host '%s' not found", host); - - const ip::tcp::endpoint &ep - { - *epit - }; - - const asio::ip::address &address - { - ep.address() - }; - std::get(*this) = address.is_v6(); if(is_v6(*this)) @@ -1825,49 +1624,149 @@ ircd::net::ipport::ipport(const std::string &host, } else host4(*this) = address.to_v4().to_ulong(); - log.debug("resolved remote %s:%u => %s %s", - host, - net::port(*this), - is_v6(*this)? "IP6" : "IP4", - string(*this)); + net::port(*this) = port; +} + +// +// resolve +// + +namespace ircd::net +{ + // Internal resolve base (requires boost syms) + using resolve_callback = std::function; + void async_resolve(const hostport &, ip::tcp::resolver::flags, resolve_callback); +} + +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 ipport &ip) + mutable + { + if(eptr) + p.set_exception(std::move(eptr)); + else + p.set_value(ip); + }); +} + +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 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) +{ + static const ip::tcp::resolver::flags flags{}; + async_resolve(hostport, flags, [callback(std::move(callback))] + (std::exception_ptr eptr, ip::tcp::resolver::results_type results) + { + if(eptr) + return callback(std::move(eptr), {}); + + static const size_t max{64}; + const size_t result_count{results.size()}; + const size_t count{std::min(max, result_count)}; + + ipport vector[count]; + std::transform(begin(results), end(results), vector, [] + (const auto &entry) + { + return make_ipport(entry.endpoint()); + }); + + assert(!eptr); + callback(std::move(eptr), vector_view(vector, count)); + }); +} + +void +ircd::net::async_resolve(const hostport &hostport, + ip::tcp::resolver::flags flags, + resolve_callback callback) +{ + // Trivial host string + const string_view &host + { + hostport.host + }; + + // Determine if the port is a string or requires a lex_cast to one. + char portbuf[8]; + const string_view &port + { + hostport.portnum? lex_cast(hostport.portnum, portbuf) : hostport.port + }; + + // Determine if the port is numeric and hint to avoid name lookup if so. + if(hostport.portnum || ctype(hostport.port) == -1) + flags |= ip::tcp::resolver::numeric_service; + + // 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))] + (const error_code &ec, ip::tcp::resolver::results_type results) + noexcept + { + if(ec) + { + callback(std::make_exception_ptr(boost::system::system_error{ec}), {}); + } + else try + { + callback({}, std::move(results)); + } + catch(...) + { + callback(std::make_exception_ptr(std::current_exception()), {}); + } + }); } // // hostport // -const ircd::net::hostport -ircd::net::hostport::null +std::ostream & +ircd::net::operator<<(std::ostream &s, const hostport &t) { - "0.0.0.0"s, 0 -}; - -ircd::net::hostport::hostport(std::string s, - const uint16_t &port) -try -:std::pair -{ - std::move(s), port -} -{ - if(port != 8448) - return; - - //TODO: ipv6 - const auto port_suffix - { - rsplit(first, ':').second - }; - - if(!port_suffix.empty() && port_suffix != "8448") - second = lex_cast(port_suffix); -} -catch(const std::exception &e) -{ - throw net::invalid_argument - { - "Supplied host name and/or port number: ", e.what() - }; + char buf[256]; + s << string(buf, t); + return s; } ///////////////////////////////////////////////////////////////////////////////