Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(82)

Side by Side Diff: net/dns/dns_transaction.cc

Issue 9190031: DnsClient refactoring + features (timeout, suffix search, server rotation). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Renamed dns_client -> dns_transaction. Created 8 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698