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

Side by Side Diff: net/base/dnsrr_resolver.cc

Issue 3029035: net: add DnsRRResovler to fetch arbitary DNS resource types. (Closed)
Patch Set: Created 10 years, 4 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
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698