Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(669)

Side by Side Diff: net/dns/address_sorter_posix.cc

Issue 10442098: [net/dns] Resolve AF_UNSPEC on dual-stacked systems. Sort addresses according to RFC3484. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Handle ifa_netmnask == NULL and other errors after getifaddrs. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/dns/address_sorter_posix.h"
6
7 #include <netinet/in.h>
8
9 #if defined(OS_MACOSX) || defined(OS_BSD)
10 #include <sys/socket.h> // Must be included before ifaddrs.h.
11 #include <ifaddrs.h>
12 #include <net/if.h>
13 #include <netinet/in_var.h>
14 #include <string.h>
15 #include <sys/ioctl.h>
16 #endif
17
18 #include <algorithm>
19 #include <map>
20 #include <vector>
21
22 #include "base/eintr_wrapper.h"
23 #include "base/logging.h"
24 #include "base/memory/scoped_vector.h"
25 #include "net/base/address_list.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/net_util.h"
28 #include "net/base/network_change_notifier.h"
29 #include "net/socket/client_socket_factory.h"
30 #include "net/udp/datagram_client_socket.h"
31
32 #if defined(OS_LINUX)
33 #include "net/base/address_tracker_linux.h"
34 #endif
35
36 namespace net {
37
38 namespace {
39
40 // Address sorting is performed according to RFC3484 with revisions.
41 // http://tools.ietf.org/html/draft-ietf-6man-rfc3484bis-06
42 // Precedence and label are separate to support override through /etc/gai.conf.
43
44 // Returns true if |p1| should precede |p2| in the table.
45 // Sorts table by decreasing prefix size to allow longest prefix matching.
46 bool ComparePolicy(const PolicyEntry& p1, const PolicyEntry& p2) {
47 return p1.prefix_length > p2.prefix_length;
48 }
49
50 // Creates sorted PolicyTable from |table| with |size| entries.
51 PolicyTable LoadPolicy(PolicyEntry* table, size_t size) {
52 PolicyTable result(table, table + size);
53 std::sort(result.begin(), result.end(), ComparePolicy);
54 return result;
55 }
56
57 // Search |table| for matching prefix of |address|. |table| must be sorted by
58 // descending prefix (prefix of another prefix must be later in table).
59 unsigned GetPolicyValue(const PolicyTable& table,
60 const IPAddressNumber& address) {
61 if (address.size() == kIPv4AddressSize)
62 return GetPolicyValue(table, ConvertIPv4NumberToIPv6Number(address));
63 for (unsigned i = 0; i < table.size(); ++i) {
64 const PolicyEntry& entry = table[i];
65 IPAddressNumber prefix(entry.prefix, entry.prefix + kIPv6AddressSize);
66 if (IPNumberMatchesPrefix(address, prefix, entry.prefix_length))
67 return entry.value;
68 }
69 NOTREACHED();
70 // The last entry is the least restrictive, so assume it's default.
71 return table.back().value;
72 }
73
74 bool IsMulticast(const IPAddressNumber& address) {
mmenke 2012/08/07 18:30:27 Should probably call this IsIPv6Multicast.
mmenke 2012/08/07 18:30:27 May want to DCHECK on address's length.
75 return address[0] == 0xFF;
76 }
77
78 AddressScope GetMulticastScope(const IPAddressNumber& address) {
mmenke 2012/08/07 18:30:27 And this GetIPv6MulticastScope.
mmenke 2012/08/07 18:30:27 Suggest you DCHECK on address's length and address
79 return static_cast<AddressScope>(address[1] & 0x0F);
80 }
81
82 bool IsIPv6Loopback(const IPAddressNumber& address) {
83 // IN6_IS_ADDR_LOOPBACK
84 unsigned char kLoopback[kIPv6AddressSize] = {
85 0, 0, 0, 0, 0, 0, 0, 0,
86 0, 0, 0, 0, 0, 0, 0, 1,
87 };
mmenke 2012/08/07 18:30:27 nit: The style for C-style initialization seems t
88 return address == IPAddressNumber(kLoopback, kLoopback + kIPv6AddressSize);
89 }
90
91 bool IsLinkLocal(const IPAddressNumber& address) {
mmenke 2012/08/07 18:30:27 Suggest IsIPv6LinkLocal, and IsIPv6SiteLocal below
92 // IN6_IS_ADDR_LINKLOCAL
93 return (address[0] == 0xFE) && ((address[1] & 0xC0) == 0x80);
94 }
95
96 bool IsSiteLocal(const IPAddressNumber& address) {
97 // IN6_IS_ADDR_SITELOCAL
98 return (address[0] == 0xFE) && ((address[1] & 0xC0) == 0xC0);
99 }
100
101 AddressScope GetScope(const PolicyTable& table,
102 const IPAddressNumber& address) {
103 if (address.size() == kIPv6AddressSize) {
104 if (IsMulticast(address)) {
105 return GetMulticastScope(address);
106 } else if (IsIPv6Loopback(address) || IsLinkLocal(address)) {
107 return SCOPE_LINKLOCAL;
108 } else if (IsSiteLocal(address)) {
109 return SCOPE_SITELOCAL;
110 } else {
111 return SCOPE_GLOBAL;
112 }
113 } else if (address.size() == kIPv4AddressSize) {
114 return static_cast<AddressScope>(GetPolicyValue(table, address));
115 } else {
116 NOTREACHED();
117 return SCOPE_NODELOCAL;
118 }
119 }
120
121 // Default policy table. RFC 3484, Section 2.1.
mmenke 2012/08/07 18:30:27 Actually RFC 3484, Httpbis draft 06, Section 2.1.
122 PolicyEntry kDefaultPrecedenceTable[] = {
123 // ::1/128 -- loopback
124 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 128, 50 },
125 // ::/0 -- any
126 { { }, 0, 40 },
127 // ::ffff:0:0/96 -- IPv4 mapped
128 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF }, 96, 35 },
129 // 2002::/16 -- 6to4
130 { { 0x20, 0x02, }, 16, 30 },
131 // 2001::/32 -- Teredo
132 { { 0x20, 0x01, 0, 0 }, 32, 5 },
133 // fc00::/7 -- unique local address
134 { { 0xFC }, 7, 3 },
135 // ::/96 -- IPv4 compatible
136 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 96, 1 },
137 // fec0::/10 -- site-local expanded scope
138 { { 0xFE, 0xC0 }, 10, 1 },
139 // 3ffe::/16 -- 6bone
140 { { 0x3F, 0xFE }, 16, 1 },
141 };
142
143 PolicyEntry kDefaultLabelTable[] = {
144 // ::1/128 -- loopback
145 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 128, 0 },
146 // ::/0 -- any
147 { { }, 0, 1 },
148 // ::ffff:0:0/96 -- IPv4 mapped
149 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF }, 96, 4 },
150 // 2002::/16 -- 6to4
151 { { 0x20, 0x02, }, 16, 2 },
152 // 2001::/32 -- Teredo
153 { { 0x20, 0x01, 0, 0 }, 32, 5 },
154 // fc00::/7 -- unique local address
155 { { 0xFC }, 7, 13 },
156 // ::/96 -- IPv4 compatible
157 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 96, 3 },
158 // fec0::/10 -- site-local expanded scope
159 { { 0xFE, 0xC0 }, 10, 11 },
160 // 3ffe::/16 -- 6bone
161 { { 0x3F, 0xFE }, 16, 12 },
162 };
163
164 // Default mapping of IPv4 addresses to scope.
165 PolicyEntry kDefaultIPv4ScopeTable[] = {
166 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x7F }, 104,
167 SCOPE_LINKLOCAL },
168 { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0xA9, 0xFE }, 112,
169 SCOPE_LINKLOCAL },
170 { { }, 0, SCOPE_GLOBAL },
171 };
172
173 // Returns number of matching initial bits between the addresses |a1| and |a2|.
174 unsigned CommonPrefixLength(const IPAddressNumber& a1,
175 const IPAddressNumber& a2) {
176 DCHECK_EQ(a1.size(), a2.size());
177 for (size_t i = 0; i < a1.size(); ++i) {
178 unsigned diff = a1[i] ^ a2[i];
179 if (!diff)
180 continue;
181 for (unsigned j = 0; j < CHAR_BIT; ++j) {
182 if (diff & (1 << (CHAR_BIT - 1)))
183 return i * CHAR_BIT + j;
184 diff <<= 1;
185 }
mmenke 2012/08/07 18:30:27 NOTREACHED()?
szym 2012/08/07 20:02:48 Will be reached if |a1 == a2|.
mmenke1 2012/08/07 20:08:50 No it won't - then diff is 0, and you hit the cont
szym 2012/08/07 20:13:33 Sorry, I didn't notice that you're asking for NOTR
186 }
187 return a1.size() * CHAR_BIT;
188 }
189
190 // Computes the number of leading 1-bits in |addr|.
191 unsigned MaskPrefixLength(const IPAddressNumber& addr) {
mmenke 2012/08/07 18:30:27 Still think you might want to rename addr, since i
192 IPAddressNumber all_ones(addr.size(), 0xFF);
193 return CommonPrefixLength(addr, all_ones);
194 }
195
196 struct DestinationInfo {
197 IPAddressNumber address;
198 AddressScope scope;
199 unsigned precedence;
200 unsigned label;
201 const SourceAddressInfo* src;
202 unsigned common_prefix_length;
203 };
204
205 // Returns true iff |dst_a| should precede |dst_b| in the address list.
206 // RFC 3484, section 6.
207 bool CompareDestinations(const DestinationInfo* dst_a,
208 const DestinationInfo* dst_b) {
209 // Rule 1: Avoid unusable destinations.
210 // Unusable destinations are already filtered out.
211 DCHECK(dst_a->src);
212 DCHECK(dst_b->src);
213
214 // Rule 2: Prefer matching scope.
215 bool scope_match1 = (dst_a->src->scope == dst_a->scope);
216 bool scope_match2 = (dst_b->src->scope == dst_b->scope);
217 if (scope_match1 != scope_match2)
218 return scope_match1;
219
220 // Rule 3: Avoid deprecated addresses.
221 if (dst_a->src->deprecated != dst_b->src->deprecated)
222 return !dst_a->src->deprecated;
223
224 // Rule 4: Prefer home addresses.
225 if (dst_a->src->home != dst_b->src->home)
226 return dst_a->src->home;
227
228 // Rule 5: Prefer matching label.
229 bool label_match1 = (dst_a->src->label == dst_a->label);
230 bool label_match2 = (dst_b->src->label == dst_b->label);
231 if (label_match1 != label_match2)
232 return label_match1;
233
234 // Rule 6: Prefer higher precedence.
235 if (dst_a->precedence != dst_b->precedence)
236 return dst_a->precedence > dst_b->precedence;
237
238 // Rule 7: Prefer native transport.
239 if (dst_a->src->native != dst_b->src->native)
240 return dst_a->src->native;
241
242 // Rule 8: Prefer smaller scope.
243 if (dst_a->scope != dst_b->scope)
244 return dst_a->scope < dst_b->scope;
245
246 // Rule 9: Use longest matching prefix. Only for matching address families.
247 if (dst_a->address.size() == dst_b->address.size()) {
248 if (dst_a->common_prefix_length != dst_b->common_prefix_length)
249 return dst_a->common_prefix_length > dst_b->common_prefix_length;
250 }
251
252 // Rule 10: Leave the order unchanged.
253 // stable_sort takes care of that.
254 return false;
255 }
256
257 } // namespace
258
259 AddressSorterPosix::AddressSorterPosix(ClientSocketFactory* socket_factory)
260 : socket_factory_(socket_factory),
261 precedence_table_(LoadPolicy(kDefaultPrecedenceTable,
262 arraysize(kDefaultPrecedenceTable))),
263 label_table_(LoadPolicy(kDefaultLabelTable,
264 arraysize(kDefaultLabelTable))),
265 ipv4_scope_table_(LoadPolicy(kDefaultIPv4ScopeTable,
266 arraysize(kDefaultIPv4ScopeTable))) {
267 NetworkChangeNotifier::AddIPAddressObserver(this);
268 OnIPAddressChanged();
269 }
270
271 AddressSorterPosix::~AddressSorterPosix() {
272 NetworkChangeNotifier::RemoveIPAddressObserver(this);
273 }
274
275 void AddressSorterPosix::Sort(const AddressList& list,
276 const CallbackType& callback) const {
277 ScopedVector<DestinationInfo> sort_list;
278
279 for (size_t i = 0; i < list.size(); ++i) {
280 scoped_ptr<DestinationInfo> info(new DestinationInfo());
281 info->address = list[i].address();
282 info->scope = GetScope(ipv4_scope_table_, info->address);
283 info->precedence = GetPolicyValue(precedence_table_, info->address);
284 info->label = GetPolicyValue(label_table_, info->address);
285
286 // Each socket can only be bound once.
287 scoped_ptr<DatagramClientSocket> socket(
288 socket_factory_->CreateDatagramClientSocket(
289 DatagramSocket::DEFAULT_BIND,
290 RandIntCallback(),
291 NULL /* NetLog */,
292 NetLog::Source()));
293
294 // Even though no packets are sent, cannot use port 0 in Connect.
295 IPEndPoint dest(info->address, 80 /* port */);
296 int rv = socket->Connect(dest);
297 if (rv != OK) {
298 LOG(WARNING) << "Could not connect to " << dest.ToStringWithoutPort()
299 << " reason " << rv;
300 continue;
301 }
302 // Filter out unusable destinations.
303 IPEndPoint src;
304 rv = socket->GetLocalAddress(&src);
305 if (rv != OK) {
306 LOG(WARNING) << "Could not get local address for "
307 << src.ToStringWithoutPort() << " reason " << rv;
308 continue;
309 }
310
311 SourceAddressInfo& src_info = source_map_[src.address()];
312 if (src_info.scope == SCOPE_UNDEFINED) {
313 // If |source_info_| is out of date, |src| might be missing, but we still
314 // want to sort, even though the HostCache will be cleared soon.
315 FillPolicy(src.address(), &src_info);
316 }
317 info->src = &src_info;
318
319 if (info->address.size() == src.address().size()) {
320 info->common_prefix_length = std::min(
321 CommonPrefixLength(info->address, src.address()),
322 info->src->prefix_length);
323 }
324 sort_list.push_back(info.release());
325 }
326
327 std::stable_sort(sort_list.begin(), sort_list.end(), CompareDestinations);
328
329 AddressList result;
330 for (size_t i = 0; i < sort_list.size(); ++i)
331 result.push_back(IPEndPoint(sort_list[i]->address, 0 /* port */));
332
333 callback.Run(true, result);
334 }
335
336 void AddressSorterPosix::OnIPAddressChanged() {
337 #if defined(OS_LINUX)
338 const internal::AddressTrackerLinux* tracker =
339 NetworkChangeNotifier::GetAddressTracker();
340 if (!tracker)
341 return;
342 typedef internal::AddressTrackerLinux::AddressMap AddressMap;
343 AddressMap map = tracker->GetAddressMap();
344 source_map_.clear();
345 for (AddressMap::const_iterator it = map.begin(); it != map.end(); ++it) {
346 const IPAddressNumber& address = it->first;
347 const struct ifaddrmsg& msg = it->second;
348 SourceAddressInfo& info = source_map_[address];
349 info.native = false; // TODO(szym): obtain this via netlink.
350 info.deprecated = msg.ifa_flags & IFA_F_DEPRECATED;
351 info.home = msg.ifa_flags & IFA_F_HOMEADDRESS;
352 info.prefix_length = msg.ifa_prefixlen;
353 FillPolicy(address, &info);
354 }
355 #elif defined(OS_MACOSX) || defined(OS_BSD)
mmenke 2012/08/07 18:30:27 Hmm...We don't even have a NetworkChangeNotifier t
szym 2012/08/07 20:02:48 We don't. The libc on FreeBSD does not use getifad
mmenke1 2012/08/07 20:08:50 May make sense to call into this just after we see
356 // It's not clear we will receive notification when deprecated flag changes.
357 source_map_.clear();
358 // Socket for ioctl.
359 int ioctl_socket = socket(AF_INET6, SOCK_DGRAM, 0);
360 if (ioctl_socket < 0) {
361 return;
362 }
mmenke 2012/08/07 18:30:27 nit: Brackets not needed
363 struct ifaddrs* addrs;
364 int rv = getifaddrs(&addrs);
365 if (rv < 0) {
366 LOG(WARNING) << "getifaddrs failed " << rv;
367 close(ioctl_socket);
368 return;
369 }
370
371 for (struct ifaddrs* ifa = addrs; ifa != NULL; ifa = ifa->ifa_next) {
372 IPEndPoint src;
373 int rv = src.FromSockAddr(ifa->ifa_addr, ifa->ifa_addr->sa_len);
374 if (rv != OK) {
375 LOG(WARNING) << "FromSockAddr failed " << rv;
376 continue;
377 }
378 SourceAddressInfo& info = source_map_[src.address()];
379 // Note: no known way to fill in |native| and |home|.
380 info.native = info.home = info.deprecated = false;
381 if (ifa->ifa_addr->sa_family == AF_INET6) {
382 struct in6_ifreq ifr = {};
383 strncpy(ifr.ifr_name, ifa->ifa_name, sizeof(ifr.ifr_name) - 1);
384 DCHECK_LE(ifa->ifa_addr->sa_len, sizeof(ifr.ifr_ifru.ifru_addr));
385 memcpy(&ifr.ifr_ifru.ifru_addr, ifa->ifa_addr, ifa->ifa_addr->sa_len);
386 rv = ioctl(ioctl_socket, SIOCGIFAFLAG_IN6, &ifr);
387 if (rv > 0) {
388 info.deprecated = ifr.ifr_ifru.ifru_flags & IN6_IFF_DEPRECATED;
389 } else {
390 LOG(WARNING) << "SIOCGIFAFLAG_IN6 failed " << rv;
391 }
392 }
393 if (ifa->ifa_netmask) {
394 IPEndPoint netmask;
395 rv = netmask.FromSockAddr(ifa->ifa_netmask, ifa->ifa_addr->sa_len);
396 if (rv == OK) {
397 info.prefix_length = MaskPrefixLength(netmask.address());
398 } else {
399 LOG(WARNING) << "FromSockAddr failed on netmask " << rv;
400 }
401 }
402 FillPolicy(src.address(), &info);
403 }
404 freeifaddrs(addrs);
405 close(ioctl_socket);
406 #endif
407 }
408
409 void AddressSorterPosix::FillPolicy(const IPAddressNumber& address,
410 SourceAddressInfo* info) const {
411 info->scope = GetScope(ipv4_scope_table_, address);
412 info->label = GetPolicyValue(label_table_, address);
413 }
414
415 // static
416 scoped_ptr<AddressSorter> AddressSorter::CreateAddressSorter() {
417 return scoped_ptr<AddressSorter>(
418 new AddressSorterPosix(ClientSocketFactory::GetDefaultFactory()));
419 }
420
421 } // namespace net
422
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698