Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / net / tools / dns_fuzz_stub / dns_fuzz_stub.cc
blob549d11c24459247d3f376b5cbe7eaa3cd230763a
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.
5 #include <algorithm>
6 #include <sstream>
7 #include <string>
8 #include <vector>
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"
25 namespace {
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);
35 void CrashNullPointerDereference(void) {
36 // Cause the program to segfault with a NULL pointer dereference
37 int *p = NULL;
38 *p = 0;
41 bool FitsUint8(int num) {
42 return (num >= 0) && (num <= kuint8max);
45 bool FitsUint16(int num) {
46 return (num >= 0) && (num <= kuint16max);
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);
55 std::string json;
56 if (!base::ReadFileToString(filepath, &json)) {
57 LOG(ERROR) << filename << ": couldn't read file.";
58 return false;
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;
67 base::DictionaryValue* dict;
68 if (!value->GetAsDictionary(&dict)) {
69 LOG(ERROR) << filename << ": test case is not a dictionary.";
70 return false;
73 *crash_test = dict->HasKey("crash_test");
74 if (*crash_test) {
75 LOG(INFO) << filename << ": crash_test is set!";
76 return true;
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;
84 if (!FitsUint16(id_int)) {
85 LOG(ERROR) << filename << ": id is out of range.";
86 return false;
88 *id = static_cast<uint16>(id_int);
90 if (!dict->GetStringASCII("qname", qname)) {
91 LOG(ERROR) << filename << ": qname is missing or not a string.";
92 return false;
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;
100 if (!FitsUint16(qtype_int)) {
101 LOG(ERROR) << filename << ": qtype is out of range.";
102 return false;
104 *qtype = static_cast<uint16>(qtype_int);
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;
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;
121 if (!FitsUint8(resp_byte_int)) {
122 LOG(ERROR) << filename << ": response[" << i << "] is out of range.";
123 return false;
125 resp_buf->push_back(static_cast<char>(resp_byte_int));
127 DCHECK(resp_buf->size() == resp_size);
129 LOG(INFO) << "Query: id=" << id_int << ", "
130 << "qname=" << *qname << ", "
131 << "qtype=" << qtype_int << ", "
132 << "resp_size=" << resp_size;
134 return true;
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());
143 if (!response.InitParse(resp_buf.size(), query)) {
144 LOG(INFO) << "InitParse failed.";
145 return;
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;
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";
164 LOG(INFO) << result_line.str();
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;
174 LOG(INFO) << "Test case: " << filename;
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;
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;
189 std::string qname_dns;
190 if (!net::DNSDomainFromDot(qname, &qname_dns)) {
191 LOG(ERROR) << filename << ": DNSDomainFromDot(" << qname << ") failed.";
192 return false;
195 RunTestCase(id, qname_dns, qtype, resp_buf);
197 return true;
202 int main(int argc, char** argv) {
203 int ret = 0;
205 for (int i = 1; i < argc; i++)
206 if (!ReadAndRunTestCase(argv[i]))
207 ret = 2;
209 // Cluster-Fuzz likes "#EOF" as the last line of output to help distinguish
210 // successful runs from crashes.
211 printf("#EOF\n");
213 return ret;