merged uTP branch into trunk (yay)

This commit is contained in:
Arvid Norberg
2010-11-29 01:33:05 +00:00
parent 5a1669cf03
commit e5f980d80d
77 changed files with 7497 additions and 630 deletions

View File

@@ -33,6 +33,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/config.hpp"
#include <boost/bind.hpp>
#include <vector>
#include <stdlib.h> // for wcstombscstombs
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/error_code.hpp"
@@ -84,31 +85,44 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent { namespace
{
address inaddr_to_address(in_addr const* ina)
address inaddr_to_address(in_addr const* ina, int len = 4)
{
typedef asio::ip::address_v4::bytes_type bytes_t;
bytes_t b;
std::memcpy(&b[0], ina, b.size());
std::memset(&b[0], 0, b.size());
if (len > 0) std::memcpy(&b[0], ina, (std::min)(len, int(b.size())));
return address_v4(b);
}
#if TORRENT_USE_IPV6
address inaddr6_to_address(in6_addr const* ina6)
address inaddr6_to_address(in6_addr const* ina6, int len = 16)
{
typedef asio::ip::address_v6::bytes_type bytes_t;
bytes_t b;
std::memcpy(&b[0], ina6, b.size());
std::memset(&b[0], 0, b.size());
if (len > 0) std::memcpy(&b[0], ina6, (std::min)(len, int(b.size())));
return address_v6(b);
}
#endif
address sockaddr_to_address(sockaddr const* sin)
int sockaddr_len(sockaddr const* sin)
{
if (sin->sa_family == AF_INET)
return inaddr_to_address(&((sockaddr_in const*)sin)->sin_addr);
#if defined TORRENT_WINDOWS || TORRENT_MINGW || defined TORRENT_LINUX
return sin->sa_family == AF_INET ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
#else
return sin->sa_len;
#endif
}
address sockaddr_to_address(sockaddr const* sin, int assume_family = -1)
{
if (sin->sa_family == AF_INET || assume_family == AF_INET)
return inaddr_to_address(&((sockaddr_in const*)sin)->sin_addr
, sockaddr_len(sin) - offsetof(sockaddr, sa_data));
#if TORRENT_USE_IPV6
else if (sin->sa_family == AF_INET6)
return inaddr6_to_address(&((sockaddr_in6 const*)sin)->sin6_addr);
else if (sin->sa_family == AF_INET6 || assume_family == AF_INET6)
return inaddr6_to_address(&((sockaddr_in6 const*)sin)->sin6_addr
, sockaddr_len(sin) - offsetof(sockaddr, sa_data));
#endif
return address();
}
@@ -142,13 +156,15 @@ namespace libtorrent { namespace
return msg_len;
}
bool parse_route(nlmsghdr* nl_hdr, ip_route* rt_info)
bool parse_route(int s, nlmsghdr* nl_hdr, ip_route* rt_info)
{
rtmsg* rt_msg = (rtmsg*)NLMSG_DATA(nl_hdr);
if((rt_msg->rtm_family != AF_INET) || (rt_msg->rtm_table != RT_TABLE_MAIN))
if((rt_msg->rtm_family != AF_INET && rt_msg->rtm_family != AF_INET6) || (rt_msg->rtm_table != RT_TABLE_MAIN
&& rt_msg->rtm_table != RT_TABLE_LOCAL))
return false;
int if_index = 0;
int rt_len = RTM_PAYLOAD(nl_hdr);
for (rtattr* rt_attr = (rtattr*)RTM_RTA(rt_msg);
RTA_OK(rt_attr,rt_len); rt_attr = RTA_NEXT(rt_attr,rt_len))
@@ -156,23 +172,52 @@ namespace libtorrent { namespace
switch(rt_attr->rta_type)
{
case RTA_OIF:
if_indextoname(*(int*)RTA_DATA(rt_attr), rt_info->name);
if_index = *(int*)RTA_DATA(rt_attr);
break;
case RTA_GATEWAY:
rt_info->gateway = address_v4(ntohl(*(u_int*)RTA_DATA(rt_attr)));
#if TORRENT_USE_IPV6
if (rt_msg->rtm_family == AF_INET6)
{
rt_info->gateway = inaddr6_to_address((in6_addr*)RTA_DATA(rt_attr));
}
else
#endif
{
rt_info->gateway = inaddr_to_address((in_addr*)RTA_DATA(rt_attr));
}
break;
case RTA_DST:
rt_info->destination = address_v4(ntohl(*(u_int*)RTA_DATA(rt_attr)));
#if TORRENT_USE_IPV6
if (rt_msg->rtm_family == AF_INET6)
{
rt_info->destination = inaddr6_to_address((in6_addr*)RTA_DATA(rt_attr));
}
else
#endif
{
rt_info->destination = inaddr_to_address((in_addr*)RTA_DATA(rt_attr));
}
break;
}
}
if_indextoname(if_index, rt_info->name);
ifreq req;
memset(&req, 0, sizeof(req));
if_indextoname(if_index, req.ifr_name);
ioctl(s, SIOCGIFMTU, &req);
rt_info->mtu = req.ifr_mtu;
// obviously this doesn't work correctly. How do you get the netmask for a route?
// if (ioctl(s, SIOCGIFNETMASK, &req) == 0) {
// rt_info->netmask = sockaddr_to_address(&req.ifr_addr, req.ifr_addr.sa_family);
// }
return true;
}
#endif
#if defined TORRENT_BSD
bool parse_route(rt_msghdr* rtm, ip_route* rt_info)
bool parse_route(int s, rt_msghdr* rtm, ip_route* rt_info)
{
sockaddr* rti_info[RTAX_MAX];
sockaddr* sa = (sockaddr*)(rtm + 1);
@@ -205,9 +250,17 @@ namespace libtorrent { namespace
return false;
rt_info->gateway = sockaddr_to_address(rti_info[RTAX_GATEWAY]);
rt_info->netmask = sockaddr_to_address(rti_info[RTAX_NETMASK]);
rt_info->destination = sockaddr_to_address(rti_info[RTAX_DST]);
rt_info->netmask = sockaddr_to_address(rti_info[RTAX_NETMASK]
, rt_info->destination.is_v4() ? AF_INET : AF_INET6);
if_indextoname(rtm->rtm_index, rt_info->name);
ifreq req;
memset(&req, 0, sizeof(req));
if_indextoname(rtm->rtm_index, req.ifr_name);
if (ioctl(s, SIOCGIFMTU, &req) < 0) return false;
rt_info->mtu = req.ifr_mtu;
return true;
}
#endif
@@ -231,17 +284,29 @@ namespace libtorrent { namespace
namespace libtorrent
{
bool in_subnet(address const& addr, ip_interface const& iface)
// return (a1 & mask) == (a2 & mask)
bool match_addr_mask(address const& a1, address const& a2, address const& mask)
{
if (addr.is_v4() != iface.interface_address.is_v4()) return false;
// since netmasks seems unreliable for IPv6 interfaces
// (MacOS X returns AF_INET addresses as bitmasks) assume
// that any IPv6 address belongs to the subnet of any
// interface with an IPv6 address
if (addr.is_v6()) return true;
// all 3 addresses needs to belong to the same family
if (a1.is_v4() != a2.is_v4()) return false;
if (a1.is_v4() != mask.is_v4()) return false;
return (addr.to_v4().to_ulong() & iface.netmask.to_v4().to_ulong())
== (iface.interface_address.to_v4().to_ulong() & iface.netmask.to_v4().to_ulong());
#if TORRENT_USE_IPV6
if (a1.is_v6())
{
address_v6::bytes_type b1;
address_v6::bytes_type b2;
address_v6::bytes_type m;
b1 = a1.to_v6().to_bytes();
b2 = a2.to_v6().to_bytes();
m = mask.to_v6().to_bytes();
for (int i = 0; i < b1.size(); ++i)
b1[i] &= m[i];
return memcmp(&b1[0], &b2[0], b1.size());
}
#endif
return (a1.to_v4().to_ulong() & mask.to_v4().to_ulong())
== (a2.to_v4().to_ulong() & mask.to_v4().to_ulong());
}
bool in_local_network(io_service& ios, address const& addr, error_code& ec)
@@ -251,10 +316,56 @@ namespace libtorrent
for (std::vector<ip_interface>::iterator i = net.begin()
, end(net.end()); i != end; ++i)
{
if (in_subnet(addr, *i)) return true;
if (match_addr_mask(addr, i->interface_address, i->netmask)) return true;
}
return false;
}
#if defined TORRENT_WINDOWS || defined TORRENT_MINGW
address build_netmask(int bits, int family)
{
if (family == AF_INET)
{
typedef asio::ip::address_v4::bytes_type bytes_t;
bytes_t b;
std::memset(&b[0], 0xff, b.size());
for (int i = sizeof(bytes_t)/8-1; i > 0; --i)
{
if (bits < 8)
{
b[i] <<= bits;
break;
}
b[i] = 0;
bits -= 8;
}
return address_v4(b);
}
#if TORRENT_USE_IPV6
else if (family == AF_INET6)
{
typedef asio::ip::address_v6::bytes_type bytes_t;
bytes_t b;
std::memset(&b[0], 0xff, b.size());
for (int i = sizeof(bytes_t)/8-1; i > 0; --i)
{
if (bits < 8)
{
b[i] <<= bits;
break;
}
b[i] = 0;
bits -= 8;
}
return address_v6(b);
}
#endif
else
{
return address();
}
}
#endif
std::vector<ip_interface> enum_net_interfaces(io_service& ios, error_code& ec)
{
@@ -295,8 +406,20 @@ namespace libtorrent
iface.interface_address = sockaddr_to_address(&item.ifr_addr);
strcpy(iface.name, item.ifr_name);
ifreq netmask = item;
if (ioctl(s, SIOCGIFNETMASK, &netmask) < 0)
ifreq req;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE);
if (ioctl(s, SIOCGIFMTU, &req) < 0)
{
ec = error_code(errno, asio::error::system_category);
close(s);
return ret;
}
iface.mtu = req.ifr_mtu;
memset(&req, 0, sizeof(req));
strncpy(req.ifr_name, item.ifr_name, IF_NAMESIZE);
if (ioctl(s, SIOCGIFNETMASK, &req) < 0)
{
#if TORRENT_USE_IPV6
if (iface.interface_address.is_v6())
@@ -314,7 +437,7 @@ namespace libtorrent
}
else
{
iface.netmask = sockaddr_to_address(&netmask.ifr_addr);
iface.netmask = sockaddr_to_address(&req.ifr_addr, item.ifr_addr.sa_family);
}
ret.push_back(iface);
}
@@ -331,6 +454,65 @@ namespace libtorrent
#elif defined TORRENT_WINDOWS || defined TORRENT_MINGW
// Load Iphlpapi library
HMODULE iphlp = LoadLibraryA("Iphlpapi.dll");
if (iphlp)
{
// Get GetAdaptersAddresses() pointer
typedef ULONG (WINAPI *GetAdaptersAddresses_t)(ULONG,ULONG,PVOID,PIP_ADAPTER_ADDRESSES,PULONG);
GetAdaptersAddresses_t GetAdaptersAddresses = (GetAdaptersAddresses_t)GetProcAddress(
iphlp, "GetAdaptersAddresses");
if (GetAdaptersAddresses)
{
PIP_ADAPTER_ADDRESSES adapter_addresses = 0;
ULONG out_buf_size = 0;
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER
| GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &out_buf_size) != ERROR_BUFFER_OVERFLOW)
{
FreeLibrary(iphlp);
ec = asio::error::operation_not_supported;
return std::vector<ip_interface>();
}
adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(out_buf_size);
if (!adapter_addresses)
{
FreeLibrary(iphlp);
ec = asio::error::no_memory;
return std::vector<ip_interface>();
}
if (GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER
| GAA_FLAG_SKIP_ANYCAST, NULL, adapter_addresses, &out_buf_size) == NO_ERROR)
{
for (PIP_ADAPTER_ADDRESSES adapter = adapter_addresses;
adapter != 0; adapter = adapter->Next)
{
ip_interface r;
strncpy(r.name, adapter->AdapterName, sizeof(r.name));
r.name[sizeof(r.name)-1] = 0;
r.mtu = adapter->Mtu;
IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress;
while (unicast)
{
r.interface_address = sockaddr_to_address(unicast->Address.lpSockaddr);
ret.push_back(r);
unicast = unicast->Next;
}
}
}
// Free memory
free(adapter_addresses);
FreeLibrary(iphlp);
return ret;
}
FreeLibrary(iphlp);
}
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == SOCKET_ERROR)
{
@@ -356,9 +538,11 @@ namespace libtorrent
for (int i = 0; i < n; ++i)
{
iface.interface_address = sockaddr_to_address(&buffer[i].iiAddress.Address);
iface.netmask = sockaddr_to_address(&buffer[i].iiNetmask.Address);
iface.name[0] = 0;
if (iface.interface_address == address_v4::any()) continue;
iface.netmask = sockaddr_to_address(&buffer[i].iiNetmask.Address
, iface.interface_address.is_v4() ? AF_INET : AF_INET6);
iface.name[0] = 0;
iface.mtu = 1500; // how to get the MTU?
ret.push_back(iface);
}
@@ -372,6 +556,7 @@ namespace libtorrent
for (;i != udp::resolver_iterator(); ++i)
{
iface.interface_address = i->endpoint().address();
iface.mtu = 1500;
if (iface.interface_address.is_v4())
iface.netmask = address_v4::netmask(iface.interface_address.to_v4());
ret.push_back(iface);
@@ -528,6 +713,12 @@ namespace libtorrent
char* end = buf.get() + needed;
int s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
{
ec = error_code(errno, asio::error::system_category);
return std::vector<ip_route>();
}
rt_msghdr* rtm;
for (char* next = buf.get(); next < end; next += rtm->rtm_msglen)
{
@@ -536,11 +727,13 @@ namespace libtorrent
continue;
ip_route r;
if (parse_route(rtm, &r)) ret.push_back(r);
if (parse_route(s, rtm, &r)) ret.push_back(r);
}
close(s);
#elif defined TORRENT_WINDOWS || defined TORRENT_MINGW
/*
move this to enum_net_interfaces
// Load Iphlpapi library
HMODULE iphlp = LoadLibraryA("Iphlpapi.dll");
if (!iphlp)
@@ -596,11 +789,109 @@ namespace libtorrent
ret.push_back(r);
}
}
// Free memory
free(adapter_info);
FreeLibrary(iphlp);
*/
// Load Iphlpapi library
HMODULE iphlp = LoadLibraryA("Iphlpapi.dll");
if (!iphlp)
{
ec = asio::error::operation_not_supported;
return std::vector<ip_route>();
}
typedef DWORD (WINAPI *GetIpForwardTable2_t)(
ADDRESS_FAMILY, PMIB_IPFORWARD_TABLE2*);
typedef void (WINAPI *FreeMibTable_t)(PVOID Memory);
GetIpForwardTable2_t GetIpForwardTable2 = (GetIpForwardTable2_t)GetProcAddress(
iphlp, "GetIpForwardTable2");
FreeMibTable_t FreeMibTable = (FreeMibTable_t)GetProcAddress(
iphlp, "FreeMibTable");
if (GetIpForwardTable2 && FreeMibTable)
{
MIB_IPFORWARD_TABLE2* routes = NULL;
int res = GetIpForwardTable2(AF_UNSPEC, &routes);
if (res == NO_ERROR)
{
for (int i = 0; i < routes->NumEntries; ++i)
{
ip_route r;
r.gateway = sockaddr_to_address((const sockaddr*)&routes->Table[i].NextHop);
r.destination = sockaddr_to_address(
(const sockaddr*)&routes->Table[i].DestinationPrefix.Prefix);
r.netmask = build_netmask(routes->Table[i].SitePrefixLength
, routes->Table[i].DestinationPrefix.Prefix.si_family);
MIB_IFROW ifentry;
ifentry.dwIndex = routes->Table[i].InterfaceIndex;
if (GetIfEntry(&ifentry) == NO_ERROR)
{
wcstombs(r.name, ifentry.wszName, sizeof(r.name));
r.mtu = ifentry.dwMtu;
ret.push_back(r);
}
}
}
if (routes) FreeMibTable(routes);
FreeLibrary(iphlp);
return ret;
}
// Get GetIpForwardTable() pointer
typedef DWORD (WINAPI *GetIpForwardTable_t)(PMIB_IPFORWARDTABLE pIpForwardTable,PULONG pdwSize,BOOL bOrder);
GetIpForwardTable_t GetIpForwardTable = (GetIpForwardTable_t)GetProcAddress(
iphlp, "GetIpForwardTable");
if (!GetIpForwardTable)
{
FreeLibrary(iphlp);
ec = asio::error::operation_not_supported;
return std::vector<ip_route>();
}
MIB_IPFORWARDTABLE* routes = NULL;
ULONG out_buf_size = 0;
if (GetIpForwardTable(routes, &out_buf_size, FALSE) != ERROR_INSUFFICIENT_BUFFER)
{
FreeLibrary(iphlp);
ec = asio::error::operation_not_supported;
return std::vector<ip_route>();
}
routes = (MIB_IPFORWARDTABLE*)malloc(out_buf_size);
if (!routes)
{
FreeLibrary(iphlp);
ec = asio::error::no_memory;
return std::vector<ip_route>();
}
if (GetIpForwardTable(routes, &out_buf_size, FALSE) == NO_ERROR)
{
for (int i = 0; i < routes->dwNumEntries; ++i)
{
ip_route r;
r.destination = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardDest);
r.netmask = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardMask);
r.gateway = inaddr_to_address((in_addr const*)&routes->table[i].dwForwardNextHop);
MIB_IFROW ifentry;
ifentry.dwIndex = routes->table[i].dwForwardIfIndex;
if (GetIfEntry(&ifentry) == NO_ERROR)
{
wcstombs(r.name, ifentry.wszName, sizeof(r.name));
r.name[sizeof(r.name)-1] = 0;
r.mtu = ifentry.dwMtu;
ret.push_back(r);
}
}
}
// Free memory
free(routes);
FreeLibrary(iphlp);
#elif defined TORRENT_LINUX
enum { BUFSIZE = 8192 };
@@ -639,11 +930,18 @@ namespace libtorrent
return std::vector<ip_route>();
}
int s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
{
ec = error_code(errno, asio::error::system_category);
return std::vector<ip_route>();
}
for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len))
{
ip_route r;
if (parse_route(nl_msg, &r)) ret.push_back(r);
if (parse_route(s, nl_msg, &r)) ret.push_back(r);
}
close(s);
close(sock);
#endif