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