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 |
91 // ---------------------------------------------------------------------------- | |
92 | |
93 // A single asynchronous DNS exchange over UDP, which consists of sending out a | |
94 // DNS query, waiting for a response, and returning the response that it | |
95 // matches. Logging is done in the socket and in the outer DnsTransaction. | |
96 class DnsUDPAttempt { | |
97 public: | |
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->IsValid()) ? 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_); | |
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) { | |
187 DCHECK_NE(ERR_IO_PENDING, rv); | |
188 if (rv < 0) | |
189 return rv; | |
190 | |
191 // Writing to UDP should not result in a partial datagram. | |
192 if (rv != query_->io_buffer()->size()) | |
193 return ERR_MSG_TOO_BIG; | |
194 | |
195 next_state_ = STATE_READ_RESPONSE; | |
196 return OK; | |
197 } | |
198 | |
199 int DoReadResponse() { | |
200 next_state_ = STATE_READ_RESPONSE_COMPLETE; | |
201 response_.reset(new DnsResponse()); | |
202 return socket_->Read(response_->io_buffer(), | |
203 response_->io_buffer()->size(), | |
204 base::Bind(&DnsUDPAttempt::OnIOComplete, | |
205 base::Unretained(this))); | |
206 } | |
207 | |
208 int DoReadResponseComplete(int rv) { | |
209 DCHECK_NE(ERR_IO_PENDING, rv); | |
210 if (rv < 0) | |
211 return rv; | |
212 | |
213 DCHECK(rv); | |
214 if (!response_->InitParse(rv, *query_)) | |
215 return ERR_DNS_MALFORMED_RESPONSE; | |
216 if (response_->flags() & dns_protocol::kFlagTC) | |
217 return ERR_DNS_SERVER_REQUIRES_TCP; | |
218 if (response_->rcode() != dns_protocol::kRcodeNOERROR && | |
219 response_->rcode() != dns_protocol::kRcodeNXDOMAIN) { | |
220 return ERR_DNS_SERVER_FAILED; | |
221 } | |
222 if (response_->answer_count() == 0) | |
223 return ERR_NAME_NOT_RESOLVED; | |
224 | |
225 return OK; | |
226 } | |
227 | |
228 void OnIOComplete(int rv) { | |
229 rv = DoLoop(rv); | |
230 if (rv != ERR_IO_PENDING) | |
231 callback_.Run(rv); | |
232 } | |
233 | |
234 State next_state_; | |
235 | |
236 scoped_ptr<DatagramClientSocket> socket_; | |
237 IPEndPoint server_; | |
238 scoped_ptr<DnsQuery> query_; | |
239 | |
240 scoped_ptr<DnsResponse> response_; | |
241 | |
242 CompletionCallback callback_; | |
243 | |
244 DISALLOW_COPY_AND_ASSIGN(DnsUDPAttempt); | |
245 }; | |
246 | |
247 // ---------------------------------------------------------------------------- | |
248 | |
249 // Implements DnsTransaction. Configuration is supplied by DnsSession. | |
250 // The suffix list is built according to the DnsConfig from the session. | |
251 // The timeout for each DnsUDPAttempt is given by DnsSession::NextTimeout. | |
252 // The first server to attempt on each query is given by | |
253 // DnsSession::NextFirstServerIndex, and the order is round-robin afterwards. | |
254 // Each server is attempted DnsConfig::attempts times. | |
255 class DnsTransactionImpl : public DnsTransaction, public base::NonThreadSafe { | |
256 public: | |
257 DnsTransactionImpl(DnsSession* session, | |
258 const std::string& hostname, | |
259 uint16 qtype, | |
260 const DnsTransactionFactory::CallbackType& callback, | |
261 const BoundNetLog& source_net_log) | |
262 : session_(session), | |
263 hostname_(hostname), | |
264 qtype_(qtype), | |
265 callback_(callback), | |
266 net_log_(BoundNetLog::Make(session->net_log(), | |
267 NetLog::SOURCE_DNS_TRANSACTION)), | |
268 first_server_index_(0) { | |
269 DCHECK(session_); | |
270 DCHECK(!hostname_.empty()); | |
271 DCHECK(!callback_.is_null()); | |
272 | |
273 DCHECK(!IsIPLiteral(hostname_)); | |
274 | |
275 net_log_.BeginEvent(NetLog::TYPE_DNS_TRANSACTION, make_scoped_refptr( | |
276 new StartParameters(hostname_, qtype_, source_net_log.source()))); | |
277 } | |
278 | |
279 virtual ~DnsTransactionImpl() { | |
280 STLDeleteElements(&attempts_); | |
281 if (!callback_.is_null()) { | |
282 net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL); | |
283 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION, | |
284 ERR_ABORTED); | |
285 } | |
286 } | |
287 | |
288 virtual const std::string& GetHostname() const OVERRIDE { | |
289 DCHECK(CalledOnValidThread()); | |
290 return hostname_; | |
291 } | |
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; | |
mmenke
2012/01/19 17:24:47
nit: "labeled" is preferred in the US, technicall
| |
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, const DnsUDPAttempt* successful_attempt) { | |
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 unsigned 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 unsigned 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)); | |
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(unsigned attempt_number, int rv) { | |
419 DCHECK_LT(attempt_number, attempts_.size()); | |
420 timer_.Stop(); | |
421 | |
422 const DnsUDPAttempt* attempt = attempts_[attempt_number]; | |
423 | |
424 if (attempt->response()) { | |
425 net_log_.AddEvent( | |
426 NetLog::TYPE_DNS_TRANSACTION_RESPONSE, | |
427 make_scoped_refptr( | |
428 new ResponseParameters(attempt->response()->rcode(), | |
429 attempt->response()->answer_count(), | |
430 attempt->socket()->NetLog().source()))); | |
431 } | |
432 | |
433 net_log_.EndEventWithNetErrorCode(NetLog::TYPE_DNS_TRANSACTION_QUERY, rv); | |
434 | |
435 switch (rv) { | |
436 case ERR_NAME_NOT_RESOLVED: | |
437 // Try next suffix. | |
438 qnames_.pop_front(); | |
439 if (qnames_.empty()) | |
440 rv = ERR_NAME_NOT_RESOLVED; | |
441 else | |
442 rv = StartQuery(); | |
443 break; | |
444 case OK: | |
445 DoCallback(rv, attempt); | |
446 return; | |
447 default: | |
448 // TODO(szym): Some nameservers could fail so try the next one. | |
449 const DnsConfig& config = session_->config(); | |
450 if (attempts_.size() < config.attempts * config.nameservers.size()) { | |
451 rv = MakeAttempt(); | |
452 } else { | |
453 // TODO(szym): Should this be different than the timeout case? | |
454 rv = ERR_DNS_SERVER_FAILED; | |
455 } | |
456 break; | |
457 } | |
458 if (rv != ERR_IO_PENDING) | |
459 DoCallback(rv, NULL); | |
460 } | |
461 | |
462 void OnTimeout() { | |
463 const DnsConfig& config = session_->config(); | |
464 if (attempts_.size() == config.attempts * config.nameservers.size()) { | |
465 DoCallback(ERR_DNS_TIMED_OUT, NULL); | |
466 return; | |
467 } | |
468 int rv = MakeAttempt(); | |
469 if (rv != ERR_IO_PENDING) | |
470 DoCallback(rv, NULL); | |
471 } | |
472 | |
473 scoped_refptr<DnsSession> session_; | |
474 std::string hostname_; | |
475 uint16 qtype_; | |
476 // Set to null once the transaction completes. | |
477 DnsTransactionFactory::CallbackType callback_; | |
478 | |
479 BoundNetLog net_log_; | |
480 | |
481 // Search list of fully-qualified DNS names to query next (in DNS format). | |
482 std::deque<std::string> qnames_; | |
483 | |
484 // List of attempts for the current name. | |
485 std::vector<DnsUDPAttempt*> attempts_; | |
486 | |
487 // Index of the first server to try on each search query. | |
488 int first_server_index_; | |
489 | |
490 base::OneShotTimer<DnsTransactionImpl> timer_; | |
491 | |
492 DISALLOW_COPY_AND_ASSIGN(DnsTransactionImpl); | |
493 }; | |
494 | |
495 // ---------------------------------------------------------------------------- | |
496 | |
497 // Implementation of DnsTransactionFactory that returns instances of | |
498 // DnsTransactionImpl. | |
499 class DnsTransactionFactoryImpl : public DnsTransactionFactory { | |
500 public: | |
501 explicit DnsTransactionFactoryImpl(DnsSession* session) { | |
502 session_ = session; | |
503 } | |
504 | |
505 virtual scoped_ptr<DnsTransaction> CreateTransaction( | |
506 const std::string& hostname, | |
507 uint16 qtype, | |
508 const CallbackType& callback, | |
509 const BoundNetLog& source_net_log) OVERRIDE { | |
510 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(session_, | |
511 hostname, | |
512 qtype, | |
513 callback, | |
514 source_net_log)); | |
515 } | |
516 | |
517 private: | |
518 scoped_refptr<DnsSession> session_; | |
519 }; | |
520 | |
88 } // namespace | 521 } // namespace |
89 | 522 |
90 | 523 // static |
91 DnsTransaction::DnsTransaction(DnsSession* session, | 524 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory( |
92 const base::StringPiece& qname, | 525 DnsSession* session) { |
93 uint16 qtype, | 526 return scoped_ptr<DnsTransactionFactory>( |
94 const ResultCallback& callback, | 527 new DnsTransactionFactoryImpl(session)); |
95 const BoundNetLog& source_net_log) | |
96 : session_(session), | |
97 dns_server_(session->NextServer()), | |
98 query_(new DnsQuery(session->NextId(), qname, qtype)), | |
99 callback_(callback), | |
100 attempts_(0), | |
101 next_state_(STATE_NONE), | |
102 net_log_(BoundNetLog::Make(session->net_log(), | |
103 NetLog::SOURCE_DNS_TRANSACTION)) { | |
104 net_log_.BeginEvent( | |
105 NetLog::TYPE_DNS_TRANSACTION, | |
106 make_scoped_refptr( | |
107 new DnsTransactionStartParameters(dns_server_, | |
108 qname, | |
109 qtype, | |
110 source_net_log.source()))); | |
111 } | 528 } |
112 | 529 |
113 DnsTransaction::~DnsTransaction() {} | |
114 | |
115 int DnsTransaction::Start() { | |
116 DCHECK_EQ(STATE_NONE, next_state_); | |
117 next_state_ = STATE_CONNECT; | |
118 return DoLoop(OK); | |
119 } | |
120 | |
121 int DnsTransaction::DoLoop(int result) { | |
122 DCHECK_NE(STATE_NONE, next_state_); | |
123 int rv = result; | |
124 do { | |
125 State state = next_state_; | |
126 next_state_ = STATE_NONE; | |
127 switch (state) { | |
128 case STATE_CONNECT: | |
129 rv = DoConnect(); | |
130 break; | |
131 case STATE_CONNECT_COMPLETE: | |
132 rv = DoConnectComplete(rv); | |
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; | |
146 default: | |
147 NOTREACHED(); | |
148 break; | |
149 } | |
150 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
151 | |
152 return rv; | |
153 } | |
154 | |
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 | 530 } // namespace net |
531 | |
OLD | NEW |