Home | History | Annotate | Download | only in dns_fuzz_stub
      1 // Copyright (c) 2012 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 <algorithm>
      6 #include <sstream>
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/basictypes.h"
     11 #include "base/files/file_path.h"
     12 #include "base/files/file_util.h"
     13 #include "base/json/json_reader.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/time/time.h"
     16 #include "base/values.h"
     17 #include "net/base/address_list.h"
     18 #include "net/base/dns_util.h"
     19 #include "net/base/io_buffer.h"
     20 #include "net/base/ip_endpoint.h"
     21 #include "net/dns/dns_protocol.h"
     22 #include "net/dns/dns_query.h"
     23 #include "net/dns/dns_response.h"
     24 
     25 namespace {
     26 
     27 void CrashDoubleFree(void) {
     28   // Cause ASAN to detect a double-free
     29   void *p = malloc(1);
     30   LOG(INFO) << "Allocated p=" << p << ".  Double-freeing...";
     31   free(p);
     32   free(p);
     33 }
     34 
     35 void CrashNullPointerDereference(void) {
     36   // Cause the program to segfault with a NULL pointer dereference
     37   int *p = NULL;
     38   *p = 0;
     39 }
     40 
     41 bool FitsUint8(int num) {
     42   return (num >= 0) && (num <= kuint8max);
     43 }
     44 
     45 bool FitsUint16(int num) {
     46   return (num >= 0) && (num <= kuint16max);
     47 }
     48 
     49 bool ReadTestCase(const char* filename,
     50                   uint16* id, std::string* qname, uint16* qtype,
     51                   std::vector<char>* resp_buf,
     52                   bool* crash_test) {
     53   base::FilePath filepath = base::FilePath::FromUTF8Unsafe(filename);
     54 
     55   std::string json;
     56   if (!base::ReadFileToString(filepath, &json)) {
     57     LOG(ERROR) << filename << ": couldn't read file.";
     58     return false;
     59   }
     60 
     61   scoped_ptr<base::Value> value(base::JSONReader::Read(json));
     62   if (!value.get()) {
     63     LOG(ERROR) << filename << ": couldn't parse JSON.";
     64     return false;
     65   }
     66 
     67   base::DictionaryValue* dict;
     68   if (!value->GetAsDictionary(&dict)) {
     69     LOG(ERROR) << filename << ": test case is not a dictionary.";
     70     return false;
     71   }
     72 
     73   *crash_test = dict->HasKey("crash_test");
     74   if (*crash_test) {
     75     LOG(INFO) << filename << ": crash_test is set!";
     76     return true;
     77   }
     78 
     79   int id_int;
     80   if (!dict->GetInteger("id", &id_int)) {
     81     LOG(ERROR) << filename << ": id is missing or not an integer.";
     82     return false;
     83   }
     84   if (!FitsUint16(id_int)) {
     85     LOG(ERROR) << filename << ": id is out of range.";
     86     return false;
     87   }
     88   *id = static_cast<uint16>(id_int);
     89 
     90   if (!dict->GetStringASCII("qname", qname)) {
     91     LOG(ERROR) << filename << ": qname is missing or not a string.";
     92     return false;
     93   }
     94 
     95   int qtype_int;
     96   if (!dict->GetInteger("qtype", &qtype_int)) {
     97     LOG(ERROR) << filename << ": qtype is missing or not an integer.";
     98     return false;
     99   }
    100   if (!FitsUint16(qtype_int)) {
    101     LOG(ERROR) << filename << ": qtype is out of range.";
    102     return false;
    103   }
    104   *qtype = static_cast<uint16>(qtype_int);
    105 
    106   base::ListValue* resp_list;
    107   if (!dict->GetList("response", &resp_list)) {
    108     LOG(ERROR) << filename << ": response is missing or not a list.";
    109     return false;
    110   }
    111 
    112   size_t resp_size = resp_list->GetSize();
    113   resp_buf->clear();
    114   resp_buf->reserve(resp_size);
    115   for (size_t i = 0; i < resp_size; i++) {
    116     int resp_byte_int;
    117     if ((!resp_list->GetInteger(i, &resp_byte_int))) {
    118       LOG(ERROR) << filename << ": response[" << i << "] is not an integer.";
    119       return false;
    120     }
    121     if (!FitsUint8(resp_byte_int)) {
    122       LOG(ERROR) << filename << ": response[" << i << "] is out of range.";
    123       return false;
    124     }
    125     resp_buf->push_back(static_cast<char>(resp_byte_int));
    126   }
    127   DCHECK(resp_buf->size() == resp_size);
    128 
    129   LOG(INFO) << "Query: id=" << id_int << ", "
    130             << "qname=" << *qname << ", "
    131             << "qtype=" << qtype_int << ", "
    132             << "resp_size=" << resp_size;
    133 
    134   return true;
    135 }
    136 
    137 void RunTestCase(uint16 id, std::string& qname, uint16 qtype,
    138                  std::vector<char>& resp_buf) {
    139   net::DnsQuery query(id, qname, qtype);
    140   net::DnsResponse response;
    141   std::copy(resp_buf.begin(), resp_buf.end(), response.io_buffer()->data());
    142 
    143   if (!response.InitParse(resp_buf.size(), query)) {
    144     LOG(INFO) << "InitParse failed.";
    145     return;
    146   }
    147 
    148   net::AddressList address_list;
    149   base::TimeDelta ttl;
    150   net::DnsResponse::Result result = response.ParseToAddressList(
    151       &address_list, &ttl);
    152   if (result != net::DnsResponse::DNS_PARSE_OK) {
    153     LOG(INFO) << "ParseToAddressList failed: " << result;
    154     return;
    155   }
    156 
    157   // Print the response in one compact line.
    158   std::stringstream result_line;
    159   result_line << "Response: address_list={ ";
    160   for (unsigned int i = 0; i < address_list.size(); i++)
    161     result_line << address_list[i].ToString() << " ";
    162   result_line << "}, ttl=" << ttl.InSeconds() << "s";
    163 
    164   LOG(INFO) << result_line.str();
    165 }
    166 
    167 bool ReadAndRunTestCase(const char* filename) {
    168   uint16 id = 0;
    169   std::string qname;
    170   uint16 qtype = 0;
    171   std::vector<char> resp_buf;
    172   bool crash_test = false;
    173 
    174   LOG(INFO) << "Test case: " << filename;
    175 
    176   // ReadTestCase will print a useful error message if it fails.
    177   if (!ReadTestCase(filename, &id, &qname, &qtype, &resp_buf, &crash_test))
    178     return false;
    179 
    180   if (crash_test) {
    181     LOG(INFO) << "Crashing.";
    182     CrashDoubleFree();
    183     // if we're not running under ASAN, that might not have worked
    184     CrashNullPointerDereference();
    185     NOTREACHED();
    186     return true;
    187   }
    188 
    189   std::string qname_dns;
    190   if (!net::DNSDomainFromDot(qname, &qname_dns)) {
    191     LOG(ERROR) << filename << ": DNSDomainFromDot(" << qname << ") failed.";
    192     return false;
    193   }
    194 
    195   RunTestCase(id, qname_dns, qtype, resp_buf);
    196 
    197   return true;
    198 }
    199 
    200 }
    201 
    202 int main(int argc, char** argv) {
    203   int ret = 0;
    204 
    205   for (int i = 1; i < argc; i++)
    206     if (!ReadAndRunTestCase(argv[i]))
    207       ret = 2;
    208 
    209   // Cluster-Fuzz likes "#EOF" as the last line of output to help distinguish
    210   // successful runs from crashes.
    211   printf("#EOF\n");
    212 
    213   return ret;
    214 }
    215 
    216