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.
5 #include "net/dns/address_sorter.h"
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/threading/worker_pool.h"
15 #include "base/win/windows_version.h"
16 #include "net/base/address_list.h"
17 #include "net/base/ip_endpoint.h"
18 #include "net/base/winsock_init.h"
24 class AddressSorterWin
: public AddressSorter
{
30 virtual ~AddressSorterWin() {}
33 virtual void Sort(const AddressList
& list
,
34 const CallbackType
& callback
) const OVERRIDE
{
35 DCHECK(!list
.empty());
36 scoped_refptr
<Job
> job
= new Job(list
, callback
);
40 // Executes the SIO_ADDRESS_LIST_SORT ioctl on the WorkerPool, and
41 // performs the necessary conversions to/from AddressList.
42 class Job
: public base::RefCountedThreadSafe
<Job
> {
44 Job(const AddressList
& list
, const CallbackType
& callback
)
45 : callback_(callback
),
46 buffer_size_(sizeof(SOCKET_ADDRESS_LIST
) +
47 list
.size() * (sizeof(SOCKET_ADDRESS
) +
48 sizeof(SOCKADDR_STORAGE
))),
49 input_buffer_(reinterpret_cast<SOCKET_ADDRESS_LIST
*>(
50 malloc(buffer_size_
))),
51 output_buffer_(reinterpret_cast<SOCKET_ADDRESS_LIST
*>(
52 malloc(buffer_size_
))),
54 input_buffer_
->iAddressCount
= list
.size();
55 SOCKADDR_STORAGE
* storage
= reinterpret_cast<SOCKADDR_STORAGE
*>(
56 input_buffer_
->Address
+ input_buffer_
->iAddressCount
);
58 for (size_t i
= 0; i
< list
.size(); ++i
) {
59 IPEndPoint ipe
= list
[i
];
60 // Addresses must be sockaddr_in6.
61 if (ipe
.GetFamily() == ADDRESS_FAMILY_IPV4
) {
62 ipe
= IPEndPoint(ConvertIPv4NumberToIPv6Number(ipe
.address()),
66 struct sockaddr
* addr
= reinterpret_cast<struct sockaddr
*>(storage
+ i
);
67 socklen_t addr_len
= sizeof(SOCKADDR_STORAGE
);
68 bool result
= ipe
.ToSockAddr(addr
, &addr_len
);
70 input_buffer_
->Address
[i
].lpSockaddr
= addr
;
71 input_buffer_
->Address
[i
].iSockaddrLength
= addr_len
;
74 if (!base::WorkerPool::PostTaskAndReply(
76 base::Bind(&Job::Run
, this),
77 base::Bind(&Job::OnComplete
, this),
78 false /* task is slow */)) {
79 LOG(ERROR
) << "WorkerPool::PostTaskAndReply failed";
85 friend class base::RefCountedThreadSafe
<Job
>;
88 // Executed on the WorkerPool.
90 SOCKET sock
= socket(AF_INET6
, SOCK_DGRAM
, IPPROTO_UDP
);
91 if (sock
== INVALID_SOCKET
)
93 DWORD result_size
= 0;
94 int result
= WSAIoctl(sock
, SIO_ADDRESS_LIST_SORT
, input_buffer_
.get(),
95 buffer_size_
, output_buffer_
.get(), buffer_size_
,
96 &result_size
, NULL
, NULL
);
97 if (result
== SOCKET_ERROR
) {
98 LOG(ERROR
) << "SIO_ADDRESS_LIST_SORT failed " << WSAGetLastError();
105 // Executed on the calling thread.
109 list
.reserve(output_buffer_
->iAddressCount
);
110 for (int i
= 0; i
< output_buffer_
->iAddressCount
; ++i
) {
112 ipe
.FromSockAddr(output_buffer_
->Address
[i
].lpSockaddr
,
113 output_buffer_
->Address
[i
].iSockaddrLength
);
114 // Unmap V4MAPPED IPv6 addresses so that Happy Eyeballs works.
115 if (IsIPv4Mapped(ipe
.address())) {
116 ipe
= IPEndPoint(ConvertIPv4MappedToIPv4(ipe
.address()),
122 callback_
.Run(success_
, list
);
125 const CallbackType callback_
;
126 const size_t buffer_size_
;
127 scoped_ptr
<SOCKET_ADDRESS_LIST
, base::FreeDeleter
> input_buffer_
;
128 scoped_ptr
<SOCKET_ADDRESS_LIST
, base::FreeDeleter
> output_buffer_
;
131 DISALLOW_COPY_AND_ASSIGN(Job
);
134 DISALLOW_COPY_AND_ASSIGN(AddressSorterWin
);
137 // Merges |list_ipv4| and |list_ipv6| before passing it to |callback|, but
138 // only if |success| is true.
139 void MergeResults(const AddressSorter::CallbackType
& callback
,
140 const AddressList
& list_ipv4
,
142 const AddressList
& list_ipv6
) {
144 callback
.Run(false, AddressList());
148 list
.insert(list
.end(), list_ipv6
.begin(), list_ipv6
.end());
149 list
.insert(list
.end(), list_ipv4
.begin(), list_ipv4
.end());
150 callback
.Run(true, list
);
153 // Wrapper for AddressSorterWin which does not sort IPv4 or IPv4-mapped
154 // addresses but always puts them at the end of the list. Needed because the
155 // SIO_ADDRESS_LIST_SORT does not support IPv4 addresses on Windows XP.
156 class AddressSorterWinXP
: public AddressSorter
{
158 AddressSorterWinXP() {}
159 virtual ~AddressSorterWinXP() {}
162 virtual void Sort(const AddressList
& list
,
163 const CallbackType
& callback
) const OVERRIDE
{
164 AddressList list_ipv4
;
165 AddressList list_ipv6
;
166 for (size_t i
= 0; i
< list
.size(); ++i
) {
167 const IPEndPoint
& ipe
= list
[i
];
168 if (ipe
.GetFamily() == ADDRESS_FAMILY_IPV4
) {
169 list_ipv4
.push_back(ipe
);
171 list_ipv6
.push_back(ipe
);
174 if (!list_ipv6
.empty()) {
175 sorter_
.Sort(list_ipv6
, base::Bind(&MergeResults
, callback
, list_ipv4
));
177 NOTREACHED() << "Should not be called with IPv4-only addresses.";
178 callback
.Run(true, list
);
183 AddressSorterWin sorter_
;
185 DISALLOW_COPY_AND_ASSIGN(AddressSorterWinXP
);
191 scoped_ptr
<AddressSorter
> AddressSorter::CreateAddressSorter() {
192 if (base::win::GetVersion() < base::win::VERSION_VISTA
)
193 return scoped_ptr
<AddressSorter
>(new AddressSorterWinXP());
194 return scoped_ptr
<AddressSorter
>(new AddressSorterWin());