OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/dns/dns_transaction.h" | 5 #include "net/dns/dns_transaction.h" |
6 | 6 |
7 #include <deque> | |
8 #include <string> | |
9 #include <vector> | |
10 | |
7 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/memory/ref_counted.h" | |
13 #include "base/memory/scoped_ptr.h" | |
14 #include "base/memory/weak_ptr.h" | |
15 #include "base/message_loop.h" | |
8 #include "base/rand_util.h" | 16 #include "base/rand_util.h" |
9 #include "base/values.h" | 17 #include "base/stl_util.h" |
18 #include "base/string_piece.h" | |
19 #include "base/threading/non_thread_safe.h" | |
20 #include "base/timer.h" | |
21 #include "net/base/completion_callback.h" | |
22 #include "net/base/dns_util.h" | |
10 #include "net/base/io_buffer.h" | 23 #include "net/base/io_buffer.h" |
24 #include "net/base/ip_endpoint.h" | |
11 #include "net/base/net_errors.h" | 25 #include "net/base/net_errors.h" |
26 #include "net/base/net_log.h" | |
12 #include "net/dns/dns_protocol.h" | 27 #include "net/dns/dns_protocol.h" |
13 #include "net/dns/dns_query.h" | 28 #include "net/dns/dns_query.h" |
14 #include "net/dns/dns_response.h" | 29 #include "net/dns/dns_response.h" |
15 #include "net/dns/dns_session.h" | 30 #include "net/dns/dns_session.h" |
16 #include "net/socket/client_socket_factory.h" | 31 #include "net/socket/client_socket_factory.h" |
17 #include "net/udp/datagram_client_socket.h" | 32 #include "net/udp/datagram_client_socket.h" |
18 | 33 |
19 namespace net { | 34 namespace net { |
20 | 35 |
21 namespace { | 36 namespace { |
22 | 37 |
23 class DnsTransactionStartParameters : public NetLog::EventParameters { | 38 // Count labels in the fully-qualified name in DNS format. |
39 int CountLabels(const std::string& name) { | |
40 size_t count = 0; | |
41 for (size_t i = 0; i < name.size() && name[i]; i += name[i] + 1) | |
42 ++count; | |
43 return count; | |
44 } | |
45 | |
46 bool IsIPLiteral(const std::string& hostname) { | |
47 IPAddressNumber ip; | |
48 return ParseIPLiteralToNumber(hostname, &ip); | |
49 } | |
50 | |
51 class StartParameters : public NetLog::EventParameters { | |
24 public: | 52 public: |
25 DnsTransactionStartParameters(const IPEndPoint& dns_server, | 53 StartParameters(const std::string& hostname, |
26 const base::StringPiece& qname, | 54 uint16 qtype, |
27 uint16 qtype, | 55 const NetLog::Source& source) |
28 const NetLog::Source& source) | 56 : hostname_(hostname), qtype_(qtype), source_(source) {} |
29 : dns_server_(dns_server), | 57 |
30 qname_(qname.data(), qname.length()), | 58 virtual Value* ToValue() const OVERRIDE { |
31 qtype_(qtype), | |
32 source_(source) {} | |
33 | |
34 virtual Value* ToValue() const { | |
35 DictionaryValue* dict = new DictionaryValue(); | 59 DictionaryValue* dict = new DictionaryValue(); |
36 dict->SetString("dns_server", dns_server_.ToString()); | 60 dict->SetString("hostname", hostname_); |
37 dict->SetString("hostname", qname_); | |
38 dict->SetInteger("query_type", qtype_); | 61 dict->SetInteger("query_type", qtype_); |
39 if (source_.is_valid()) | 62 dict->Set("source_dependency", source_.ToValue()); |
40 dict->Set("source_dependency", source_.ToValue()); | |
41 return dict; | 63 return dict; |
42 } | 64 } |
43 | 65 |
44 private: | 66 private: |
45 IPEndPoint dns_server_; | 67 const std::string hostname_; |
46 std::string qname_; | 68 const uint16 qtype_; |
47 uint16 qtype_; | |
48 const NetLog::Source source_; | 69 const NetLog::Source source_; |
49 }; | 70 }; |
50 | 71 |
51 class DnsTransactionFinishParameters : public NetLog::EventParameters { | 72 class ResponseParameters : public NetLog::EventParameters { |
52 public: | 73 public: |
53 // TODO(szym): add rcode ? | 74 ResponseParameters(int rcode, int answer_count, const NetLog::Source& source) |
54 DnsTransactionFinishParameters(int net_error, int answer_count) | 75 : rcode_(rcode), answer_count_(answer_count), source_(source) {} |
55 : net_error_(net_error), answer_count_(answer_count) {} | 76 |
56 | 77 virtual Value* ToValue() const OVERRIDE { |
57 virtual Value* ToValue() const { | |
58 DictionaryValue* dict = new DictionaryValue(); | 78 DictionaryValue* dict = new DictionaryValue(); |
59 if (net_error_) | 79 dict->SetInteger("rcode", rcode_); |
60 dict->SetInteger("net_error", net_error_); | |
61 dict->SetInteger("answer_count", answer_count_); | 80 dict->SetInteger("answer_count", answer_count_); |
81 dict->Set("socket_source", source_.ToValue()); | |
62 return dict; | 82 return dict; |
63 } | 83 } |
64 | 84 |
65 private: | 85 private: |
66 const int net_error_; | 86 const int rcode_; |
67 const int answer_count_; | 87 const int answer_count_; |
68 }; | |
69 | |
70 class DnsTransactionRetryParameters : public NetLog::EventParameters { | |
71 public: | |
72 DnsTransactionRetryParameters(int attempt_number, | |
73 const NetLog::Source& source) | |
74 : attempt_number_(attempt_number), source_(source) {} | |
75 | |
76 virtual Value* ToValue() const { | |
77 DictionaryValue* dict = new DictionaryValue(); | |
78 dict->SetInteger("attempt_number", attempt_number_); | |
79 dict->Set("source_dependency", source_.ToValue()); | |
80 return dict; | |
81 } | |
82 | |
83 private: | |
84 const int attempt_number_; | |
85 const NetLog::Source source_; | 88 const NetLog::Source source_; |
86 }; | 89 }; |
87 | 90 |
88 } // namespace | 91 // ---------------------------------------------------------------------------- |
89 | 92 |
90 | 93 // A single asynchronous DNS exchange over UDP, which consists of sending out a |
91 DnsTransaction::DnsTransaction(DnsSession* session, | 94 // DNS query, waiting for a response, and returning the response that it |
92 const base::StringPiece& qname, | 95 // matches. Logging is done in the socket and in the outer DnsTransaction. |
93 uint16 qtype, | 96 class DnsUDPAttempt { |
94 const ResultCallback& callback, | 97 public: |
95 const BoundNetLog& source_net_log) | 98 DnsUDPAttempt(scoped_ptr<DatagramClientSocket> socket, |
99 const IPEndPoint& server, | |
100 scoped_ptr<DnsQuery> query, | |
101 const CompletionCallback& callback) | |
102 : next_state_(STATE_NONE), | |
103 socket_(socket.Pass()), | |
104 server_(server), | |
105 query_(query.Pass()), | |
106 callback_(callback) { | |
107 } | |
108 | |
109 // Starts the attempt. Returns ERR_IO_PENDING if cannot complete synchronously | |
110 // and calls |callback| upon completion. | |
111 int Start() { | |
112 DCHECK_EQ(STATE_NONE, next_state_); | |
113 next_state_ = STATE_CONNECT; | |
114 return DoLoop(OK); | |
115 } | |
116 | |
117 const DnsQuery* query() const { | |
118 return query_.get(); | |
119 } | |
120 | |
121 const DatagramClientSocket* socket() const { | |
122 return socket_.get(); | |
123 } | |
124 | |
125 // Returns the response or NULL if has not received a matching response from | |
126 // the server. | |
127 const DnsResponse* response() const { | |
128 const DnsResponse* resp = response_.get(); | |
129 return (resp != NULL && resp->is_valid()) ? resp : NULL; | |
130 } | |
131 | |
132 private: | |
133 enum State { | |
134 STATE_CONNECT, | |
135 STATE_SEND_QUERY, | |
136 STATE_SEND_QUERY_COMPLETE, | |
137 STATE_READ_RESPONSE, | |
138 STATE_READ_RESPONSE_COMPLETE, | |
139 STATE_NONE, | |
140 }; | |
141 | |
142 int DoLoop(int result) { | |
143 DCHECK_NE(STATE_NONE, next_state_); | |
144 int rv = result; | |
145 do { | |
146 State state = next_state_; | |
147 next_state_ = STATE_NONE; | |
148 switch (state) { | |
149 case STATE_CONNECT: | |
150 rv = DoConnect(); | |
151 break; | |
152 case STATE_SEND_QUERY: | |
153 rv = DoSendQuery(); | |
154 break; | |
155 case STATE_SEND_QUERY_COMPLETE: | |
156 rv = DoSendQueryComplete(rv); | |
157 break; | |
158 case STATE_READ_RESPONSE: | |
159 rv = DoReadResponse(); | |
160 break; | |
161 case STATE_READ_RESPONSE_COMPLETE: | |
162 rv = DoReadResponseComplete(rv); | |
163 break; | |
164 default: | |
165 NOTREACHED(); | |
166 break; | |
167 } | |
168 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
169 | |
170 return rv; | |
171 } | |
172 | |
173 int DoConnect() { | |
174 next_state_ = STATE_SEND_QUERY; | |
175 return socket_->Connect(server_); | |
cbentzel
2012/01/13 22:17:25
Why not DoConnectComplete? Is this guaranteed to c
szym
2012/01/13 22:32:44
We don't actually have an API that would allow for
| |
176 } | |
177 | |
178 int DoSendQuery() { | |
179 next_state_ = STATE_SEND_QUERY_COMPLETE; | |
180 return socket_->Write(query_->io_buffer(), | |
181 query_->io_buffer()->size(), | |
182 base::Bind(&DnsUDPAttempt::OnIOComplete, | |
183 base::Unretained(this))); | |
184 } | |
185 | |
186 int DoSendQueryComplete(int rv) { | |
cbentzel
2012/01/13 22:17:25
DCHECK_NE(ERR_IO_PENDING, rv);
| |
187 if (rv < 0) | |
188 return rv; | |
189 | |
190 // Writing to UDP should not result in a partial datagram. | |
191 if (rv != query_->io_buffer()->size()) | |
192 return ERR_MSG_TOO_BIG; | |
193 | |
194 next_state_ = STATE_READ_RESPONSE; | |
195 return OK; | |
196 } | |
197 | |
198 int DoReadResponse() { | |
199 next_state_ = STATE_READ_RESPONSE_COMPLETE; | |
200 response_.reset(new DnsResponse()); | |
201 return socket_->Read(response_->io_buffer(), | |
202 response_->io_buffer()->size(), | |
203 base::Bind(&DnsUDPAttempt::OnIOComplete, | |
204 base::Unretained(this))); | |
205 } | |
206 | |
207 int DoReadResponseComplete(int rv) { | |
208 DCHECK_NE(ERR_IO_PENDING, rv); | |
209 if (rv < 0) | |
210 return rv; | |
211 | |
212 DCHECK(rv); | |
213 if (!response_->InitParse(rv, *query_)) | |
214 return ERR_DNS_MALFORMED_RESPONSE; | |
215 if (response_->flags() & dns_protocol::kFlagTC) | |
216 return ERR_DNS_SERVER_REQUIRES_TCP; | |
217 if (response_->rcode() != dns_protocol::kRcodeNOERROR && | |
218 response_->rcode() != dns_protocol::kRcodeNXDOMAIN) { | |
219 return ERR_DNS_SERVER_FAILED; | |
220 } | |
221 if (response_->answer_count() == 0) | |
222 return ERR_NAME_NOT_RESOLVED; | |
223 | |
224 return OK; | |
225 } | |
226 | |
227 void OnIOComplete(int rv) { | |
228 rv = DoLoop(rv); | |
229 if (rv != ERR_IO_PENDING) | |
230 callback_.Run(rv); | |
231 } | |
232 | |
233 State next_state_; | |
234 | |
235 scoped_ptr<DatagramClientSocket> socket_; | |
236 IPEndPoint server_; | |
237 scoped_ptr<DnsQuery> query_; | |
238 | |
239 scoped_ptr<DnsResponse> response_; | |
240 | |
241 CompletionCallback callback_; | |
242 | |
243 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt); | |
244 }; | |
245 | |
246 // ---------------------------------------------------------------------------- | |
247 | |
248 // Implements DnsTransaction. Configuration is supplied by DnsSession. | |
249 // The suffix list is built according to the DnsConfig from the session. | |
250 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout. | |
251 // The first server to attempt on each query is given by | |
252 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards. | |
253 // Each server is attempted DnsConfig::attempts times. | |
254 class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe { | |
255 public: | |
256 DnsTransactionImpl(DnsSession* session, | |
257 const std::string& hostname, | |
258 uint16 qtype, | |
259 const DnsTransactionFactory::CallbackType& callback, | |
260 const BoundNetLog& source_net_log) | |
96 : session_(session), | 261 : session_(session), |
97 dns_server_(session->NextServer()), | 262 hostname_(hostname), |
98 query_(new DnsQuery(session->NextId(), qname, qtype)), | 263 qtype_(qtype), |
99 callback_(callback), | 264 callback_(callback), |
100 attempts_(0), | |
101 next_state_(STATE_NONE), | |
102 net_log_(BoundNetLog::Make(session->net_log(), | 265 net_log_(BoundNetLog::Make(session->net_log(), |
103 NetLog::SOURCE_DNS_TRANSACTION)) { | 266 NetLog::SOURCE_DNS_TRANSACTION)), |
104 net_log_.BeginEvent( | 267 successful_attempt_(NULL), |
105 NetLog::TYPE_DNS_TRANSACTION, | 268 first_server_index_(0) { |
106 make_scoped_refptr( | 269 DCHECK(session_); |
107 new DnsTransactionStartParameters(dns_server_, | 270 DCHECK(!hostname_.empty()); |
108 qname, | 271 DCHECK(!callback_.is_null()); |
109 qtype, | 272 |
110 source_net_log.source()))); | 273 DCHECK(!IsIPLiteral(hostname_)); |
111 } | 274 |
112 | 275 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION, make_scoped_refptr( |
113 DnsTransaction::~DnsTransaction() {} | 276 new StartParameters(hostname_, qtype_, source_net_log.source()))); |
114 | 277 } |
115 int DnsTransaction::Start() { | 278 |
116 DCHECK_EQ(STATE_NONE, next_state_); | 279 virtual ~DnsTransactionImpl() { |
117 next_state_ = STATE_CONNECT; | 280 STLDeleteElements(&attempts_); |
118 return DoLoop(OK); | 281 if (!callback_.is_null()) { |
119 } | 282 net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL); |
120 | 283 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, |
121 int DnsTransaction::DoLoop(int result) { | 284 ERR_ABORTED); |
122 DCHECK_NE(STATE_NONE, next_state_); | 285 } |
123 int rv = result; | 286 } |
124 do { | 287 |
125 State state = next_state_; | 288 virtual const std::string& GetHostname() const OVERRIDE { |
126 next_state_ = STATE_NONE; | 289 DCHECK(CalledOnValidThread()); |
127 switch (state) { | 290 return hostname_; |
128 case STATE_CONNECT: | 291 } |
129 rv = DoConnect(); | 292 |
293 virtual uint16 GetType() const OVERRIDE { | |
294 DCHECK(CalledOnValidThread()); | |
295 return qtype_; | |
296 } | |
297 | |
298 virtual int Start() OVERRIDE { | |
299 int rv = PrepareSearch(); | |
300 if (rv == OK) | |
301 rv = StartQuery(); | |
302 DCHECK_NE(OK, rv); | |
303 return rv; | |
304 } | |
305 | |
306 private: | |
307 // Prepares |qnames_| according to the DnsConfig. | |
308 int PrepareSearch() { | |
309 const DnsConfig& config = session_->config(); | |
310 | |
311 std::string labelled_hostname; | |
312 if (!DNSDomainFromDot(hostname_, &labelled_hostname)) | |
313 return ERR_INVALID_ARGUMENT; | |
314 | |
315 if (hostname_[hostname_.size() - 1] == '.') { | |
316 // It's a fully-qualified name, no suffix search. | |
317 qnames_.push_back(labelled_hostname); | |
318 return OK; | |
319 } | |
320 | |
321 // Set true when |labelled_hostname| is put on the list. | |
322 bool had_hostname = false; | |
323 | |
324 int ndots = CountLabels(labelled_hostname) - 1; | |
325 if (ndots >= config.ndots) { | |
326 qnames_.push_back(labelled_hostname); | |
327 had_hostname = true; | |
328 } | |
329 | |
330 std::string qname; | |
331 for (size_t i = 0; i < config.search.size(); ++i) { | |
332 // Ignore invalid (too long) combinations. | |
333 if (!DNSDomainFromDot(hostname_ + "." + config.search[i], &qname)) | |
334 continue; | |
335 if (qname.size() == labelled_hostname.size()) { | |
336 if (had_hostname) | |
337 continue; | |
338 had_hostname = true; | |
339 } | |
340 qnames_.push_back(qname); | |
341 } | |
342 | |
343 if (!had_hostname) | |
344 qnames_.push_back(labelled_hostname); | |
345 | |
346 return OK; | |
347 } | |
348 | |
349 void DoCallback(int rv) { | |
350 if (callback_.is_null()) | |
351 return; | |
352 DCHECK_NE(ERR_IO_PENDING, rv); | |
353 DCHECK(rv != OK || successful_attempt_ != NULL); | |
354 | |
355 DnsTransactionFactory::CallbackType callback = callback_; | |
356 callback_.Reset(); | |
357 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, rv); | |
358 callback.Run(this, | |
359 rv, | |
360 successful_attempt_ ? successful_attempt_->response() : NULL); | |
361 } | |
362 | |
363 // Makes another attempt at the current name, |qnames_.front()|, using the | |
364 // next nameserver. | |
365 int MakeAttempt() { | |
366 int attempt_number = attempts_.size(); | |
367 | |
368 scoped_ptr<DatagramClientSocket> socket( | |
369 session_->socket_factory()->CreateDatagramClientSocket( | |
370 DatagramSocket::RANDOM_BIND, | |
371 base::Bind(&base::RandInt), | |
372 net_log_.net_log(), | |
373 net_log_.source())); | |
374 | |
375 uint16 id = session_->NextQueryId(); | |
376 scoped_ptr<DnsQuery> query; | |
377 if (attempts_.empty()) { | |
378 query.reset(new DnsQuery(id, qnames_.front(), qtype_)); | |
379 } else { | |
380 query.reset(attempts_[0]->query()->CloneWithNewId(id)); | |
381 } | |
382 | |
383 net_log_.AddEvent(NetLog::TYPE_DNS_TRANSACTION_ATTEMPT, make_scoped_refptr( | |
384 new NetLogSourceParameter("socket_source", socket->NetLog().source()))); | |
385 | |
386 const DnsConfig& config = session_->config(); | |
387 | |
388 int server_index = first_server_index_ + | |
389 (attempt_number % config.nameservers.size()); | |
390 | |
391 DnsUDPAttempt* attempt = new DnsUDPAttempt( | |
392 socket.Pass(), | |
393 config.nameservers[server_index], | |
394 query.Pass(), | |
395 base::Bind(&DnsTransactionImpl::OnAttemptComplete, | |
396 base::Unretained(this), | |
397 attempt_number)); | |
cbentzel
2012/01/13 22:17:25
Nice use of currying here.
| |
398 | |
399 base::TimeDelta timeout = session_->NextTimeout(attempt_number); | |
400 timer_.Start(FROM_HERE, timeout, this, &DnsTransactionImpl::OnTimeout); | |
401 attempts_.push_back(attempt); | |
402 return attempt->Start(); | |
403 } | |
404 | |
405 // Begins query for the current name. Makes the first attempt. | |
406 int StartQuery() { | |
407 std::string dotted_qname = DNSDomainToString(qnames_.front()); | |
408 net_log_.BeginEvent( | |
409 NetLog::TYPE_DNS_TRANSACTION_QUERY, | |
410 make_scoped_refptr(new NetLogStringParameter("qname", dotted_qname))); | |
411 | |
412 first_server_index_ = session_->NextFirstServerIndex(); | |
413 | |
414 STLDeleteElements(&attempts_); | |
415 return MakeAttempt(); | |
416 } | |
417 | |
418 void OnAttemptComplete(int attempt_number, int rv) { | |
cbentzel
2012/01/13 22:17:25
DCHECK that attempt_number within reasonable range
| |
419 timer_.Stop(); | |
420 | |
421 const DnsUDPAttempt* attempt = attempts_[attempt_number]; | |
422 | |
423 if (attempt->response()) { | |
424 net_log_.AddEvent( | |
425 NetLog::TYPE_DNS_TRANSACTION_RESPONSE, | |
426 make_scoped_refptr( | |
427 new ResponseParameters(attempt->response()->rcode(), | |
428 attempt->response()->answer_count(), | |
429 attempt->socket()->NetLog().source()))); | |
430 } | |
431 | |
432 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY, rv); | |
433 | |
434 switch (rv) { | |
435 case ERR_NAME_NOT_RESOLVED: | |
436 // Try next suffix. | |
437 qnames_.pop_front(); | |
438 if (qnames_.empty()) | |
439 rv = ERR_NAME_NOT_RESOLVED; | |
440 else | |
441 rv = StartQuery(); | |
130 break; | 442 break; |
131 case STATE_CONNECT_COMPLETE: | 443 case OK: |
132 rv = DoConnectComplete(rv); | 444 successful_attempt_ = attempt; |
133 break; | |
134 case STATE_SEND_QUERY: | |
135 rv = DoSendQuery(); | |
136 break; | |
137 case STATE_SEND_QUERY_COMPLETE: | |
138 rv = DoSendQueryComplete(rv); | |
139 break; | |
140 case STATE_READ_RESPONSE: | |
141 rv = DoReadResponse(); | |
142 break; | |
143 case STATE_READ_RESPONSE_COMPLETE: | |
144 rv = DoReadResponseComplete(rv); | |
145 break; | 445 break; |
146 default: | 446 default: |
147 NOTREACHED(); | 447 // TODO(szym): Some nameservers could fail and we should just ignore |
cbentzel
2012/01/13 22:17:25
It wouldn't be too hard to handle the ERR_DNS_SERV
| |
448 // them. | |
148 break; | 449 break; |
149 } | 450 } |
150 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | 451 if (rv != ERR_IO_PENDING) |
151 | 452 DoCallback(rv); |
152 return rv; | 453 } |
454 | |
455 void OnTimeout() { | |
456 const DnsConfig& config = session_->config(); | |
457 if (attempts_.size() == config.attempts * config.nameservers.size()) { | |
458 DoCallback(ERR_DNS_TIMED_OUT); | |
459 return; | |
460 } | |
461 int rv = MakeAttempt(); | |
462 if (rv != ERR_IO_PENDING) | |
463 DoCallback(rv); | |
464 } | |
465 | |
466 scoped_refptr<DnsSession> session_; | |
467 std::string hostname_; | |
468 uint16 qtype_; | |
469 // Set to null once the transaction completes. | |
470 DnsTransactionFactory::CallbackType callback_; | |
471 | |
472 BoundNetLog net_log_; | |
473 | |
474 // Search list of fully-qualified DNS names to query next (in DNS format). | |
475 std::deque<std::string> qnames_; | |
476 | |
477 // List of attempts for the current name. | |
478 std::vector<DnsUDPAttempt*> attempts_; | |
479 // The member of |attempts_| that succeeded first. | |
480 const DnsUDPAttempt* successful_attempt_; | |
cbentzel
2012/01/13 22:17:25
|successful_attempt_| doesn't need to be a member
| |
481 | |
482 // Index of the first server to try on each search query. | |
483 int first_server_index_; | |
484 | |
485 base::OneShotTimer<DnsTransactionImpl> timer_; | |
486 | |
487 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl); | |
488 }; | |
489 | |
490 // ---------------------------------------------------------------------------- | |
491 | |
492 // Implementation of DnsTransactionFactory that returns instances of | |
493 // DnsTransactionImpl. | |
494 class DnsTransactionFactoryImpl : public DnsTransactionFactory { | |
495 public: | |
496 explicit DnsTransactionFactoryImpl(DnsSession* session) { | |
497 session_ = session; | |
498 } | |
499 | |
500 virtual scoped_ptr<DnsTransaction> CreateTransaction( | |
501 const std::string& hostname, | |
502 uint16 qtype, | |
503 const CallbackType& callback, | |
504 const BoundNetLog& source_net_log) OVERRIDE { | |
505 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(session_, | |
506 hostname, | |
507 qtype, | |
508 callback, | |
509 source_net_log)); | |
510 } | |
511 | |
512 private: | |
513 scoped_refptr<DnsSession> session_; | |
514 }; | |
515 | |
516 } // namespace | |
517 | |
518 // static | |
519 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory( | |
520 DnsSession* session) { | |
521 return scoped_ptr<DnsTransactionFactory>( | |
522 new DnsTransactionFactoryImpl(session)); | |
153 } | 523 } |
154 | 524 |
155 void DnsTransaction::DoCallback(int result) { | |
156 DCHECK_NE(result, ERR_IO_PENDING); | |
157 int answer_count = (result == OK) ? response()->answer_count() : 0; | |
158 net_log_.EndEvent( | |
159 NetLog::TYPE_DNS_TRANSACTION, | |
160 make_scoped_refptr( | |
161 new DnsTransactionFinishParameters(result, answer_count))); | |
162 callback_.Run(this, result); | |
163 } | |
164 | |
165 void DnsTransaction::OnIOComplete(int result) { | |
166 int rv = DoLoop(result); | |
167 if (rv != ERR_IO_PENDING) | |
168 DoCallback(rv); | |
169 } | |
170 | |
171 int DnsTransaction::DoConnect() { | |
172 next_state_ = STATE_CONNECT_COMPLETE; | |
173 | |
174 StartTimer(session_->NextTimeout(attempts_)); | |
175 ++attempts_; | |
176 | |
177 // TODO(szym): keep all sockets around in case the server responds | |
178 // after its timeout; state machine will need to change to handle that. | |
179 // The current plan is to move socket management out to DnsSession. | |
180 // Hence also move retransmissions to DnsClient::Request. | |
181 socket_.reset(session_->socket_factory()->CreateDatagramClientSocket( | |
182 DatagramSocket::RANDOM_BIND, | |
183 base::Bind(&base::RandInt), | |
184 net_log_.net_log(), | |
185 net_log_.source())); | |
186 | |
187 net_log_.AddEvent( | |
188 NetLog::TYPE_DNS_TRANSACTION_ATTEMPT_STARTED, | |
189 make_scoped_refptr( | |
190 new DnsTransactionRetryParameters(attempts_, | |
191 socket_->NetLog().source()))); | |
192 | |
193 return socket_->Connect(dns_server_); | |
194 } | |
195 | |
196 int DnsTransaction::DoConnectComplete(int rv) { | |
197 if (rv < 0) | |
198 return rv; | |
199 next_state_ = STATE_SEND_QUERY; | |
200 return OK; | |
201 } | |
202 | |
203 int DnsTransaction::DoSendQuery() { | |
204 next_state_ = STATE_SEND_QUERY_COMPLETE; | |
205 return socket_->Write(query_->io_buffer(), | |
206 query_->io_buffer()->size(), | |
207 base::Bind(&DnsTransaction::OnIOComplete, | |
208 base::Unretained(this))); | |
209 } | |
210 | |
211 int DnsTransaction::DoSendQueryComplete(int rv) { | |
212 if (rv < 0) | |
213 return rv; | |
214 | |
215 // Writing to UDP should not result in a partial datagram. | |
216 if (rv != query_->io_buffer()->size()) | |
217 return ERR_MSG_TOO_BIG; | |
218 | |
219 next_state_ = STATE_READ_RESPONSE; | |
220 return OK; | |
221 } | |
222 | |
223 int DnsTransaction::DoReadResponse() { | |
224 next_state_ = STATE_READ_RESPONSE_COMPLETE; | |
225 response_.reset(new DnsResponse()); | |
226 return socket_->Read(response_->io_buffer(), | |
227 response_->io_buffer()->size(), | |
228 base::Bind(&DnsTransaction::OnIOComplete, | |
229 base::Unretained(this))); | |
230 } | |
231 | |
232 int DnsTransaction::DoReadResponseComplete(int rv) { | |
233 DCHECK_NE(ERR_IO_PENDING, rv); | |
234 RevokeTimer(); | |
235 if (rv < 0) | |
236 return rv; | |
237 | |
238 DCHECK(rv); | |
239 if (!response_->InitParse(rv, *query_)) | |
240 return ERR_DNS_MALFORMED_RESPONSE; | |
241 // TODO(szym): define this flag value in dns_protocol | |
242 if (response_->flags1() & 2) | |
243 return ERR_DNS_SERVER_REQUIRES_TCP; | |
244 // TODO(szym): move this handling out of DnsTransaction? | |
245 if (response_->rcode() != dns_protocol::kRcodeNOERROR && | |
246 response_->rcode() != dns_protocol::kRcodeNXDOMAIN) { | |
247 return ERR_DNS_SERVER_FAILED; | |
248 } | |
249 // TODO(szym): add ERR_DNS_RR_NOT_FOUND? | |
250 if (response_->answer_count() == 0) | |
251 return ERR_NAME_NOT_RESOLVED; | |
252 | |
253 return OK; | |
254 } | |
255 | |
256 void DnsTransaction::StartTimer(base::TimeDelta delay) { | |
257 timer_.Start(FROM_HERE, delay, this, &DnsTransaction::OnTimeout); | |
258 } | |
259 | |
260 void DnsTransaction::RevokeTimer() { | |
261 timer_.Stop(); | |
262 } | |
263 | |
264 void DnsTransaction::OnTimeout() { | |
265 DCHECK(next_state_ == STATE_SEND_QUERY_COMPLETE || | |
266 next_state_ == STATE_READ_RESPONSE_COMPLETE); | |
267 if (attempts_ == session_->config().attempts) { | |
268 DoCallback(ERR_DNS_TIMED_OUT); | |
269 return; | |
270 } | |
271 next_state_ = STATE_CONNECT; | |
272 query_.reset(query_->CloneWithNewId(session_->NextId())); | |
273 int rv = DoLoop(OK); | |
274 if (rv != ERR_IO_PENDING) | |
275 DoCallback(rv); | |
276 } | |
277 | |
278 } // namespace net | 525 } // namespace net |
526 | |
OLD | NEW |