OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2010 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/base/dnsrr_resolver.h" | |
6 | |
7 #if defined(OS_LINUX) | |
8 #include <resolv.h> | |
9 #endif | |
10 | |
11 #include "base/string_piece.h" | |
12 #include "base/task.h" | |
13 #include "base/worker_pool.h" | |
14 #include "net/base/dns_reload_timer.h" | |
15 #include "net/base/dns_util.h" | |
16 #include "net/base/net_errors.h" | |
17 | |
18 namespace net { | |
19 | |
20 static const uint16 kClassIN = 1; | |
21 | |
22 #if defined(OS_LINUX) | |
23 | |
24 // A Buffer is used for walking over a DNS packet. | |
25 class Buffer { | |
26 public: | |
27 Buffer(const uint8* p, unsigned len) | |
28 : p_(p), | |
29 packet_(p), | |
30 len_(len), | |
31 packet_len_(len) { | |
32 } | |
33 | |
34 bool U8(uint8* v) { | |
35 if (len_ < 1) | |
36 return false; | |
37 *v = *p_; | |
38 p_++; | |
39 len_--; | |
40 return true; | |
41 } | |
42 | |
43 bool U16(uint16* v) { | |
44 if (len_ < 2) | |
45 return false; | |
46 *v = static_cast<uint16>(p_[0]) << 8 | | |
47 static_cast<uint16>(p_[1]); | |
48 p_ += 2; | |
49 len_ -= 2; | |
50 return true; | |
51 } | |
52 | |
53 bool U32(uint32* v) { | |
54 if (len_ < 4) | |
55 return false; | |
56 *v = static_cast<uint32>(p_[0]) << 24 | | |
57 static_cast<uint32>(p_[1]) << 16 | | |
58 static_cast<uint32>(p_[2]) << 8 | | |
59 static_cast<uint32>(p_[3]); | |
60 p_ += 4; | |
61 len_ -= 4; | |
62 return true; | |
63 } | |
64 | |
65 bool Skip(unsigned n) { | |
66 if (len_ < n) | |
67 return false; | |
68 p_ += n; | |
69 len_ -= n; | |
70 return true; | |
71 } | |
72 | |
73 bool Block(base::StringPiece* out, unsigned len) { | |
74 if (len_ < len) | |
75 return false; | |
76 *out = base::StringPiece(reinterpret_cast<const char*>(p_), len); | |
77 p_ += len; | |
78 len_ -= len; | |
79 return true; | |
80 } | |
81 | |
82 // DNSName parses a (possibly compressed) DNS name from the packet. If |name| | |
83 // is not NULL, then the name is written into it. See RFC 1035 section 4.1.4. | |
84 bool DNSName(std::string* name) { | |
85 unsigned jumps = 0; | |
86 const uint8* p = p_; | |
87 unsigned len = len_; | |
88 | |
89 if (name) | |
90 name->clear(); | |
91 | |
92 for (;;) { | |
93 if (len < 1) | |
94 return false; | |
95 uint8 d = *p; | |
Mike Belshe
2010/08/02 14:18:28
nit: I hate all these 1 char variable names!
| |
96 p++; | |
97 len--; | |
98 | |
99 if ((d & 0xc0) == 0xc0) { | |
Mike Belshe
2010/08/02 14:18:28
I'm sure 0xc0 is a mask of some sort; for those no
| |
100 if (jumps > 100) | |
Mike Belshe
2010/08/02 14:18:28
100 is a hardcoded limit for ???
| |
101 return false; | |
102 if (len < 1) | |
103 return false; | |
104 uint16 offset = static_cast<uint16>(d) << 8 | | |
105 static_cast<uint16>(p[0]); | |
106 offset &= 0x3ff; | |
107 p++; | |
108 len--; | |
109 | |
110 if (jumps == 0) { | |
111 p_ = p; | |
112 len_ = len; | |
113 } | |
114 jumps++; | |
115 | |
116 if (offset >= packet_len_) | |
117 return false; | |
118 p = &packet_[offset]; | |
119 } else if ((d & 0xc0) == 0) { | |
120 uint8 label_len = d; | |
121 if (len < label_len) | |
122 return false; | |
123 if (name && label_len) { | |
124 if (!name->empty()) | |
125 name->append("."); | |
126 name->append(reinterpret_cast<const char*>(p), label_len); | |
127 } | |
128 p += label_len; | |
129 len -= label_len; | |
130 | |
131 if (jumps == 0) { | |
132 p_ = p; | |
133 len_ = len; | |
134 } | |
135 | |
136 if (label_len == 0) | |
137 break; | |
138 } else { | |
139 return false; | |
140 } | |
141 } | |
142 | |
143 return true; | |
144 } | |
145 | |
146 private: | |
147 const uint8* p_; | |
148 const uint8* const packet_; | |
149 unsigned len_; | |
150 const unsigned packet_len_; | |
151 }; | |
152 | |
153 bool DnsRRResolver::Response::ParseFromResponse(const uint8* p, unsigned len, | |
154 uint16 rrtype_requested) { | |
155 name.clear(); | |
156 ttl = 0; | |
157 dnssec = false; | |
158 rrdatas.clear(); | |
159 signatures.clear(); | |
160 | |
161 // RFC 1035 section 4.4.1 | |
162 uint8 flags2; | |
163 Buffer buf(p, len); | |
164 if (!buf.Skip(2) || // skip id | |
165 !buf.Skip(1) || // skip first flags byte | |
166 !buf.U8(&flags2)) { | |
167 return false; | |
168 } | |
169 | |
170 // Bit 5 is the Authenticated Data (AD) bit. See | |
171 // http://tools.ietf.org/html/rfc2535#section-6.1 | |
172 if (flags2 & 32) { | |
173 // AD flag is set. We'll trust it if it came from a local nameserver. | |
174 if (_res.nscount == 1 && | |
175 memcmp(&_res.nsaddr_list[0].sin_addr, | |
176 "\x7f\x00\x00\x01" /* 127.0.0.1 */, 4) == 0) { | |
Mike Belshe
2010/08/02 14:18:28
Is there any ipv6 issue here? Maybe a TODO
| |
177 dnssec = true; | |
178 } | |
179 } | |
180 | |
181 uint16 query_count, answer_count, authority_count, additional_count; | |
182 if (!buf.U16(&query_count) || | |
183 !buf.U16(&answer_count) || | |
184 !buf.U16(&authority_count) || | |
185 !buf.U16(&additional_count)) { | |
186 return false; | |
187 } | |
188 | |
189 if (query_count != 1) | |
190 return false; | |
191 | |
192 uint16 type, klass; | |
193 if (!buf.DNSName(NULL) || | |
194 !buf.U16(&type) || | |
195 !buf.U16(&klass) || | |
196 type != rrtype_requested || | |
197 klass != kClassIN) { | |
198 return false; | |
199 } | |
200 | |
201 if (answer_count < 1) | |
202 return false; | |
203 | |
204 for (uint32 i = 0; i < answer_count; i++) { | |
205 std::string* name = NULL; | |
206 if (i == 0) | |
207 name = &this->name; | |
208 uint32 ttl; | |
209 uint16 rrdata_len; | |
210 if (!buf.DNSName(name) || | |
211 !buf.U16(&type) || | |
212 !buf.U16(&klass) || | |
213 !buf.U32(&ttl) || | |
214 !buf.U16(&rrdata_len)) { | |
215 return false; | |
216 } | |
217 | |
218 base::StringPiece rrdata; | |
219 if (!buf.Block(&rrdata, rrdata_len)) | |
220 return false; | |
221 | |
222 if (klass == kClassIN && type == rrtype_requested) { | |
223 if (i == 0) | |
224 this->ttl = ttl; | |
225 rrdatas.push_back(std::string(rrdata.data(), rrdata.size())); | |
226 } else if (klass == kClassIN && type == kDNS_RRSIG) { | |
227 signatures.push_back(std::string(rrdata.data(), rrdata.size())); | |
228 } | |
229 } | |
230 | |
231 return true; | |
232 } | |
233 | |
234 class ResolveTask : public Task { | |
235 public: | |
236 ResolveTask(const std::string& name, uint16 rrtype, | |
237 uint16 flags, CompletionCallback* callback, | |
238 DnsRRResolver::Response* response) | |
239 : name_(name), | |
240 rrtype_(rrtype), | |
241 flags_(flags), | |
242 callback_(callback), | |
243 response_(response) { | |
244 } | |
245 | |
246 virtual void Run() { | |
247 // Runs on a worker thread. | |
248 | |
249 if ((_res.options & RES_INIT) == 0) { | |
250 if (res_ninit(&_res) != 0) | |
251 return Failure(); | |
252 } | |
253 | |
254 unsigned long saved_options = _res.options; | |
255 bool r = Do(); | |
256 if (!r && DnsReloadTimerHasExpired()) { | |
257 res_nclose(&_res); | |
258 if (res_ninit(&_res) == 0) | |
259 r = Do(); | |
260 } | |
261 _res.options = saved_options; | |
262 int error = r ? OK : ERR_NAME_NOT_RESOLVED; | |
263 callback_->Run(error); | |
264 } | |
265 | |
266 bool Do() { | |
267 // For DNSSEC, a 4K buffer is suggested | |
268 static const unsigned kMaxDNSPayload = 4096; | |
269 | |
270 // We set the options explicitly. Note that this removes several default | |
271 // options: RES_DEFNAMES and RES_DNSRCH (see res_init(3)). | |
272 _res.options = RES_INIT | RES_RECURSE | RES_USE_EDNS0 | RES_USE_DNSSEC; | |
273 uint8 answer[kMaxDNSPayload]; | |
274 int len = res_search(name_.c_str(), kClassIN, rrtype_, answer, | |
275 sizeof(answer)); | |
276 if (len == -1) | |
277 return false; | |
278 | |
279 return response_->ParseFromResponse(answer, len, rrtype_); | |
280 } | |
281 | |
282 private: | |
283 void Failure() { | |
284 callback_->Run(ERR_NAME_NOT_RESOLVED); | |
285 } | |
286 | |
287 const std::string name_; | |
288 const uint16 rrtype_; | |
289 const uint16 flags_; | |
290 CompletionCallback* const callback_; | |
291 DnsRRResolver::Response* const response_; | |
292 }; | |
293 #else // OS_LINUX | |
294 // On non-Linux platforms we fail everything for now. | |
295 class ResolveTask { | |
296 public: | |
297 ResolveTask(const std::string& name, uint16 rrtype, | |
298 uint16 flags, CompletionCallback* callback, | |
299 Response* response) { | |
300 } | |
301 | |
302 virtual void Run() { | |
303 callback_->Run(ERR_NAME_NOT_RESOLVED); | |
304 } | |
305 }; | |
306 #endif | |
307 | |
308 // static | |
309 bool DnsRRResolver::Resolve(const std::string& name, uint16 rrtype, | |
310 uint16 flags, CompletionCallback* callback, | |
311 Response* response) { | |
312 if (!callback || !response || name.empty()) | |
313 return false; | |
314 | |
315 // Don't allow queries of type ANY | |
316 if (rrtype == kDNS_ANY) | |
317 return false; | |
318 | |
319 ResolveTask* task = new ResolveTask(name, rrtype, flags, callback, response); | |
320 | |
321 return WorkerPool::PostTask(FROM_HERE, task, true /* task is slow */); | |
322 } | |
323 | |
324 } // namespace net | |
OLD | NEW |