From a477a53b8070e964fd5979c4745ad5b5dc16f6be Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sun, 29 Jul 2018 09:05:50 +0200 Subject: [PATCH] simplify natpmp gateway and local address discovery --- include/libtorrent/enum_net.hpp | 5 ++ src/enum_net.cpp | 18 +++++-- src/natpmp.cpp | 83 ++++++++++++++++++--------------- test/enum_if.cpp | 2 +- 4 files changed, 65 insertions(+), 43 deletions(-) diff --git a/include/libtorrent/enum_net.hpp b/include/libtorrent/enum_net.hpp index 9dbad1813..b8f23f8b1 100644 --- a/include/libtorrent/enum_net.hpp +++ b/include/libtorrent/enum_net.hpp @@ -41,6 +41,8 @@ POSSIBILITY OF SUCH DAMAGE. #include // for SO_BINDTODEVICE #endif +#include + #include "libtorrent/aux_/disable_warnings_pop.hpp" #include "libtorrent/io_service_fwd.hpp" @@ -94,6 +96,9 @@ namespace libtorrent { TORRENT_EXTRA_EXPORT bool in_local_network(std::vector const& net , address const& addr); + TORRENT_EXTRA_EXPORT boost::optional get_default_route(io_service& ios + , string_view device, bool v6, error_code& ec); + // returns the first default gateway found if device is empty TORRENT_EXTRA_EXPORT address get_default_gateway(io_service& ios , string_view device, bool v6, error_code& ec); diff --git a/src/enum_net.cpp b/src/enum_net.cpp index 934332421..f49f45d1e 100644 --- a/src/enum_net.cpp +++ b/src/enum_net.cpp @@ -45,6 +45,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/aux_/disable_warnings_push.hpp" #include +#include #if TORRENT_USE_IFCONF #include @@ -844,10 +845,10 @@ int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldl return ret; } - address get_default_gateway(io_service& ios - , string_view device, bool v6, error_code& ec) + boost::optional get_default_route(io_service& ios + , string_view const device, bool const v6, error_code& ec) { - std::vector ret = enum_routes(ios, ec); + std::vector const ret = enum_routes(ios, ec); auto const i = std::find_if(ret.begin(), ret.end() , [device,v6](ip_route const& r) { @@ -855,8 +856,15 @@ int _System __libsocket_sysctl(int* mib, u_int namelen, void *oldp, size_t *oldl && r.destination.is_v6() == v6 && (device.empty() || r.name == device); }); - if (i == ret.end()) return address(); - return i->gateway; + if (i == ret.end()) return boost::none; + return *i; + } + + address get_default_gateway(io_service& ios + , string_view const device, bool const v6, error_code& ec) + { + auto const default_route = get_default_route(ios, device, v6, ec); + return default_route ? default_route->gateway : address(); } std::vector enum_routes(io_service& ios, error_code& ec) diff --git a/src/natpmp.cpp b/src/natpmp.cpp index afeb17402..982557af5 100644 --- a/src/natpmp.cpp +++ b/src/natpmp.cpp @@ -156,24 +156,58 @@ void natpmp::start(address local_address, std::string device) // if necessary m_version = version_pcp; + // we really want a device name to get the right default gateway + // try to find one even if the listen socket isn't bound to a device + if (device.empty()) + { + device = device_for_address(local_address, m_socket.get_io_service(), ec); + // if this fails fall back to using the first default gateway in the + // routing table + ec.clear(); + } + + auto const route = get_default_route(m_socket.get_io_service() + , device, local_address.is_v6(), ec); + + if (!route) + { +#ifndef TORRENT_DISABLE_LOGGING + if (should_log()) + { + log("failed to find default route for \"%s\" %s: %s" + , device.c_str(), local_address.to_string().c_str() + , convert_from_native(ec.message()).c_str()); + } +#endif + disable(ec); + return; + } + + if (device.empty()) device = route->name; + + TORRENT_ASSERT(!device.empty()); + // PCP requires reporting the source address at the application // layer so the socket MUST be bound to a specific address // if the caller didn't specify one then get the first suitable // address from the OS if (local_address.is_unspecified()) { - for (auto const& a : enum_net_interfaces(m_socket.get_io_service(), ec)) - { - if (a.interface_address.is_loopback()) continue; - if (a.interface_address.is_v4() != local_address.is_v4()) continue; - if (a.interface_address.is_v6() && is_local(a.interface_address)) continue; - if (!device.empty() && a.name != device) continue; - local_address = a.interface_address; - device = a.name; - break; - } + std::vector const net = enum_net_interfaces( + m_socket.get_io_service(), ec); - if (local_address.is_unspecified()) + auto const it = std::find_if(net.begin(), net.end(), [&](ip_interface const& i) + { + return i.interface_address.is_v4() == local_address.is_v4() + && (i.interface_address.is_v4() || !is_local(i.interface_address)) + && i.name == device; + }); + + if (it != net.end()) + { + local_address = it->interface_address; + } + else { // if we can't get a specific address to bind to we'll have // to fall back to NAT-PMP @@ -198,34 +232,9 @@ void natpmp::start(address local_address, std::string device) } } - // we really want a device name to get the right default gateway - // try to find one even if the listen socket isn't bound to a device - if (device.empty()) - { - device = device_for_address(local_address, m_socket.get_io_service(), ec); - // if this fails fall back to using the first default gateway in the - // routing table - ec.clear(); - } - - address const gateway = get_default_gateway(m_socket.get_io_service() - , device, local_address.is_v6(), ec); - if (ec || gateway.is_unspecified()) - { -#ifndef TORRENT_DISABLE_LOGGING - if (should_log()) - { - log("failed to find default route for %s: %s" - , local_address.to_string().c_str(), convert_from_native(ec.message()).c_str()); - } -#endif - disable(ec); - return; - } - m_disabled = false; - udp::endpoint nat_endpoint(gateway, 5351); + udp::endpoint const nat_endpoint(route->gateway, 5351); if (nat_endpoint == m_nat_endpoint) return; m_nat_endpoint = nat_endpoint; diff --git a/test/enum_if.cpp b/test/enum_if.cpp index 6a7d8806d..2e35c83c4 100644 --- a/test/enum_if.cpp +++ b/test/enum_if.cpp @@ -84,7 +84,7 @@ int main() for (auto const& i : net) { - address iface_def_gw = get_default_gateway(ios, i.name, i.interface_address.is_v6(), ec); + address const iface_def_gw = get_default_gateway(ios, i.name, i.interface_address.is_v6(), ec); std::printf("%-34s%-45s%-20s%s%s%-20s%-34s%s %s\n" , i.interface_address.to_string(ec).c_str() , i.netmask.to_string(ec).c_str()