| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/dns/dns_transaction.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/rand_util.h" | |
| 9 #include "base/values.h" | |
| 10 #include "net/base/io_buffer.h" | |
| 11 #include "net/base/net_errors.h" | |
| 12 #include "net/dns/dns_protocol.h" | |
| 13 #include "net/dns/dns_query.h" | |
| 14 #include "net/dns/dns_response.h" | |
| 15 #include "net/dns/dns_session.h" | |
| 16 #include "net/socket/client_socket_factory.h" | |
| 17 #include "net/udp/datagram_client_socket.h" | |
| 18 | |
| 19 namespace net { | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 class DnsTransactionStartParameters : public NetLog::EventParameters { | |
| 24 public: | |
| 25 DnsTransactionStartParameters(const IPEndPoint& dns_server, | |
| 26 const base::StringPiece& qname, | |
| 27 uint16 qtype, | |
| 28 const NetLog::Source& source) | |
| 29 : dns_server_(dns_server), | |
| 30 qname_(qname.data(), qname.length()), | |
| 31 qtype_(qtype), | |
| 32 source_(source) {} | |
| 33 | |
| 34 virtual Value* ToValue() const { | |
| 35 DictionaryValue* dict = new DictionaryValue(); | |
| 36 dict->SetString("dns_server", dns_server_.ToString()); | |
| 37 dict->SetString("hostname", qname_); | |
| 38 dict->SetInteger("query_type", qtype_); | |
| 39 if (source_.is_valid()) | |
| 40 dict->Set("source_dependency", source_.ToValue()); | |
| 41 return dict; | |
| 42 } | |
| 43 | |
| 44 private: | |
| 45 IPEndPoint dns_server_; | |
| 46 std::string qname_; | |
| 47 uint16 qtype_; | |
| 48 const NetLog::Source source_; | |
| 49 }; | |
| 50 | |
| 51 class DnsTransactionFinishParameters : public NetLog::EventParameters { | |
| 52 public: | |
| 53 // TODO(szym): add rcode ? | |
| 54 DnsTransactionFinishParameters(int net_error, int answer_count) | |
| 55 : net_error_(net_error), answer_count_(answer_count) {} | |
| 56 | |
| 57 virtual Value* ToValue() const { | |
| 58 DictionaryValue* dict = new DictionaryValue(); | |
| 59 if (net_error_) | |
| 60 dict->SetInteger("net_error", net_error_); | |
| 61 dict->SetInteger("answer_count", answer_count_); | |
| 62 return dict; | |
| 63 } | |
| 64 | |
| 65 private: | |
| 66 const int net_error_; | |
| 67 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_; | |
| 86 }; | |
| 87 | |
| 88 } // namespace | |
| 89 | |
| 90 | |
| 91 DnsTransaction::DnsTransaction(DnsSession* session, | |
| 92 const base::StringPiece& qname, | |
| 93 uint16 qtype, | |
| 94 const ResultCallback& callback, | |
| 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 } | |
| 112 | |
| 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 | |
| OLD | NEW |