forked from isc-projects/dnsgen
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathqueryfile.cc
290 lines (261 loc) · 6.67 KB
/
queryfile.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <cstdio>
#include <iostream>
#include <sstream>
#include <fstream>
#include <stdexcept>
#include <cerrno>
#include <map>
#include <algorithm>
#include <arpa/inet.h> // for ntohs() etc
#include <resolv.h> // for res_mkquery()
#include "queryfile.h"
#include "util.h"
//
// from https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
//
static std::map<std::string, uint16_t> type_map = {
{ "A", 1 },
{ "NS", 2 },
{ "MD", 3 },
{ "MF", 4 },
{ "CNAME", 5 },
{ "SOA", 6 },
{ "MB", 7 },
{ "MG", 8 },
{ "MR", 9 },
{ "NULL", 10 },
{ "WKS", 11 },
{ "PTR", 12 },
{ "HINFO", 13 },
{ "MINFO", 14 },
{ "MX", 15 },
{ "TXT", 16 },
{ "RP", 17 },
{ "AFSDB", 18 },
{ "X25", 19 },
{ "ISDN", 20 },
{ "RT", 21 },
{ "NSAP", 22 },
{ "NSAP-PTR", 23 },
{ "SIG", 24 },
{ "KEY", 25 },
{ "PX", 26 },
{ "GPOS", 27 },
{ "AAAA", 28 },
{ "LOC", 29 },
{ "NXT", 30 },
{ "EID", 31 },
{ "NIMLOC", 32 },
{ "SRV", 33 },
{ "ATMA", 34 },
{ "NAPTR", 35 },
{ "KX", 36 },
{ "CERT", 37 },
{ "A6", 38 },
{ "DNAME", 39 },
{ "SINK", 40 },
{ "OPT", 41 },
{ "APL", 42 },
{ "DS", 43 },
{ "SSHFP", 44 },
{ "IPSECKEY", 45 },
{ "RRSIG", 46 },
{ "NSEC", 47 },
{ "DNSKEY", 48 },
{ "DHCID", 49 },
{ "NSEC3", 50 },
{ "NSEC3PARAM", 51 },
{ "TLSA", 52 },
{ "SMIMEA", 53 },
{ "HIP", 55 },
{ "NINFO", 56 },
{ "RKEY", 57 },
{ "TALINK", 58 },
{ "CDS", 59 },
{ "CDNSKEY", 60 },
{ "OPENPGPKEY", 61 },
{ "CSYNC", 62 },
{ "SPF", 99 },
{ "UINFO", 100 },
{ "UID", 101 },
{ "GID", 102 },
{ "UNSPEC", 103 },
{ "NID", 104 },
{ "L32", 105 },
{ "L64", 106 },
{ "LP", 107 },
{ "EUI48", 108 },
{ "EUI64", 109 },
{ "TKEY", 249 },
{ "TSIG", 250 },
{ "IXFR", 251 },
{ "AXFR", 252 },
{ "MAILB", 253 },
{ "MAILA", 254 },
{ "ANY", 255 },
{ "URI", 256 },
{ "CAA", 257 },
{ "AVC", 258 },
{ "DOA", 259 },
{ "TA", 32768 },
{ "DLV", 32769 }
};
//
// Converts an RR type string to its numeric equivalent
//
// for performance, the match against the uppercase table
// above is always done first (since the input files are
// typically in upper case) and only if that lookup fails
// does the code then create a temporary upper-cased
// version of the input and recursively calls itself
//
static uint16_t type_to_number(const std::string& type, bool case_insensitive = true)
{
auto itr = type_map.find(type);
if (itr != type_map.end()) {
return itr->second;
} else if (type.compare(0, 4, "TYPE", 4) == 0) {
size_t index;
std::string num = type.substr(4, std::string::npos);
try {
unsigned long val = std::stoul(num, &index, 10);
if (num.cbegin() + index != num.cend()) {
throw std::runtime_error("numeric QTYPE trailing garbage");
} else if (val > std::numeric_limits<uint16_t>::max()) {
throw std::runtime_error("numeric QTYPE out of range");
} else {
return type_map[type] = val;
}
} catch (std::logic_error& e) {
throw std::runtime_error("numeric QTYPE unparseable");
}
} else {
// search again using the upper-cased version of the string
if (case_insensitive) {
std::string tmp(type);
std::transform(tmp.cbegin(), tmp.cend(), tmp.begin(), ::toupper);
return type_map[type] = type_to_number(tmp, false);
} else {
throw std::runtime_error("unrecognised QTYPE: " + type);
}
}
}
//
// creates a Record entry from the given qname and qtype
//
static QueryFile::Record make_record(const std::string& name, const std::string& type)
{
QueryFile::Record record;
record.resize(12 + 255 + 4); // maximum question section
uint16_t qtype = type_to_number(type);
int n = res_mkquery(0, name.c_str(), 1, qtype, nullptr, 0, nullptr,
record.data(), record.size());
if (n < 0) {
throw std::runtime_error("couldn't parse domain name");
} else {
record.resize(n);
return record;
}
}
//
// Loads a text file (in dnsperf format)
//
void QueryFile::read_txt(const std::string& filename)
{
std::ifstream file(filename);
if (!file) {
throw_errno("opening query file");
}
storage_t list;
std::string name, type;
size_t line_no = 0;
while (file >> name >> type) {
line_no++;
try {
Record record;
list.push_back(make_record(name, type));
} catch (std::runtime_error &e) {
std::string error = "reading query file at line "
+ std::to_string(line_no)
+ ": " + e.what();
throw_errno(error);
}
}
file.close();
std::swap(queries, list);
}
//
// Loads a raw input file (<16 bit network order length><payload...>)
//
void QueryFile::read_raw(const std::string& filename)
{
std::ifstream file(filename, std::ifstream::binary);
if (!file) {
throw_errno("opening query file");
}
storage_t list;
uint16_t len;
while (file) {
if (file.read(reinterpret_cast<char*>(&len), sizeof(len))) {
len = ntohs(len); // swap to host order
Record record;
record.resize(len);
if (file.read(reinterpret_cast<char*>(record.data()), len)) {
list.push_back(record);
}
}
}
file.close();
std::swap(queries, list);
}
//
// Saves the query set in raw format
//
void QueryFile::write_raw(const std::string& filename) const
{
std::ofstream file(filename, std::ifstream::binary);
if (!file) {
throw_errno("opening query file");
}
for (const auto& query: queries) {
uint16_t len = htons(query.size()); // big-endian
file.write(reinterpret_cast<const char*>(&len), sizeof(len));
file.write(reinterpret_cast<const char*>(query.data()), query.size());
}
file.close();
}
//
// Adds an EDNS OPT RR to every record in the QueryFile with
// the specified UDP buffer length and flags
//
void QueryFile::edns(const uint16_t buflen, uint16_t flags)
{
std::vector<uint8_t> opt = {
0, // name
0, 41, // type = OPT
static_cast<uint8_t>(buflen >> 8), // buflen MSB
static_cast<uint8_t>(buflen >> 0), // buflen LSB
0, // xrcode = 0,
0, // version = 0,
static_cast<uint8_t>(flags >> 8), // flags MSB
static_cast<uint8_t>(flags >> 0), // flags LSB
0, 0 // rdlen = 0
};
for (auto& query: queries) {
// adjust ARCOUNT
auto* p = reinterpret_cast<uint16_t*>(query.data());
p[5] = htons(ntohs(p[5]) + 1);
query.reserve(query.size() + 11);
query.insert(query.end(), opt.cbegin(), opt.cend());
}
}