added some precautionary checks in bdecoder
[libtorrent.git] / src / enum_net.cpp
blobeb5a9eeefe388a4f8c47adf5b2b03e8341695e6f
1 /*
3 Copyright (c) 2007, Arvid Norberg
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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>
35 #include <vector>
36 #include "libtorrent/enum_net.hpp"
37 #include "libtorrent/broadcast_socket.hpp"
38 #if BOOST_VERSION < 103500
39 #include <asio/ip/host_name.hpp>
40 #else
41 #include <boost/asio/ip/host_name.hpp>
42 #endif
44 #if defined TORRENT_BSD
45 #include <sys/ioctl.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <net/if.h>
49 #include <net/route.h>
50 #include <sys/sysctl.h>
51 #include <boost/scoped_array.hpp>
52 #endif
54 #if defined TORRENT_WINDOWS
55 #ifndef WIN32_LEAN_AND_MEAN
56 #define WIN32_LEAN_AND_MEAN
57 #endif
58 #include <windows.h>
59 #include <iphlpapi.h>
60 #endif
62 #if defined TORRENT_LINUX
63 #include <asm/types.h>
64 #include <netinet/ether.h>
65 #include <netinet/in.h>
66 #include <net/if.h>
67 #include <stdio.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>
74 #include <string.h>
75 #include <stdlib.h>
76 #include <unistd.h>
77 #endif
79 namespace libtorrent { namespace
82 address inaddr_to_address(in_addr const* ina)
84 typedef asio::ip::address_v4::bytes_type bytes_t;
85 bytes_t b;
86 memcpy(&b[0], ina, b.size());
87 return address_v4(b);
90 address inaddr6_to_address(in6_addr const* ina6)
92 typedef asio::ip::address_v6::bytes_type bytes_t;
93 bytes_t b;
94 memcpy(&b[0], ina6, b.size());
95 return address_v6(b);
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);
104 return address();
107 #if defined TORRENT_LINUX
109 int read_nl_sock(int sock, char *buf, int bufsize, int seq, int pid)
111 nlmsghdr* nl_hdr;
113 int msg_len = 0;
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))
123 return -1;
125 if (nl_hdr->nlmsg_type == NLMSG_DONE) break;
127 buf += read_len;
128 msg_len += read_len;
130 if ((nl_hdr->nlmsg_flags & NLM_F_MULTI) == 0) break;
132 } while((nl_hdr->nlmsg_seq != seq) || (nl_hdr->nlmsg_pid != pid));
133 return msg_len;
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))
141 return false;
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)
149 case RTA_OIF:
150 if_indextoname(*(int*)RTA_DATA(rt_attr), rt_info->name);
151 break;
152 case RTA_GATEWAY:
153 rt_info->gateway = address_v4(*(u_int*)RTA_DATA(rt_attr));
154 break;
155 case RTA_DST:
156 rt_info->destination = address_v4(*(u_int*)RTA_DATA(rt_attr));
157 break;
160 return true;
162 #endif
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)
174 rti_info[i] = 0;
175 continue;
177 rti_info[i] = sa;
179 #define ROUNDUP(a) \
180 ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
182 sa = (sockaddr*)((char*)(sa) + ROUNDUP(sa->sa_len));
184 #undef ROUNDUP
187 sa = rti_info[RTAX_GATEWAY];
188 if (sa == 0
189 || rti_info[RTAX_DST] == 0
190 || rti_info[RTAX_NETMASK] == 0
191 || (sa->sa_family != AF_INET && sa->sa_family != AF_INET6))
192 return false;
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);
198 return true;
200 #endif
203 #ifdef TORRENT_BSD
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);
211 #endif
213 }} // <anonymous>
215 namespace libtorrent
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;
240 return false;
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);
249 if (s < 0)
251 ec = asio::error::fault;
252 return ret;
254 ifconf ifc;
255 char buf[1024];
256 ifc.ifc_len = sizeof(buf);
257 ifc.ifc_buf = buf;
258 if (ioctl(s, SIOCGIFCONF, &ifc) < 0)
260 ec = error_code(errno, asio::error::system_category);
261 close(s);
262 return ret;
265 char *ifr = (char*)ifc.ifc_req;
266 int remaining = ifc.ifc_len;
268 while (remaining)
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)
275 ip_interface iface;
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();
287 else
289 ec = error_code(errno, asio::error::system_category);
290 close(s);
291 return ret;
294 else
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);
305 #endif
306 ifr += current_size;
307 remaining -= current_size;
309 close(s);
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);
317 return ret;
320 INTERFACE_INFO buffer[30];
321 DWORD size;
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);
327 closesocket(s);
328 return ret;
330 closesocket(s);
332 int n = size / sizeof(INTERFACE_INFO);
334 ip_interface iface;
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);
339 iface.name[0] = 0;
340 if (iface.interface_address == address_v4::any()) continue;
341 ret.push_back(iface);
344 #else
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);
349 if (ec) return ret;
350 ip_interface iface;
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);
358 #endif
359 return ret;
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)));
368 #else
369 std::vector<ip_route>::iterator i = std::find_if(ret.begin(), ret.end()
370 , boost::bind(&ip_route::destination, _1) == address());
371 #endif
372 if (i == ret.end()) return address();
373 return i->gateway;
376 std::vector<ip_route> enum_routes(io_service& ios, error_code& ec)
378 std::vector<ip_route> ret;
380 #if defined TORRENT_BSD
382 struct rt_msg
384 rt_msghdr m_rtm;
385 char buf[512];
388 rt_msg m;
389 int len = sizeof(rt_msg);
390 bzero(&m, len);
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;
395 m.m_rtm.rtm_seq = 0;
396 m.m_rtm.rtm_msglen = len;
398 int s = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC);
399 if (s == -1)
401 ec = error_code(errno, asio::error::system_category);
402 return std::vector<ip_route>();
405 int n = write(s, &m, len);
406 if (n == -1)
408 ec = error_code(errno, asio::error::system_category);
409 close(s);
410 return std::vector<ip_route>();
412 else if (n != len)
414 ec = asio::error::operation_not_supported;
415 close(s);
416 return std::vector<ip_route>();
418 bzero(&m, len);
420 n = read(s, &m, len);
421 if (n == -1)
423 ec = error_code(errno, asio::error::system_category);
424 close(s);
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;
432 if (ptr->rtm_errno)
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>();
462 ip_route r;
463 // destination
464 char* p = m.buf;
465 sockaddr_in* sin = (sockaddr_in*)p;
466 r.destination = sockaddr_to_address((sockaddr*)p);
468 // gateway
469 p += sin->sin_len;
470 sin = (sockaddr_in*)p;
471 r.gateway = sockaddr_to_address((sockaddr*)p);
473 // netmask
474 p += sin->sin_len;
475 sin = (sockaddr_in*)p;
476 r.netmask = sockaddr_to_address((sockaddr*)p);
477 ret.push_back(r);
479 close(s);
481 int mib[6] = { CTL_NET, PF_ROUTE, 0, AF_UNSPEC, NET_RT_DUMP, 0};
483 size_t needed = 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>();
490 if (needed <= 0)
492 return std::vector<ip_route>();
495 boost::scoped_array<char> buf(new (std::nothrow) char[needed]);
496 if (buf.get() == 0)
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;
510 rt_msghdr* rtm;
511 for (char* next = buf.get(); next < end; next += rtm->rtm_msglen)
513 rtm = (rt_msghdr*)next;
514 if (rtm->rtm_version != RTM_VERSION)
515 continue;
517 ip_route r;
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");
525 if (!iphlp)
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)
536 FreeLibrary(iphlp);
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)
545 FreeLibrary(iphlp);
546 ec = asio::error::operation_not_supported;
547 return std::vector<ip_route>();
550 adapter_info = (IP_ADAPTER_INFO*)malloc(out_buf_size);
551 if (!adapter_info)
553 FreeLibrary(iphlp);
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)
564 ip_route r;
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));
570 if (ec)
572 ec = error_code();
573 continue;
575 ret.push_back(r);
579 // Free memory
580 free(adapter_info);
581 FreeLibrary(iphlp);
583 #elif defined TORRENT_LINUX
585 enum { BUFSIZE = 8192 };
587 int sock = socket(PF_ROUTE, SOCK_DGRAM, NETLINK_ROUTE);
588 if (sock < 0)
590 ec = error_code(errno, asio::error::system_category);
591 return std::vector<ip_route>();
594 int seq = 0;
596 char msg[BUFSIZE];
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);
609 close(sock);
610 return std::vector<ip_route>();
613 int len = read_nl_sock(sock, msg, BUFSIZE, seq, getpid());
614 if (len < 0)
616 ec = error_code(errno, asio::error::system_category);
617 close(sock);
618 return std::vector<ip_route>();
621 for (; NLMSG_OK(nl_msg, len); nl_msg = NLMSG_NEXT(nl_msg, len))
623 ip_route r;
624 if (parse_route(nl_msg, &r)) ret.push_back(r);
626 close(sock);
628 #endif
629 return ret;