Line 0
Link Here
|
|
|
1 |
// libTorrent - BitTorrent library |
2 |
// Copyright (C) 2005-2007, Jari Sundell |
3 |
// |
4 |
// This program is free software; you can redistribute it and/or modify |
5 |
// it under the terms of the GNU General Public License as published by |
6 |
// the Free Software Foundation; either version 2 of the License, or |
7 |
// (at your option) any later version. |
8 |
// |
9 |
// This program is distributed in the hope that it will be useful, |
10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
// GNU General Public License for more details. |
13 |
// |
14 |
// You should have received a copy of the GNU General Public License |
15 |
// along with this program; if not, write to the Free Software |
16 |
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
17 |
// |
18 |
// In addition, as a special exception, the copyright holders give |
19 |
// permission to link the code of portions of this program with the |
20 |
// OpenSSL library under certain conditions as described in each |
21 |
// individual source file, and distribute linked combinations |
22 |
// including the two. |
23 |
// |
24 |
// You must obey the GNU General Public License in all respects for |
25 |
// all of the code used other than OpenSSL. If you modify file(s) |
26 |
// with this exception, you may extend this exception to your version |
27 |
// of the file(s), but you are not obligated to do so. If you do not |
28 |
// wish to do so, delete this exception statement from your version. |
29 |
// If you delete this exception statement from all source files in the |
30 |
// program, then also delete it here. |
31 |
// |
32 |
// Contact: Jari Sundell <jaris@ifi.uio.no> |
33 |
// |
34 |
// Skomakerveien 33 |
35 |
// 3185 Skoppum, NORWAY |
36 |
|
37 |
#include "config.h" |
38 |
|
39 |
#include <stdio.h> |
40 |
#include <ifaddrs.h> |
41 |
#include <rak/socket_address.h> |
42 |
#include <sys/types.h> |
43 |
#include <errno.h> |
44 |
|
45 |
#ifdef __linux__ |
46 |
#include <linux/netlink.h> |
47 |
#include <linux/rtnetlink.h> |
48 |
#endif |
49 |
|
50 |
#include "torrent/exceptions.h" |
51 |
#include "socket_fd.h" |
52 |
#include "local_addr.h" |
53 |
|
54 |
namespace torrent { |
55 |
namespace { |
56 |
|
57 |
// IPv4 priority, from highest to lowest: |
58 |
// |
59 |
// 1. Everything else (global address) |
60 |
// 2. Private address space (10.0.0.0/8, 172.16.0.0/16, 192.168.0.0/24) |
61 |
// 3. Empty/INADDR_ANY (0.0.0.0) |
62 |
// 4. Link-local address (169.254.0.0/16) |
63 |
// 5. Localhost (127.0.0.0/8) |
64 |
int get_priority_ipv4(const in_addr& addr) { |
65 |
if ((addr.s_addr & htonl(0xff000000U)) == htonl(0x7f000000U)) { |
66 |
return 5; |
67 |
} |
68 |
if (addr.s_addr == htonl(0)) { |
69 |
return 4; |
70 |
} |
71 |
if ((addr.s_addr & htonl(0xffff0000U)) == htonl(0xa9fe0000U)) { |
72 |
return 3; |
73 |
} |
74 |
if ((addr.s_addr & htonl(0xff000000U)) == htonl(0x0a000000U) || |
75 |
(addr.s_addr & htonl(0xffff0000U)) == htonl(0xac100000U) || |
76 |
(addr.s_addr & htonl(0xffff0000U)) == htonl(0xc0a80000U)) { |
77 |
return 2; |
78 |
} |
79 |
return 1; |
80 |
} |
81 |
|
82 |
#ifdef RAK_USE_INET6 |
83 |
// IPv6 priority, from highest to lowest: |
84 |
// |
85 |
// 1. Global address (2000::/16 not in 6to4 or Teredo) |
86 |
// 2. 6to4 (2002::/16) |
87 |
// 3. Teredo (2001::/32) |
88 |
// 4. Empty/INADDR_ANY (::) |
89 |
// 5. Everything else (link-local, ULA, etc.) |
90 |
int get_priority_ipv6(const in6_addr& addr) { |
91 |
const uint32_t *addr32 = reinterpret_cast<const uint32_t *>(addr.s6_addr); |
92 |
if (addr32[0] == htonl(0) && |
93 |
addr32[1] == htonl(0) && |
94 |
addr32[2] == htonl(0) && |
95 |
addr32[3] == htonl(0)) { |
96 |
return 4; |
97 |
} |
98 |
if (addr32[0] == htonl(0x20010000)) { |
99 |
return 3; |
100 |
} |
101 |
if ((addr32[0] & htonl(0xffff0000)) == htonl(0x20020000)) { |
102 |
return 2; |
103 |
} |
104 |
if ((addr32[0] & htonl(0xe0000000)) == htonl(0x20000000)) { |
105 |
return 1; |
106 |
} |
107 |
return 5; |
108 |
} |
109 |
#endif |
110 |
|
111 |
int get_priority(const rak::socket_address& addr) { |
112 |
switch (addr.family()) { |
113 |
case AF_INET: |
114 |
return get_priority_ipv4(addr.c_sockaddr_inet()->sin_addr); |
115 |
#ifdef RAK_USE_INET6 |
116 |
case AF_INET6: |
117 |
return get_priority_ipv6(addr.c_sockaddr_inet6()->sin6_addr); |
118 |
#endif |
119 |
default: |
120 |
throw torrent::internal_error("Unknown address family given to compare"); |
121 |
} |
122 |
} |
123 |
|
124 |
} |
125 |
|
126 |
#ifdef __linux__ |
127 |
|
128 |
// Linux-specific implementation that understands how to filter away |
129 |
// understands how to filter away secondary addresses. |
130 |
bool get_local_address(sa_family_t family, rak::socket_address *address) { |
131 |
ifaddrs *ifaddrs; |
132 |
if (getifaddrs(&ifaddrs)) { |
133 |
return false; |
134 |
} |
135 |
|
136 |
rak::socket_address best_addr; |
137 |
switch (family) { |
138 |
case AF_INET: |
139 |
best_addr.sa_inet()->clear(); |
140 |
break; |
141 |
#ifdef RAK_USE_INET6 |
142 |
case AF_INET6: |
143 |
best_addr.sa_inet6()->clear(); |
144 |
break; |
145 |
#endif |
146 |
default: |
147 |
throw torrent::internal_error("Unknown address family given to get_local_address"); |
148 |
} |
149 |
|
150 |
// The bottom bit of the priority is used to hold if the address is |
151 |
// a secondary address (e.g. with IPv6 privacy extensions) or not; |
152 |
// secondary addresses have lower priority (higher number). |
153 |
int best_addr_pri = get_priority(best_addr) * 2; |
154 |
|
155 |
// Get all the addresses via Linux' netlink interface. |
156 |
int fd = ::socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
157 |
if (fd == -1) { |
158 |
return false; |
159 |
} |
160 |
|
161 |
struct sockaddr_nl nladdr; |
162 |
memset(&nladdr, 0, sizeof(nladdr)); |
163 |
nladdr.nl_family = AF_NETLINK; |
164 |
if (::bind(fd, (sockaddr *)&nladdr, sizeof(nladdr))) { |
165 |
::close(fd); |
166 |
return false; |
167 |
} |
168 |
|
169 |
const int seq_no = 1; |
170 |
struct { |
171 |
nlmsghdr nh; |
172 |
rtgenmsg g; |
173 |
} req; |
174 |
memset(&req, 0, sizeof(req)); |
175 |
|
176 |
req.nh.nlmsg_len = sizeof(req); |
177 |
req.nh.nlmsg_type = RTM_GETADDR; |
178 |
req.nh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; |
179 |
req.nh.nlmsg_pid = getpid(); |
180 |
req.nh.nlmsg_seq = seq_no; |
181 |
req.g.rtgen_family = AF_UNSPEC; |
182 |
|
183 |
int ret; |
184 |
do { |
185 |
ret = ::sendto(fd, &req, sizeof(req), 0, (sockaddr *)&nladdr, sizeof(nladdr)); |
186 |
} while (ret == -1 && errno == EINTR); |
187 |
|
188 |
if (ret == -1) { |
189 |
::close(fd); |
190 |
return false; |
191 |
} |
192 |
|
193 |
bool done = false; |
194 |
do { |
195 |
char buf[4096]; |
196 |
socklen_t len = sizeof(nladdr); |
197 |
do { |
198 |
ret = ::recvfrom(fd, buf, sizeof(buf), 0, (sockaddr *)&nladdr, &len); |
199 |
} while (ret == -1 && errno == EINTR); |
200 |
|
201 |
if (ret < 0) { |
202 |
::close(fd); |
203 |
return false; |
204 |
} |
205 |
|
206 |
for (const nlmsghdr *nlmsg = (const nlmsghdr *)buf; |
207 |
NLMSG_OK(nlmsg, ret); |
208 |
nlmsg = NLMSG_NEXT(nlmsg, ret)) { |
209 |
if (nlmsg->nlmsg_seq != seq_no) |
210 |
continue; |
211 |
if (nlmsg->nlmsg_type == NLMSG_DONE) { |
212 |
done = true; |
213 |
break; |
214 |
} |
215 |
if (nlmsg->nlmsg_type == NLMSG_ERROR) { |
216 |
::close(fd); |
217 |
return false; |
218 |
} |
219 |
if (nlmsg->nlmsg_type != RTM_NEWADDR) |
220 |
continue; |
221 |
|
222 |
const ifaddrmsg *ifa = (const ifaddrmsg *)NLMSG_DATA(nlmsg); |
223 |
|
224 |
if (ifa->ifa_family != family) |
225 |
continue; |
226 |
|
227 |
#ifdef IFA_F_OPTIMISTIC |
228 |
if ((ifa->ifa_flags & IFA_F_OPTIMISTIC) != 0) |
229 |
continue; |
230 |
#endif |
231 |
#ifdef IFA_F_DADFAILED |
232 |
if ((ifa->ifa_flags & IFA_F_DADFAILED) != 0) |
233 |
continue; |
234 |
#endif |
235 |
#ifdef IFA_F_DEPRECATED |
236 |
if ((ifa->ifa_flags & IFA_F_DEPRECATED) != 0) |
237 |
continue; |
238 |
#endif |
239 |
#ifdef IFA_F_TENTATIVE |
240 |
if ((ifa->ifa_flags & IFA_F_TENTATIVE) != 0) |
241 |
continue; |
242 |
#endif |
243 |
|
244 |
// Since there can be point-to-point links on the machine, we need to keep |
245 |
// track of the addresses we've seen for this interface; if we see both |
246 |
// IFA_LOCAL and IFA_ADDRESS for an interface, keep only the IFA_LOCAL. |
247 |
rak::socket_address this_addr; |
248 |
bool seen_addr = false; |
249 |
int plen = IFA_PAYLOAD(nlmsg); |
250 |
for (const rtattr *rta = IFA_RTA(ifa); |
251 |
RTA_OK(rta, plen); |
252 |
rta = RTA_NEXT(rta, plen)) { |
253 |
if (rta->rta_type != IFA_LOCAL && |
254 |
rta->rta_type != IFA_ADDRESS) { |
255 |
continue; |
256 |
} |
257 |
if (rta->rta_type == IFA_ADDRESS && seen_addr) { |
258 |
continue; |
259 |
} |
260 |
seen_addr = true; |
261 |
switch (ifa->ifa_family) { |
262 |
case AF_INET: |
263 |
this_addr.sa_inet()->clear(); |
264 |
this_addr.sa_inet()->set_address(*(const in_addr *)RTA_DATA(rta)); |
265 |
break; |
266 |
#ifdef RAK_USE_INET6 |
267 |
case AF_INET6: |
268 |
this_addr.sa_inet6()->clear(); |
269 |
this_addr.sa_inet6()->set_address(*(const in6_addr *)RTA_DATA(rta)); |
270 |
break; |
271 |
#endif |
272 |
} |
273 |
} |
274 |
if (!seen_addr) |
275 |
continue; |
276 |
|
277 |
int this_addr_pri = get_priority(this_addr) * 2; |
278 |
if ((ifa->ifa_flags & IFA_F_SECONDARY) == IFA_F_SECONDARY) { |
279 |
++this_addr_pri; |
280 |
} |
281 |
|
282 |
if (this_addr_pri < best_addr_pri) { |
283 |
best_addr = this_addr; |
284 |
best_addr_pri = this_addr_pri; |
285 |
} |
286 |
} |
287 |
} while (!done); |
288 |
|
289 |
::close(fd); |
290 |
if (!best_addr.is_address_any()) { |
291 |
*address = best_addr; |
292 |
return true; |
293 |
} else { |
294 |
return false; |
295 |
} |
296 |
} |
297 |
|
298 |
#else |
299 |
|
300 |
// Generic POSIX variant. |
301 |
bool get_local_address(sa_family_t family, rak::socket_address *address) { |
302 |
SocketFd sock; |
303 |
if (!sock.open_datagram()) { |
304 |
return false; |
305 |
} |
306 |
|
307 |
rak::socket_address dummy_dest; |
308 |
dummy_dest.clear(); |
309 |
|
310 |
switch (family) { |
311 |
case rak::socket_address::af_inet: |
312 |
dummy_dest.set_address_c_str("4.0.0.0"); |
313 |
break; |
314 |
#ifdef RAK_USE_INET6 |
315 |
case rak::socket_address::af_inet6: |
316 |
dummy_dest.set_address_c_str("2001:700::"); |
317 |
break; |
318 |
#endif |
319 |
default: |
320 |
throw internal_error("Unknown address family"); |
321 |
} |
322 |
dummy_dest.set_port(80); |
323 |
|
324 |
if (!sock.connect(dummy_dest)) { |
325 |
sock.close(); |
326 |
return false; |
327 |
} |
328 |
|
329 |
bool ret = sock.getsockname(address); |
330 |
sock.close(); |
331 |
return ret; |
332 |
} |
333 |
|
334 |
#endif |
335 |
|
336 |
} |