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 |