Chromium Code Reviews| 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 |
| + |