3 Copyright (c) 2007, Arvid Norberg
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 * Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the distribution.
15 * Neither the name of the author nor the names of its
16 contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
33 #include "libtorrent/config.hpp"
34 #include <boost/bind.hpp>
36 #include "libtorrent/enum_net.hpp"
37 #include "libtorrent/broadcast_socket.hpp"
38 #if BOOST_VERSION < 103500
39 #include <asio/ip/host_name.hpp>
41 #include <boost/asio/ip/host_name.hpp>
44 #if defined TORRENT_BSD
45 #include <sys/ioctl.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
49 #include <net/route.h>
50 #include <sys/sysctl.h>
51 #include <boost/scoped_array.hpp>
54 #if defined TORRENT_WINDOWS
55 #ifndef WIN32_LEAN_AND_MEAN
56 #define WIN32_LEAN_AND_MEAN
62 #if defined TORRENT_LINUX
63 #include <asm/types.h>
64 #include <netinet/ether.h>
65 #include <netinet/in.h>
68 #include <sys/socket.h>
69 #include <sys/ioctl.h>
70 #include <linux/netlink.h>
71 #include <linux/rtnetlink.h>
72 #include <sys/types.h>
73 #include <arpa/inet.h>
79 namespace libtorrent
{ namespace
82 address
inaddr_to_address(in_addr
const* ina
)
84 typedef asio::ip::address_v4::bytes_type bytes_t
;
86 memcpy(&b
[0], ina
, b
.size());
90 address
inaddr6_to_address(in6_addr
const* ina6
)
92 typedef asio::ip::address_v6::bytes_type bytes_t
;
94 memcpy(&b
[0], ina6
, b
.size());
98 address
sockaddr_to_address(sockaddr
const* sin
)
100 if (sin
->sa_family
== AF_INET
)
101 return inaddr_to_address(&((sockaddr_in
const*)sin
)->sin_addr
);
102 else if (sin
->sa_family
== AF_INET6
)
103 return inaddr6_to_address(&((sockaddr_in6
const*)sin
)->sin6_addr
);
107 #if defined TORRENT_LINUX
109 int read_nl_sock(int sock
, char *buf
, int bufsize
, int seq
, int pid
)
117 int read_len
= recv(sock
, buf
, bufsize
- msg_len
, 0);
118 if (read_len
< 0) return -1;
120 nl_hdr
= (nlmsghdr
*)buf
;
122 if ((NLMSG_OK(nl_hdr
, read_len
) == 0) || (nl_hdr
->nlmsg_type
== NLMSG_ERROR
))
125 if (nl_hdr
->nlmsg_type
== NLMSG_DONE
) break;
130 if ((nl_hdr
->nlmsg_flags
& NLM_F_MULTI
) == 0) break;
132 } while((nl_hdr
->nlmsg_seq
!= seq
) || (nl_hdr
->nlmsg_pid
!= pid
));
136 bool parse_route(nlmsghdr
* nl_hdr
, ip_route
* rt_info
)
138 rtmsg
* rt_msg
= (rtmsg
*)NLMSG_DATA(nl_hdr
);
140 if((rt_msg
->rtm_family
!= AF_INET
) || (rt_msg
->rtm_table
!= RT_TABLE_MAIN
))
143 int rt_len
= RTM_PAYLOAD(nl_hdr
);
144 for (rtattr
* rt_attr
= (rtattr
*)RTM_RTA(rt_msg
);
145 RTA_OK(rt_attr
,rt_len
); rt_attr
= RTA_NEXT(rt_attr
,rt_len
))
147 switch(rt_attr
->rta_type
)
150 if_indextoname(*(int*)RTA_DATA(rt_attr
), rt_info
->name
);
153 rt_info
->gateway
= address_v4(*(u_int
*)RTA_DATA(rt_attr
));
156 rt_info
->destination
= address_v4(*(u_int
*)RTA_DATA(rt_attr
));
164 #if defined TORRENT_BSD
166 bool parse_route(rt_msghdr
* rtm
, ip_route
* rt_info
)
168 sockaddr
* rti_info
[RTAX_MAX
];
169 sockaddr
* sa
= (sockaddr
*)(rtm
+ 1);
170 for (int i
= 0; i
< RTAX_MAX
; ++i
)
172 if ((rtm
->rtm_addrs
& (1 << i
)) == 0)
180 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
182 sa
= (sockaddr
*)((char*)(sa
) + ROUNDUP(sa
->sa_len
));
187 sa
= rti_info
[RTAX_GATEWAY
];
189 || rti_info
[RTAX_DST
] == 0
190 || rti_info
[RTAX_NETMASK
] == 0
191 || (sa
->sa_family
!= AF_INET
&& sa
->sa_family
!= AF_INET6
))
194 rt_info
->gateway
= sockaddr_to_address(rti_info
[RTAX_GATEWAY
]);
195 rt_info
->netmask
= sockaddr_to_address(rti_info
[RTAX_NETMASK
]);
196 rt_info
->destination
= sockaddr_to_address(rti_info
[RTAX_DST
]);
197 if_indextoname(rtm
->rtm_index
, rt_info
->name
);
204 bool verify_sockaddr(sockaddr_in
* sin
)
206 return (sin
->sin_len
== sizeof(sockaddr_in
)
207 && sin
->sin_family
== AF_INET
)
208 || (sin
->sin_len
== sizeof(sockaddr_in6
)
209 && sin
->sin_family
== AF_INET6
);
218 bool in_subnet(address
const& addr
, ip_interface
const& iface
)
220 if (addr
.is_v4() != iface
.interface_address
.is_v4()) return false;
221 // since netmasks seems unreliable for IPv6 interfaces
222 // (MacOS X returns AF_INET addresses as bitmasks) assume
223 // that any IPv6 address belongs to the subnet of any
224 // interface with an IPv6 address
225 if (addr
.is_v6()) return true;
227 return (addr
.to_v4().to_ulong() & iface
.netmask
.to_v4().to_ulong())
228 == (iface
.interface_address
.to_v4().to_ulong() & iface
.netmask
.to_v4().to_ulong());
231 bool in_local_network(io_service
& ios
, address
const& addr
, error_code
& ec
)
233 std::vector
<ip_interface
> const& net
= enum_net_interfaces(ios
, ec
);
234 if (ec
) return false;
235 for (std::vector
<ip_interface
>::const_iterator i
= net
.begin()
236 , end(net
.end()); i
!= end
; ++i
)
238 if (in_subnet(addr
, *i
)) return true;
243 std::vector
<ip_interface
> enum_net_interfaces(io_service
& ios
, error_code
& ec
)
245 std::vector
<ip_interface
> ret
;
246 // covers linux, MacOS X and BSD distributions
247 #if defined TORRENT_LINUX || defined TORRENT_BSD
248 int s
= socket(AF_INET
, SOCK_DGRAM
, 0);
251 ec
= asio::error::fault
;
256 ifc
.ifc_len
= sizeof(buf
);
258 if (ioctl(s
, SIOCGIFCONF
, &ifc
) < 0)
260 ec
= error_code(errno
, asio::error::system_category
);
265 char *ifr
= (char*)ifc
.ifc_req
;
266 int remaining
= ifc
.ifc_len
;
270 ifreq
const& item
= *reinterpret_cast<ifreq
*>(ifr
);
272 if (item
.ifr_addr
.sa_family
== AF_INET
273 || item
.ifr_addr
.sa_family
== AF_INET6
)
276 iface
.interface_address
= sockaddr_to_address(&item
.ifr_addr
);
277 strcpy(iface
.name
, item
.ifr_name
);
279 ifreq netmask
= item
;
280 if (ioctl(s
, SIOCGIFNETMASK
, &netmask
) < 0)
282 if (iface
.interface_address
.is_v6())
284 // this is expected to fail (at least on MacOS X)
285 iface
.netmask
= address_v6::any();
289 ec
= error_code(errno
, asio::error::system_category
);
296 iface
.netmask
= sockaddr_to_address(&netmask
.ifr_addr
);
298 ret
.push_back(iface
);
301 #if defined TORRENT_BSD
302 int current_size
= item
.ifr_addr
.sa_len
+ IFNAMSIZ
;
303 #elif defined TORRENT_LINUX
304 int current_size
= sizeof(ifreq
);
307 remaining
-= current_size
;
311 #elif defined TORRENT_WINDOWS
313 SOCKET s
= socket(AF_INET
, SOCK_DGRAM
, 0);
314 if (s
== SOCKET_ERROR
)
316 ec
= error_code(WSAGetLastError(), asio::error::system_category
);
320 INTERFACE_INFO buffer
[30];
323 if (WSAIoctl(s
, SIO_GET_INTERFACE_LIST
, 0, 0, buffer
,
324 sizeof(buffer
), &size
, 0, 0) != 0)
326 ec
= error_code(WSAGetLastError(), asio::error::system_category
);
332 int n
= size
/ sizeof(INTERFACE_INFO
);
335 for (int i
= 0; i
< n
; ++i
)
337 iface
.interface_address
= sockaddr_to_address(&buffer
[i
].iiAddress
.Address
);
338 iface
.netmask
= sockaddr_to_address(&buffer
[i
].iiNetmask
.Address
);
340 if (iface
.interface_address
== address_v4::any()) continue;
341 ret
.push_back(iface
);
345 #warning THIS OS IS NOT RECOGNIZED, enum_net_interfaces WILL PROBABLY NOT WORK
346 // make a best guess of the interface we're using and its IP
347 udp::resolver
r(ios
);
348 udp::resolver::iterator i
= r
.resolve(udp::resolver::query(asio::ip::host_name(ec
), "0"), ec
);
351 for (;i
!= udp::resolver_iterator(); ++i
)
353 iface
.interface_address
= i
->endpoint().address();
354 if (iface
.interface_address
.is_v4())
355 iface
.netmask
= address_v4::netmask(iface
.interface_address
.to_v4());
356 ret
.push_back(iface
);
362 address
get_default_gateway(io_service
& ios
, error_code
& ec
)
364 std::vector
<ip_route
> ret
= enum_routes(ios
, ec
);
365 #ifdef TORRENT_WINDOWS
366 std::vector
<ip_route
>::iterator i
= std::find_if(ret
.begin(), ret
.end()
367 , boost::bind(&is_loopback
, boost::bind(&ip_route::destination
, _1
)));
369 std::vector
<ip_route
>::iterator i
= std::find_if(ret
.begin(), ret
.end()
370 , boost::bind(&ip_route::destination
, _1
) == address());
372 if (i
== ret
.end()) return address();
376 std::vector
<ip_route
> enum_routes(io_service
& ios
, error_code
& ec
)
378 std::vector
<ip_route
> ret
;
380 #if defined TORRENT_BSD
389 int len = sizeof(rt_msg);
391 m.m_rtm.rtm_type = RTM_GET;
392 m.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY;
393 m.m_rtm.rtm_version = RTM_VERSION;
394 m.m_rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
396 m.m_rtm.rtm_msglen = len;
398 int s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
401 ec = error_code(errno, asio::error::system_category);
402 return std::vector<ip_route>();
405 int n = write(s, &m, len);
408 ec = error_code(errno, asio::error::system_category);
410 return std::vector<ip_route>();
414 ec = asio::error::operation_not_supported;
416 return std::vector<ip_route>();
420 n = read(s, &m, len);
423 ec = error_code(errno, asio::error::system_category);
425 return std::vector<ip_route>();
428 for (rt_msghdr* ptr = &m.m_rtm; (char*)ptr < ((char*)&m.m_rtm) + n; ptr = (rt_msghdr*)(((char*)ptr) + ptr->rtm_msglen))
430 std::cout << " rtm_msglen: " << ptr->rtm_msglen << std::endl;
431 std::cout << " rtm_type: " << ptr->rtm_type << std::endl;
434 ec = error_code(ptr->rtm_errno, asio::error::system_category);
435 return std::vector<ip_route>();
437 if (m.m_rtm.rtm_flags & RTF_UP == 0
438 || m.m_rtm.rtm_flags & RTF_GATEWAY == 0)
440 ec = asio::error::operation_not_supported;
441 return address_v4::any();
443 if (ptr->rtm_addrs & RTA_DST == 0
444 || ptr->rtm_addrs & RTA_GATEWAY == 0
445 || ptr->rtm_addrs & RTA_NETMASK == 0)
447 ec = asio::error::operation_not_supported;
448 return std::vector<ip_route>();
450 if (ptr->rtm_msglen > len - ((char*)ptr - ((char*)&m.m_rtm)))
452 ec = asio::error::operation_not_supported;
453 return std::vector<ip_route>();
455 int min_len = sizeof(rt_msghdr) + 2 * sizeof(sockaddr_in);
456 if (m.m_rtm.rtm_msglen < min_len)
458 ec = asio::error::operation_not_supported;
459 return std::vector<ip_route>();
465 sockaddr_in* sin = (sockaddr_in*)p;
466 r.destination = sockaddr_to_address((sockaddr*)p);
470 sin = (sockaddr_in*)p;
471 r.gateway = sockaddr_to_address((sockaddr*)p);
475 sin = (sockaddr_in*)p;
476 r.netmask = sockaddr_to_address((sockaddr*)p);
481 int mib
[6] = { CTL_NET
, PF_ROUTE
, 0, AF_UNSPEC
, NET_RT_DUMP
, 0};
484 if (sysctl(mib
, 6, 0, &needed
, 0, 0) < 0)
486 ec
= error_code(errno
, asio::error::system_category
);
487 return std::vector
<ip_route
>();
492 return std::vector
<ip_route
>();
495 boost::scoped_array
<char> buf(new (std::nothrow
) char[needed
]);
498 ec
= asio::error::no_memory
;
499 return std::vector
<ip_route
>();
502 if (sysctl(mib
, 6, buf
.get(), &needed
, 0, 0) < 0)
504 ec
= error_code(errno
, asio::error::system_category
);
505 return std::vector
<ip_route
>();
508 char* end
= buf
.get() + needed
;
511 for (char* next
= buf
.get(); next
< end
; next
+= rtm
->rtm_msglen
)
513 rtm
= (rt_msghdr
*)next
;
514 if (rtm
->rtm_version
!= RTM_VERSION
)
518 if (parse_route(rtm
, &r
)) ret
.push_back(r
);
521 #elif defined TORRENT_WINDOWS
523 // Load Iphlpapi library
524 HMODULE iphlp
= LoadLibraryA("Iphlpapi.dll");
527 ec
= asio::error::operation_not_supported
;
528 return std::vector
<ip_route
>();
531 // Get GetAdaptersInfo() pointer
532 typedef DWORD (WINAPI
*GetAdaptersInfo_t
)(PIP_ADAPTER_INFO
, PULONG
);
533 GetAdaptersInfo_t GetAdaptersInfo
= (GetAdaptersInfo_t
)GetProcAddress(iphlp
, "GetAdaptersInfo");
534 if (!GetAdaptersInfo
)
537 ec
= asio::error::operation_not_supported
;
538 return std::vector
<ip_route
>();
541 PIP_ADAPTER_INFO adapter_info
= 0;
542 ULONG out_buf_size
= 0;
543 if (GetAdaptersInfo(adapter_info
, &out_buf_size
) != ERROR_BUFFER_OVERFLOW
)
546 ec
= asio::error::operation_not_supported
;
547 return std::vector
<ip_route
>();
550 adapter_info
= (IP_ADAPTER_INFO
*)malloc(out_buf_size
);
554 ec
= asio::error::no_memory
;
555 return std::vector
<ip_route
>();
558 if (GetAdaptersInfo(adapter_info
, &out_buf_size
) == NO_ERROR
)
560 for (PIP_ADAPTER_INFO adapter
= adapter_info
;
561 adapter
!= 0; adapter
= adapter
->Next
)
565 r
.destination
= address::from_string(adapter
->IpAddressList
.IpAddress
.String
, ec
);
566 r
.gateway
= address::from_string(adapter
->GatewayList
.IpAddress
.String
, ec
);
567 r
.netmask
= address::from_string(adapter
->IpAddressList
.IpMask
.String
, ec
);
568 strncpy(r
.name
, adapter
->AdapterName
, sizeof(r
.name
));
583 #elif defined TORRENT_LINUX
585 enum { BUFSIZE
= 8192 };
587 int sock
= socket(PF_ROUTE
, SOCK_DGRAM
, NETLINK_ROUTE
);
590 ec
= error_code(errno
, asio::error::system_category
);
591 return std::vector
<ip_route
>();
597 memset(msg
, 0, BUFSIZE
);
598 nlmsghdr
* nl_msg
= (nlmsghdr
*)msg
;
600 nl_msg
->nlmsg_len
= NLMSG_LENGTH(sizeof(rtmsg
));
601 nl_msg
->nlmsg_type
= RTM_GETROUTE
;
602 nl_msg
->nlmsg_flags
= NLM_F_DUMP
| NLM_F_REQUEST
;
603 nl_msg
->nlmsg_seq
= seq
++;
604 nl_msg
->nlmsg_pid
= getpid();
606 if (send(sock
, nl_msg
, nl_msg
->nlmsg_len
, 0) < 0)
608 ec
= error_code(errno
, asio::error::system_category
);
610 return std::vector
<ip_route
>();
613 int len
= read_nl_sock(sock
, msg
, BUFSIZE
, seq
, getpid());
616 ec
= error_code(errno
, asio::error::system_category
);
618 return std::vector
<ip_route
>();
621 for (; NLMSG_OK(nl_msg
, len
); nl_msg
= NLMSG_NEXT(nl_msg
, len
))
624 if (parse_route(nl_msg
, &r
)) ret
.push_back(r
);