dnsrr_resolver.cc revision dd0e069b1c2e5079f99024c4d54c7d06ef81d11b
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_POSIX)
8#include <resolv.h>
9#endif
10
11#include "base/message_loop.h"
12#include "base/scoped_ptr.h"
13#include "base/string_piece.h"
14#include "base/task.h"
15#include "base/worker_pool.h"
16#include "net/base/dns_reload_timer.h"
17#include "net/base/dns_util.h"
18#include "net/base/net_errors.h"
19
20namespace net {
21
22static const uint16 kClassIN = 1;
23
24namespace {
25
26class CompletionCallbackTask : public Task,
27                               public MessageLoop::DestructionObserver {
28 public:
29  explicit CompletionCallbackTask(CompletionCallback* callback,
30                                  MessageLoop* ml)
31      : callback_(callback),
32        rv_(OK),
33        posted_(false),
34        message_loop_(ml) {
35    ml->AddDestructionObserver(this);
36  }
37
38  void set_rv(int rv) {
39    rv_ = rv;
40  }
41
42  void Post() {
43    AutoLock locked(lock_);
44    DCHECK(!posted_);
45
46    if (!message_loop_) {
47      // MessageLoop got deleted, nothing to do.
48      delete this;
49      return;
50    }
51    posted_ = true;
52    message_loop_->PostTask(FROM_HERE, this);
53  }
54
55  // Task interface
56  void Run() {
57    message_loop_->RemoveDestructionObserver(this);
58    callback_->Run(rv_);
59    // We will be deleted by the message loop.
60  }
61
62  // DestructionObserver interface
63  virtual void WillDestroyCurrentMessageLoop() {
64    AutoLock locked(lock_);
65    message_loop_ = NULL;
66  }
67
68 private:
69  DISALLOW_COPY_AND_ASSIGN(CompletionCallbackTask);
70
71  CompletionCallback* callback_;
72  int rv_;
73  bool posted_;
74
75  Lock lock_;  // covers |message_loop_|
76  MessageLoop* message_loop_;
77};
78
79#if defined(OS_POSIX) && !defined(ANDROID)
80class ResolveTask : public Task {
81 public:
82  ResolveTask(const std::string& name, uint16 rrtype,
83              uint16 flags, CompletionCallback* callback,
84              RRResponse* response, MessageLoop* ml)
85      : name_(name),
86        rrtype_(rrtype),
87        flags_(flags),
88        subtask_(new CompletionCallbackTask(callback, ml)),
89        response_(response) {
90  }
91
92  virtual void Run() {
93    // Runs on a worker thread.
94
95    bool r = true;
96    if ((_res.options & RES_INIT) == 0) {
97      if (res_ninit(&_res) != 0)
98        r = false;
99    }
100
101    if (r) {
102      unsigned long saved_options = _res.options;
103      r = Do();
104
105#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
106      if (!r && DnsReloadTimerHasExpired()) {
107        res_nclose(&_res);
108        if (res_ninit(&_res) == 0)
109          r = Do();
110      }
111#endif
112      _res.options = saved_options;
113    }
114    int error = r ? OK : ERR_NAME_NOT_RESOLVED;
115
116    subtask_->set_rv(error);
117    subtask_.release()->Post();
118  }
119
120  bool Do() {
121    // For DNSSEC, a 4K buffer is suggested
122    static const unsigned kMaxDNSPayload = 4096;
123
124#ifndef RES_USE_DNSSEC
125    // Some versions of libresolv don't have support for the DO bit. In this
126    // case, we proceed without it.
127    static const int RES_USE_DNSSEC = 0;
128#endif
129
130#ifndef RES_USE_EDNS0
131    // Some versions of glibc are so old that they don't support EDNS0 either.
132    // http://code.google.com/p/chromium/issues/detail?id=51676
133    static const int RES_USE_EDNS0 = 0;
134#endif
135
136    // We set the options explicitly. Note that this removes several default
137    // options: RES_DEFNAMES and RES_DNSRCH (see res_init(3)).
138    _res.options = RES_INIT | RES_RECURSE | RES_USE_EDNS0 | RES_USE_DNSSEC;
139    uint8 answer[kMaxDNSPayload];
140    int len = res_search(name_.c_str(), kClassIN, rrtype_, answer,
141                         sizeof(answer));
142    if (len == -1)
143      return false;
144
145    return response_->ParseFromResponse(answer, len, rrtype_);
146  }
147
148 private:
149  DISALLOW_COPY_AND_ASSIGN(ResolveTask);
150
151  const std::string name_;
152  const uint16 rrtype_;
153  const uint16 flags_;
154  scoped_ptr<CompletionCallbackTask> subtask_;
155  RRResponse* const response_;
156};
157#else  // OS_POSIX
158// On non-Linux platforms we fail everything for now.
159class ResolveTask : public Task {
160 public:
161  ResolveTask(const std::string& name, uint16 rrtype,
162              uint16 flags, CompletionCallback* callback,
163              RRResponse* response, MessageLoop* ml)
164      : subtask_(new CompletionCallbackTask(callback, ml)) {
165  }
166
167  virtual void Run() {
168    subtask_->set_rv(ERR_NAME_NOT_RESOLVED);
169    subtask_->Post();
170  }
171
172 private:
173  CompletionCallbackTask* const subtask_;
174  DISALLOW_COPY_AND_ASSIGN(ResolveTask);
175};
176#endif
177
178// A Buffer is used for walking over a DNS packet.
179class Buffer {
180 public:
181  Buffer(const uint8* p, unsigned len)
182      : p_(p),
183        packet_(p),
184        len_(len),
185        packet_len_(len) {
186  }
187
188  bool U8(uint8* v) {
189    if (len_ < 1)
190      return false;
191    *v = *p_;
192    p_++;
193    len_--;
194    return true;
195  }
196
197  bool U16(uint16* v) {
198    if (len_ < 2)
199      return false;
200    *v = static_cast<uint16>(p_[0]) << 8 |
201         static_cast<uint16>(p_[1]);
202    p_ += 2;
203    len_ -= 2;
204    return true;
205  }
206
207  bool U32(uint32* v) {
208    if (len_ < 4)
209      return false;
210    *v = static_cast<uint32>(p_[0]) << 24 |
211         static_cast<uint32>(p_[1]) << 16 |
212         static_cast<uint32>(p_[2]) << 8 |
213         static_cast<uint32>(p_[3]);
214    p_ += 4;
215    len_ -= 4;
216    return true;
217  }
218
219  bool Skip(unsigned n) {
220    if (len_ < n)
221      return false;
222    p_ += n;
223    len_ -= n;
224    return true;
225  }
226
227  bool Block(base::StringPiece* out, unsigned len) {
228    if (len_ < len)
229      return false;
230    *out = base::StringPiece(reinterpret_cast<const char*>(p_), len);
231    p_ += len;
232    len_ -= len;
233    return true;
234  }
235
236  // DNSName parses a (possibly compressed) DNS name from the packet. If |name|
237  // is not NULL, then the name is written into it. See RFC 1035 section 4.1.4.
238  bool DNSName(std::string* name) {
239    unsigned jumps = 0;
240    const uint8* p = p_;
241    unsigned len = len_;
242
243    if (name)
244      name->clear();
245
246    for (;;) {
247      if (len < 1)
248        return false;
249      uint8 d = *p;
250      p++;
251      len--;
252
253      // The two couple of bits of the length give the type of the length. It's
254      // either a direct length or a pointer to the remainder of the name.
255      if ((d & 0xc0) == 0xc0) {
256        // This limit matches the depth limit in djbdns.
257        if (jumps > 100)
258          return false;
259        if (len < 1)
260          return false;
261        uint16 offset = static_cast<uint16>(d) << 8 |
262                        static_cast<uint16>(p[0]);
263        offset &= 0x3ff;
264        p++;
265        len--;
266
267        if (jumps == 0) {
268          p_ = p;
269          len_ = len;
270        }
271        jumps++;
272
273        if (offset >= packet_len_)
274          return false;
275        p = &packet_[offset];
276      } else if ((d & 0xc0) == 0) {
277        uint8 label_len = d;
278        if (len < label_len)
279          return false;
280        if (name && label_len) {
281          if (!name->empty())
282            name->append(".");
283          name->append(reinterpret_cast<const char*>(p), label_len);
284        }
285        p += label_len;
286        len -= label_len;
287
288        if (jumps == 0) {
289          p_ = p;
290          len_ = len;
291        }
292
293        if (label_len == 0)
294          break;
295      } else {
296        return false;
297      }
298    }
299
300    return true;
301  }
302
303 private:
304  DISALLOW_COPY_AND_ASSIGN(Buffer);
305
306  const uint8* p_;
307  const uint8* const packet_;
308  unsigned len_;
309  const unsigned packet_len_;
310};
311
312}  // anonymous namespace
313
314bool RRResponse::ParseFromResponse(const uint8* p, unsigned len,
315                                   uint16 rrtype_requested) {
316#if defined(OS_POSIX) && !defined(ANDROID)
317  name.clear();
318  ttl = 0;
319  dnssec = false;
320  rrdatas.clear();
321  signatures.clear();
322
323  // RFC 1035 section 4.4.1
324  uint8 flags2;
325  Buffer buf(p, len);
326  if (!buf.Skip(2) ||  // skip id
327      !buf.Skip(1) ||  // skip first flags byte
328      !buf.U8(&flags2)) {
329    return false;
330  }
331
332  // Bit 5 is the Authenticated Data (AD) bit. See
333  // http://tools.ietf.org/html/rfc2535#section-6.1
334  if (flags2 & 32) {
335    // AD flag is set. We'll trust it if it came from a local nameserver.
336    // Currently the resolv structure is IPv4 only, so we can't test for IPv6
337    // loopback addresses.
338    if (_res.nscount == 1 &&
339        memcmp(&_res.nsaddr_list[0].sin_addr,
340               "\x7f\x00\x00\x01" /* 127.0.0.1 */, 4) == 0) {
341      dnssec = true;
342    }
343  }
344
345  uint16 query_count, answer_count, authority_count, additional_count;
346  if (!buf.U16(&query_count) ||
347      !buf.U16(&answer_count) ||
348      !buf.U16(&authority_count) ||
349      !buf.U16(&additional_count)) {
350    return false;
351  }
352
353  if (query_count != 1)
354    return false;
355
356  uint16 type, klass;
357  if (!buf.DNSName(NULL) ||
358      !buf.U16(&type) ||
359      !buf.U16(&klass) ||
360      type != rrtype_requested ||
361      klass != kClassIN) {
362    return false;
363  }
364
365  if (answer_count < 1)
366    return false;
367
368  for (uint32 i = 0; i < answer_count; i++) {
369    std::string* name = NULL;
370    if (i == 0)
371      name = &this->name;
372    uint32 ttl;
373    uint16 rrdata_len;
374    if (!buf.DNSName(name) ||
375        !buf.U16(&type) ||
376        !buf.U16(&klass) ||
377        !buf.U32(&ttl) ||
378        !buf.U16(&rrdata_len)) {
379      return false;
380    }
381
382    base::StringPiece rrdata;
383    if (!buf.Block(&rrdata, rrdata_len))
384      return false;
385
386    if (klass == kClassIN && type == rrtype_requested) {
387      if (i == 0)
388        this->ttl = ttl;
389      rrdatas.push_back(std::string(rrdata.data(), rrdata.size()));
390    } else if (klass == kClassIN && type == kDNS_RRSIG) {
391      signatures.push_back(std::string(rrdata.data(), rrdata.size()));
392    }
393  }
394#endif  // defined(OS_POSIX)
395
396  return true;
397}
398
399
400// static
401bool DnsRRResolver::Resolve(const std::string& name, uint16 rrtype,
402                            uint16 flags, CompletionCallback* callback,
403                            RRResponse* response) {
404  if (!callback || !response || name.empty())
405    return false;
406
407  // Don't allow queries of type ANY
408  if (rrtype == kDNS_ANY)
409    return false;
410
411  ResolveTask* task = new ResolveTask(name, rrtype, flags, callback, response,
412                                      MessageLoop::current());
413
414  return WorkerPool::PostTask(FROM_HERE, task, true /* task is slow */);
415}
416
417}  // namespace net
418