~ubuntu-branches/ubuntu/wily/libtorrent/wily-proposed

« back to all changes in this revision

Viewing changes to src/net/local_addr.cc

  • Committer: Bazaar Package Importer
  • Author(s): Rogério Brito
  • Date: 2011-03-20 01:06:18 UTC
  • mfrom: (1.1.13 upstream) (4.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20110320010618-g3wyylccqzqko73c
Tags: 0.12.7-5
* Use Steinar's "real" patch for IPv6. Addresses #490277, #618275,
  and Closes: #617791.
* Adapt libtorrent-0.12.6-ipv6-07.patch. It FTBFS otherwise.
* Add proper attibution to the IPv6 patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}