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

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

Issue 9190031: DnsClient refactoring + features (timeout, suffix search, server rotation). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Responded to review. Renamed DnsClient -> DnsTransactionFactory. Completed logging. 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_client.h" 5 #include "net/dns/dns_client.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"
16 #include "base/rand_util.h"
17 #include "base/stl_util.h"
8 #include "base/string_piece.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"
23 #include "net/base/io_buffer.h"
24 #include "net/base/ip_endpoint.h"
9 #include "net/base/net_errors.h" 25 #include "net/base/net_errors.h"
26 #include "net/base/net_log.h"
27 #include "net/dns/dns_protocol.h"
28 #include "net/dns/dns_query.h"
10 #include "net/dns/dns_response.h" 29 #include "net/dns/dns_response.h"
11 #include "net/dns/dns_session.h" 30 #include "net/dns/dns_session.h"
12 #include "net/dns/dns_transaction.h"
13 #include "net/socket/client_socket_factory.h" 31 #include "net/socket/client_socket_factory.h"
32 #include "net/udp/datagram_client_socket.h"
14 33
15 namespace net { 34 namespace net {
16 35
17 DnsClient::Request::Request(const base::StringPiece& qname, 36 namespace {
18 uint16 qtype, 37
19 const RequestCallback& callback) 38 // Count labels in the fully-qualified name in DNS format.
20 : qname_(qname.data(), qname.size()), 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 {
52 public:
53 StartParameters(const std::string& hostname,
54 uint16 qtype,
55 const NetLog::Source& source)
56 : hostname_(hostname), qtype_(qtype), source_(source) {}
57
58 virtual Value* ToValue() const OVERRIDE {
59 DictionaryValue* dict = new DictionaryValue();
60 dict->SetString("hostname", hostname_);
61 dict->SetInteger("query_type", qtype_);
62 dict->Set("source_dependency", source_.ToValue());
63 return dict;
64 }
65
66 private:
67 const std::string hostname_;
68 const uint16 qtype_;
69 const NetLog::Source source_;
70 };
71
72 class ResponseParameters : public NetLog::EventParameters {
73 public:
74 ResponseParameters(int rcode, int answer_count, const NetLog::Source& source)
75 : rcode_(rcode), answer_count_(answer_count), source_(source) {}
76
77 virtual Value* ToValue() const OVERRIDE {
78 DictionaryValue* dict = new DictionaryValue();
79 dict->SetInteger("rcode", rcode_);
80 dict->SetInteger("answer_count", answer_count_);
81 dict->Set("socket_source", source_.ToValue());
82 return dict;
83 }
84
85 private:
86 const int rcode_;
87 const int answer_count_;
88 const NetLog::Source source_;
89 };
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->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_);
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 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)
261 : session_(session),
262 hostname_(hostname),
21 qtype_(qtype), 263 qtype_(qtype),
22 callback_(callback) { 264 callback_(callback),
23 } 265 net_log_(BoundNetLog::Make(session->net_log(),
24 266 NetLog::SOURCE_DNS_TRANSACTION)),
25 DnsClient::Request::~Request() {} 267 successful_attempt_(NULL),
26 268 first_server_index_(0) {
27 // Implementation of DnsClient that uses DnsTransaction to serve requests. 269 DCHECK(session_);
28 class DnsClientImpl : public DnsClient { 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;
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));
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) {
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();
442 break;
443 case OK:
444 successful_attempt_ = attempt;
445 break;
446 default:
447 // TODO(szym): Some nameservers could fail and we should just ignore
448 // them.
449 break;
450 }
451 if (rv != ERR_IO_PENDING)
452 DoCallback(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_;
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 {
29 public: 495 public:
30 class RequestImpl : public Request { 496 explicit DnsTransactionFactoryImpl(DnsSession* session) {
31 public:
32 RequestImpl(const base::StringPiece& qname,
33 uint16 qtype,
34 const RequestCallback& callback,
35 DnsSession* session,
36 const BoundNetLog& net_log)
37 : Request(qname, qtype, callback),
38 session_(session),
39 net_log_(net_log) {
40 }
41
42 virtual int Start() OVERRIDE {
43 transaction_.reset(new DnsTransaction(
44 session_,
45 qname(),
46 qtype(),
47 base::Bind(&RequestImpl::OnComplete, base::Unretained(this)),
48 net_log_));
49 return transaction_->Start();
50 }
51
52 void OnComplete(DnsTransaction* transaction, int rv) {
53 DCHECK_EQ(transaction_.get(), transaction);
54 // TODO(szym):
55 // - handle retransmissions here instead of DnsTransaction
56 // - handle rcode and flags here instead of DnsTransaction
57 // - update RTT in DnsSession
58 // - perform suffix search
59 // - handle DNS over TCP
60 DoCallback(rv, (rv == OK) ? transaction->response() : NULL);
61 }
62
63 private:
64 scoped_refptr<DnsSession> session_;
65 BoundNetLog net_log_;
66 scoped_ptr<DnsTransaction> transaction_;
67 };
68
69 explicit DnsClientImpl(DnsSession* session) {
70 session_ = session; 497 session_ = session;
71 } 498 }
72 499
73 virtual Request* CreateRequest( 500 virtual scoped_ptr<DnsTransaction> CreateTransaction(
74 const base::StringPiece& qname, 501 const std::string& hostname,
75 uint16 qtype, 502 uint16 qtype,
76 const RequestCallback& callback, 503 const CallbackType& callback,
77 const BoundNetLog& source_net_log) OVERRIDE { 504 const BoundNetLog& source_net_log) OVERRIDE {
78 return new RequestImpl(qname, qtype, callback, session_, source_net_log); 505 return scoped_ptr<DnsTransaction>(new DnsTransactionImpl(session_,
506 hostname,
507 qtype,
508 callback,
509 source_net_log));
79 } 510 }
80 511
81 private: 512 private:
82 scoped_refptr<DnsSession> session_; 513 scoped_refptr<DnsSession> session_;
83 }; 514 };
84 515
516 } // namespace
517
85 // static 518 // static
86 DnsClient* DnsClient::CreateClient(DnsSession* session) { 519 scoped_ptr<DnsTransactionFactory> DnsTransactionFactory::CreateFactory(
87 return new DnsClientImpl(session); 520 DnsSession* session) {
521 return scoped_ptr<DnsTransactionFactory>(
522 new DnsTransactionFactoryImpl(session));
88 } 523 }
89 524
90 } // namespace net 525 } // namespace net
91 526
OLDNEW
« net/dns/dns_client.h ('K') | « net/dns/dns_client.h ('k') | net/dns/dns_client_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698