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.
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"
27 void CrashDoubleFree(void) {
28 // Cause ASAN to detect a double-free
30 LOG(INFO
) << "Allocated p=" << p
<< ". Double-freeing...";
35 void CrashNullPointerDereference(void) {
36 // Cause the program to segfault with a NULL pointer dereference
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
,
53 base::FilePath filepath
= base::FilePath::FromUTF8Unsafe(filename
);
56 if (!base::ReadFileToString(filepath
, &json
)) {
57 LOG(ERROR
) << filename
<< ": couldn't read file.";
61 scoped_ptr
<base::Value
> value
= base::JSONReader::Read(json
);
63 LOG(ERROR
) << filename
<< ": couldn't parse JSON.";
67 base::DictionaryValue
* dict
;
68 if (!value
->GetAsDictionary(&dict
)) {
69 LOG(ERROR
) << filename
<< ": test case is not a dictionary.";
73 *crash_test
= dict
->HasKey("crash_test");
75 LOG(INFO
) << filename
<< ": crash_test is set!";
80 if (!dict
->GetInteger("id", &id_int
)) {
81 LOG(ERROR
) << filename
<< ": id is missing or not an integer.";
84 if (!FitsUint16(id_int
)) {
85 LOG(ERROR
) << filename
<< ": id is out of range.";
88 *id
= static_cast<uint16
>(id_int
);
90 if (!dict
->GetStringASCII("qname", qname
)) {
91 LOG(ERROR
) << filename
<< ": qname is missing or not a string.";
96 if (!dict
->GetInteger("qtype", &qtype_int
)) {
97 LOG(ERROR
) << filename
<< ": qtype is missing or not an integer.";
100 if (!FitsUint16(qtype_int
)) {
101 LOG(ERROR
) << filename
<< ": qtype is out of range.";
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.";
112 size_t resp_size
= resp_list
->GetSize();
114 resp_buf
->reserve(resp_size
);
115 for (size_t i
= 0; i
< resp_size
; i
++) {
117 if ((!resp_list
->GetInteger(i
, &resp_byte_int
))) {
118 LOG(ERROR
) << filename
<< ": response[" << i
<< "] is not an integer.";
121 if (!FitsUint8(resp_byte_int
)) {
122 LOG(ERROR
) << filename
<< ": response[" << i
<< "] is out of range.";
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
;
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.";
148 net::AddressList address_list
;
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
;
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
) {
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
))
181 LOG(INFO
) << "Crashing.";
183 // if we're not running under ASAN, that might not have worked
184 CrashNullPointerDereference();
189 std::string qname_dns
;
190 if (!net::DNSDomainFromDot(qname
, &qname_dns
)) {
191 LOG(ERROR
) << filename
<< ": DNSDomainFromDot(" << qname
<< ") failed.";
195 RunTestCase(id
, qname_dns
, qtype
, resp_buf
);
202 int main(int argc
, char** argv
) {
205 for (int i
= 1; i
< argc
; i
++)
206 if (!ReadAndRunTestCase(argv
[i
]))
209 // Cluster-Fuzz likes "#EOF" as the last line of output to help distinguish
210 // successful runs from crashes.