Index: net/dns/address_sorter_win.cc |
diff --git a/net/dns/address_sorter_win.cc b/net/dns/address_sorter_win.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..062b635a309d144c6ad8c54c7f929e25e491fac8 |
--- /dev/null |
+++ b/net/dns/address_sorter_win.cc |
@@ -0,0 +1,144 @@ |
+// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "net/dns/address_sorter.h" |
+ |
+#include <winsock2.h> |
+ |
+#include <algorithm> |
+ |
+#include "base/logging.h" |
+#include "base/win/windows_version.h" |
+#include "net/base/address_list.h" |
+#include "net/base/ip_endpoint.h" |
+#include "net/base/winsock_init.h" |
+ |
+namespace net { |
+ |
+namespace { |
+ |
+// If |ipe| has IPv4-mapped IPv6 address, converts it to IPv4. |
mmenke
2012/07/30 15:37:00
nit: "and leaves all other addresses unmodified."
|
+void ConvertIPv4MappedToIPv4(IPEndPoint* ipe) { |
mmenke
2012/07/30 15:37:00
May want to put this in net_util.cc. I don't see
|
+ DCHECK_EQ(AF_INET6, ipe->GetFamily()); |
+ const IPAddressNumber& address = ipe->address(); |
+ const unsigned char prefix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF }; |
+ if (std::equal(address.begin(), address.begin() + arraysize(prefix), |
+ prefix)) { |
+ *ipe = IPEndPoint( |
+ IPAddressNumber(address.begin() + arraysize(prefix), address.end()), |
+ ipe->port()); |
+ } |
+} |
+ |
+class AddressSorterWin : public AddressSorter { |
+ public: |
+ AddressSorterWin() : socket_(INVALID_SOCKET) { |
+ EnsureWinsockInit(); |
+ socket_ = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); |
+ DCHECK_NE(INVALID_SOCKET, socket_); |
+ } |
+ |
+ virtual ~AddressSorterWin() { |
+ closesocket(socket_); |
+ } |
+ |
+ virtual bool Sort(AddressList* list) const OVERRIDE { |
+ size_t heap_size = sizeof(SOCKET_ADDRESS_LIST) + |
+ list->size() * (sizeof(SOCKET_ADDRESS) + sizeof(SOCKADDR_STORAGE)); |
+ scoped_ptr_malloc<SOCKET_ADDRESS_LIST> heap_in( |
+ reinterpret_cast<SOCKET_ADDRESS_LIST*>(malloc(heap_size))); |
+ |
+ SOCKET_ADDRESS_LIST* sort_list = heap_in.get(); |
+ sort_list->iAddressCount = list->size(); |
+ SOCKADDR_STORAGE* storage = reinterpret_cast<SOCKADDR_STORAGE*>( |
+ sort_list->Address + sort_list->iAddressCount); |
+ |
+ for (size_t i = 0; i < list->size(); ++i) { |
+ IPEndPoint& ipe = (*list)[i]; |
+ // Addresses must be sockaddr_in6. |
+ if (ipe.GetFamily() == AF_INET) { |
+ ipe = IPEndPoint(ConvertIPv4NumberToIPv6Number(ipe.address()), |
+ ipe.port()); |
+ } |
+ |
+ struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(storage + i); |
+ socklen_t addr_len = sizeof(SOCKADDR_STORAGE); |
+ bool result = ipe.ToSockAddr(addr, &addr_len); |
+ DCHECK(result); |
+ sort_list->Address[i].lpSockaddr = addr; |
+ sort_list->Address[i].iSockaddrLength = addr_len; |
+ } |
+ |
+ DWORD result_size = 0; |
+ int result = WSAIoctl(socket_, SIO_ADDRESS_LIST_SORT, sort_list, heap_size, |
cbentzel
2012/07/30 15:53:43
Could this be blocking the IO thread?
szym
2012/07/30 16:28:45
Yes, but no more than GetAdaptersAddresses. The wo
mmenke1
2012/07/30 17:19:06
If I recall correctly, eroman has said previously
|
+ sort_list, heap_size, &result_size, NULL, NULL); |
mmenke
2012/07/30 15:37:00
Wonder if this ever eats addresses depending on lo
szym
2012/07/30 16:28:45
I'll need to test it, but either way I wouldn't te
|
+ if (result == SOCKET_ERROR) { |
+ LOG(ERROR) << "SIO_ADDRESS_LIST_SORT failed" << WSAGetLastError(); |
+ return false; // Unsorted. |
+ } |
+ list->clear(); |
+ for (int i = 0; i < sort_list->iAddressCount; ++i) { |
+ IPEndPoint ipe; |
+ ipe.FromSockAddr(sort_list->Address[i].lpSockaddr, |
+ sort_list->Address[i].iSockaddrLength); |
+ // Unmap V4MAPPED IPv6 addresses so that Happy Eyeballs works properly. |
+ ConvertIPv4MappedToIPv4(&ipe); |
+ list->push_back(ipe); |
+ } |
+ return true; |
+ } |
+ |
+ private: |
+ // The socket to run WSAIoctl on. |
+ SOCKET socket_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AddressSorterWin); |
+}; |
+ |
+// Wrapper for AddressSorterWin which does not sort IPv4 or IPv4-mapped |
+// addresses but always puts them at the end of the list. Needed because the |
+// SIO_ADDRESS_LIST_SORT does not support IPv4 addresses on Windows XP. |
+class AddressSorterWinXP : public AddressSorter { |
+ public: |
+ AddressSorterWinXP() {} |
+ virtual ~AddressSorterWinXP() {} |
+ |
+ virtual bool Sort(AddressList* list) const OVERRIDE { |
+ AddressList list_ipv4; |
+ AddressList list_ipv6; |
+ for (size_t i = 0; i < list->size(); ++i) { |
+ IPEndPoint ipe = (*list)[i]; |
+ if (ipe.GetFamily() == AF_INET6) |
+ ConvertIPv4MappedToIPv4(&ipe); |
+ if (ipe.GetFamily() == AF_INET) { |
+ list_ipv4.push_back(ipe); |
+ } else { |
+ list_ipv6.push_back(ipe); |
+ } |
+ } |
+ if (!list_ipv6.empty() && !sorter_.Sort(&list_ipv6)) |
+ return false; |
+ list->clear(); |
+ list->insert(list->end(), list_ipv6.begin(), list_ipv6.end()); |
+ list->insert(list->end(), list_ipv4.begin(), list_ipv4.end()); |
+ return true; |
+ } |
+ |
+ private: |
+ AddressSorterWin sorter_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AddressSorterWinXP); |
+}; |
+ |
+} // namespace |
+ |
+// static |
+scoped_ptr<AddressSorter> AddressSorter::CreateAddressSorter() { |
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) |
+ return scoped_ptr<AddressSorter>(new AddressSorterWinXP()); |
+ return scoped_ptr<AddressSorter>(new AddressSorterWin()); |
+} |
+ |
+} // namespace net |
+ |