From f36e6a6c4f4ca4a7d389e80399f677214463eb68 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Fri, 11 Jan 2008 09:38:05 +0000 Subject: [PATCH] proper default gateway discovery --- examples/enum_if.cpp | 2 - include/libtorrent/enum_net.hpp | 6 +- src/enum_net.cpp | 146 +++++++++++++++++++++++++++++--- src/natpmp.cpp | 49 ++--------- 4 files changed, 146 insertions(+), 57 deletions(-) diff --git a/examples/enum_if.cpp b/examples/enum_if.cpp index 9eb2c4868..28a1f4fa5 100644 --- a/examples/enum_if.cpp +++ b/examples/enum_if.cpp @@ -21,8 +21,6 @@ int main() if (is_local(i->interface_address)) std::cout << "local "; if (is_loopback(i->interface_address)) std::cout << "loopback "; std::cout << std::endl; - std::cout << " router: " << router_for_interface(i->interface_address, ec); - std::cout << std::endl; } address local = guess_local_address(ios); diff --git a/include/libtorrent/enum_net.hpp b/include/libtorrent/enum_net.hpp index 4570e30eb..2f61d39bd 100644 --- a/include/libtorrent/enum_net.hpp +++ b/include/libtorrent/enum_net.hpp @@ -55,9 +55,11 @@ namespace libtorrent // returns true if the specified address is on the same // local network as us - bool in_local_network(asio::io_service& ios, address const& addr, asio::error_code& ec); + bool in_local_network(asio::io_service& ios, address const& addr + , asio::error_code& ec); - address router_for_interface(address const interface, asio::error_code& ec); + address get_default_gateway(asio::io_service& ios, address const& addr + , asio::error_code& ec); } #endif diff --git a/src/enum_net.cpp b/src/enum_net.cpp index f606e1ceb..daa15b302 100644 --- a/src/enum_net.cpp +++ b/src/enum_net.cpp @@ -30,10 +30,12 @@ POSSIBILITY OF SUCH DAMAGE. */ -#if defined __linux__ || defined BSD +#if defined __linux__ || (defined __APPLE__ && __MACH__) || defined __FreeBSD__ || defined __NetBSD__ \ + || defined __OpenBSD__ || defined __bsdi__ || defined __DragonFly__ #include #include #include +#include #elif defined WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN @@ -67,6 +69,14 @@ namespace libtorrent } return address(); } + + bool verify_sockaddr(sockaddr_in* sin) + { + return (sin->sin_len == sizeof(sockaddr_in) + && sin->sin_family == AF_INET) + || (sin->sin_len == sizeof(sockaddr_in6) + && sin->sin_family == AF_INET6); + } } bool in_subnet(address const& addr, ip_interface const& iface) @@ -213,15 +223,117 @@ namespace libtorrent return ret; } - address router_for_interface(address const interface, asio::error_code& ec) + address get_default_gateway(asio::io_service& ios, address const& interface, asio::error_code& ec) { -#ifdef WIN32 + +#if defined __linux__ || (defined __APPLE__ && __MACH__) || defined __FreeBSD__ || defined __NetBSD__ \ + || defined __OpenBSD__ || defined __bsdi__ || defined __DragonFly__ + + struct rt_msg + { + rt_msghdr m_rtm; + char buf[512]; + }; + + rt_msg m; + int len = sizeof(rt_msg); + bzero(&m, len); + m.m_rtm.rtm_type = RTM_GET; + m.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY; + m.m_rtm.rtm_version = RTM_VERSION; + m.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; + m.m_rtm.rtm_seq = 0; + m.m_rtm.rtm_msglen = len; + + int s = socket(PF_ROUTE, SOCK_RAW, AF_INET); + if (s == -1) + { + ec = asio::error_code(errno, asio::error::system_category); + return address_v4::any(); + } + + int n = write(s, &m, len); + if (n == -1) + { + ec = asio::error_code(errno, asio::error::system_category); + close(s); + return address_v4::any(); + } + else if (n != len) + { + ec = asio::error::operation_not_supported; + close(s); + return address_v4::any(); + } + bzero(&m, len); + + n = read(s, &m, len); + if (n == -1) + { + ec = asio::error_code(errno, asio::error::system_category); + close(s); + return address_v4::any(); + } + close(s); + + TORRENT_ASSERT(m.m_rtm.rtm_seq == 0); + TORRENT_ASSERT(m.m_rtm.rtm_pid == getpid()); + if (m.m_rtm.rtm_errno) + { + ec = asio::error_code(m.m_rtm.rtm_errno, asio::error::system_category); + return address_v4::any(); + } + if (m.m_rtm.rtm_flags & RTF_UP == 0 + || m.m_rtm.rtm_flags & RTF_GATEWAY == 0) + { + ec = asio::error::operation_not_supported; + return address_v4::any(); + } + if (m.m_rtm.rtm_addrs & RTA_DST == 0 + || m.m_rtm.rtm_addrs & RTA_GATEWAY == 0) + { + ec = asio::error::operation_not_supported; + return address_v4::any(); + } + if (m.m_rtm.rtm_msglen > len) + { + ec = asio::error::operation_not_supported; + return address_v4::any(); + } + int min_len = sizeof(rt_msghdr) + 2 * sizeof(struct sockaddr_in); + if (m.m_rtm.rtm_msglen < min_len) + { + ec = asio::error::operation_not_supported; + return address_v4::any(); + } + + // default route + char* p = m.buf; + sockaddr_in* sin = (sockaddr_in*)p; + if (!verify_sockaddr(sin)) + { + ec = asio::error::operation_not_supported; + return address_v4::any(); + } + + // default gateway + p += sin->sin_len; + sin = (sockaddr_in*)p; + if (!verify_sockaddr(sin)) + { + ec = asio::error::operation_not_supported; + return address_v4::any(); + } + + return sockaddr_to_address((sockaddr*)sin); + +#elif defined WIN32 // Load Iphlpapi library HMODULE iphlp = LoadLibraryA("Iphlpapi.dll"); if (!iphlp) { - ec = asio::error::fault; + ec = asio::error::operation_not_supported; return address_v4::any(); } @@ -231,7 +343,7 @@ namespace libtorrent if (!GetAdaptersInfo) { FreeLibrary(iphlp); - ec = asio::error::fault; + ec = asio::error::operation_not_supported; return address_v4::any(); } @@ -240,7 +352,7 @@ namespace libtorrent if (GetAdaptersInfo(adapter_info, &out_buf_size) != ERROR_BUFFER_OVERFLOW) { FreeLibrary(iphlp); - ec = asio::error::fault; + ec = asio::error::operation_not_supported; return address_v4::any(); } @@ -248,22 +360,29 @@ namespace libtorrent if (!adapter_info) { FreeLibrary(iphlp); - ec = asio::error::fault; + ec = asio::error::no_memory; return address_v4::any(); } address ret; if (GetAdaptersInfo(adapter_info, &out_buf_size) == NO_ERROR) { - PIP_ADAPTER_INFO adapter = adapter_info; - while (adapter != 0) + + for (PIP_ADAPTER_INFO adapter = adapter_info; + adapter != 0; adapter = adapter->Next) { - if (interface == address::from_string(adapter->IpAddressList.IpAddress.String, ec)) + address iface = address::from_string(adapter->IpAddressList.IpAddress.String, ec); + if (ec) + { + ec = asio::error_code(); + continue; + } + if (is_loopback(iface) || is_any(iface)) continue; + if (interface == address() || interface == iface) { ret = address::from_string(adapter->GatewayList.IpAddress.String, ec); break; } - adapter = adapter->Next; } } @@ -274,10 +393,11 @@ namespace libtorrent return ret; #else - // TODO: temporary implementation + address interface = guess_local_address(ios); + if (!interface.is_v4()) { - ec = asio::error::fault; + ec = asio::error::operation_not_supported; return address_v4::any(); } return address_v4((interface.to_v4().to_ulong() & 0xffffff00) | 1); diff --git a/src/natpmp.cpp b/src/natpmp.cpp index b1d815fc1..f9533145e 100644 --- a/src/natpmp.cpp +++ b/src/natpmp.cpp @@ -45,13 +45,6 @@ using namespace libtorrent; enum { num_mappings = 2 }; -namespace libtorrent -{ - // defined in upnp.cpp - bool is_local(address const& a); - address guess_local_address(asio::io_service&); -} - natpmp::natpmp(io_service& ios, address const& listen_interface, portmap_callback_t const& cb) : m_callback(cb) , m_currently_mapping(-1) @@ -72,51 +65,27 @@ natpmp::natpmp(io_service& ios, address const& listen_interface, portmap_callbac void natpmp::rebind(address const& listen_interface) { - address local = address_v4::any(); - if (listen_interface != address_v4::any()) + asio::error_code ec; + address gateway = get_default_gateway(m_socket.get_io_service(), listen_interface, ec); + if (ec) { - local = listen_interface; - } - else - { - local = guess_local_address(m_socket.io_service()); - - if (local == address_v4::any()) - { - disable("local host is probably not on a NATed " - "network. disabling NAT-PMP"); - return; - } - } - #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) - m_log << time_now_string() - << " local ip: " << local.to_string() << std::endl; + m_log << time_now_string() << " failed to find default router: " + << ec.message() << std::endl; #endif - - if (!is_local(local)) - { - // the local address seems to be an external - // internet address. Assume it is not behind a NAT - disable("local IP is not on a local network"); + disable("failed to find default router"); return; } m_disabled = false; - asio::error_code ec; - udp::endpoint nat_endpoint(router_for_interface(local, ec), 5351); - if (ec) - { - disable("cannot retrieve router address"); - return; - } - + udp::endpoint nat_endpoint(gateway, 5351); if (nat_endpoint == m_nat_endpoint) return; m_nat_endpoint = nat_endpoint; #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) - m_log << "assuming router is at: " << m_nat_endpoint.address().to_string() << std::endl; + m_log << time_now_string() << " found router at: " + << m_nat_endpoint.address() << std::endl; #endif m_socket.open(udp::v4(), ec);