]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsbulktest.cc
rec: allow exception to proxy protocal usage for specific listen addresses
[thirdparty/pdns.git] / pdns / dnsbulktest.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
dc125378
OM
25#pragma GCC diagnostic push
26#pragma GCC diagnostic ignored "-Wunused-parameter"
d65e98fd 27#if __clang_major__ >= 15
dc125378 28#pragma GCC diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy"
d65e98fd 29#endif
830515d8
BH
30#include <boost/accumulators/accumulators.hpp>
31#include <boost/array.hpp>
32#include <boost/accumulators/statistics.hpp>
dc125378 33#pragma GCC diagnostic pop
b3bd2e75 34#include <boost/program_options.hpp>
4dd35bb1 35#include "inflighter.cc"
a4382ef3 36#include <deque>
4dd35bb1
BH
37#include "namespaces.hh"
38#include "dnsparser.hh"
39#include "sstuff.hh"
40#include "misc.hh"
41#include "dnswriter.hh"
42#include "dnsrecords.hh"
d720eb8a
OM
43#include "dns_random.hh"
44#include "arguments.hh"
4dd35bb1 45
830515d8 46using namespace boost::accumulators;
b3bd2e75 47namespace po = boost::program_options;
de43ec0f 48
b3bd2e75 49po::variables_map g_vm;
830515d8 50
4dd35bb1
BH
51StatBag S;
52
d720eb8a
OM
53ArgvMap &arg()
54{
55 static ArgvMap theArg;
56 return theArg;
57}
58
b3bd2e75
PD
59bool g_quiet=false;
60bool g_envoutput=false;
61
4dd35bb1
BH
62struct DNSResult
63{
64 vector<ComboAddress> ips;
014516a0 65 int rcode{0};
24a6058d 66 bool seenauthsoa{false};
4dd35bb1
BH
67};
68
62921444
BH
69struct TypedQuery
70{
71 TypedQuery(const string& name_, uint16_t type_) : name(name_), type(type_){}
a9456e80 72 DNSName name;
62921444
BH
73 uint16_t type;
74};
830515d8 75
4dd35bb1
BH
76struct SendReceive
77{
014516a0
RG
78 using Identifier = int;
79 using Answer = DNSResult; // ip
80 Socket d_socket;
dc593046 81 std::deque<uint16_t> d_idqueue;
d65e98fd 82
014516a0 83 using acc_t = accumulator_set<
830515d8
BH
84 double
85 , stats<boost::accumulators::tag::extended_p_square,
232f0877 86 boost::accumulators::tag::median(with_p_square_quantile),
830515d8 87 boost::accumulators::tag::mean(immediate)
232f0877 88 >
014516a0 89 >;
dd27dc73 90 unique_ptr<acc_t> d_acc;
014516a0
RG
91
92 static constexpr std::array<double, 11> s_probs{{0.001,0.01, 0.025, 0.1, 0.25,0.5,0.75,0.9,0.975, 0.99,0.9999}};
93 unsigned int d_errors{0};
94 unsigned int d_nxdomains{0};
95 unsigned int d_nodatas{0};
96 unsigned int d_oks{0};
97 unsigned int d_unknowns{0};
98 unsigned int d_received{0};
99 unsigned int d_receiveerrors{0};
100 unsigned int d_senderrors{0};
d65e98fd 101
014516a0
RG
102 SendReceive(const std::string& remoteAddr, uint16_t port) :
103 d_socket(AF_INET, SOCK_DGRAM),
104 d_acc(make_unique<acc_t>(acc_t(boost::accumulators::tag::extended_p_square::probabilities=s_probs)))
4dd35bb1 105 {
014516a0 106 d_socket.setReuseAddr();
910d27bb 107 ComboAddress remote(remoteAddr, port);
014516a0
RG
108 d_socket.connect(remote);
109 for (unsigned int id =0 ; id < std::numeric_limits<uint16_t>::max(); ++id) {
a4382ef3 110 d_idqueue.push_back(id);
014516a0 111 }
4dd35bb1 112 }
d65e98fd 113
62921444 114 Identifier send(TypedQuery& domain)
4dd35bb1
BH
115 {
116 //cerr<<"Sending query for '"<<domain<<"'"<<endl;
d65e98fd 117
4dd35bb1
BH
118 // send it, copy code from 'sdig'
119 vector<uint8_t> packet;
d65e98fd 120
62921444 121 DNSPacketWriter pw(packet, domain.name, domain.type);
bf2aaa52 122
014516a0 123 if (d_idqueue.empty()) {
bf2aaa52
BH
124 cerr<<"Exhausted ids!"<<endl;
125 exit(1);
d65e98fd 126 }
a4382ef3
BH
127 pw.getHeader()->id = d_idqueue.front();
128 d_idqueue.pop_front();
4dd35bb1
BH
129 pw.getHeader()->rd = 1;
130 pw.getHeader()->qr = 0;
d65e98fd 131
014516a0 132 if (::send(d_socket.getHandle(), &*packet.begin(), packet.size(), 0) < 0) {
bf2aaa52 133 d_senderrors++;
014516a0 134 }
d65e98fd 135
014516a0 136 if (!g_quiet) {
a9456e80 137 cout<<"Sent out query for '"<<domain.name<<"' with id "<<pw.getHeader()->id<<endl;
014516a0 138 }
4dd35bb1
BH
139 return pw.getHeader()->id;
140 }
d65e98fd 141
ba2b8a7a 142 bool receive(Identifier& iden, DNSResult& dnsResult)
4dd35bb1 143 {
014516a0 144 if (waitForData(d_socket.getHandle(), 0, 500000) > 0) {
e2ecb440 145 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init): no need to initialize the buffer
014516a0 146 std::array<char, 512> buf;
d65e98fd 147
014516a0
RG
148 auto len = recv(d_socket.getHandle(), buf.data(), buf.size(), 0);
149 if (len < 0) {
9cb47734 150 d_receiveerrors++;
014516a0 151 return false;
9cb47734 152 }
014516a0 153 d_received++;
d65e98fd
FM
154 // parse packet, set 'id', fill out 'ip'
155
014516a0 156 MOADNSParser mdp(false, string(buf.data(), static_cast<size_t>(len)));
ba2b8a7a
FM
157 if (!g_quiet) {
158 cout << "Reply to question for qname='" << mdp.d_qname << "', qtype=" << DNSRecordContent::NumberToType(mdp.d_qtype) << endl;
159 cout << "Rcode: " << mdp.d_header.rcode << ", RD: " << mdp.d_header.rd << ", QR: " << mdp.d_header.qr;
160 cout << ", TC: " << mdp.d_header.tc << ", AA: " << mdp.d_header.aa << ", opcode: " << mdp.d_header.opcode << endl;
b3bd2e75 161 }
ba2b8a7a
FM
162 dnsResult.rcode = mdp.d_header.rcode;
163 for (auto i = mdp.d_answers.begin(); i != mdp.d_answers.end(); ++i) {
164 if (i->first.d_place == 1 && i->first.d_type == mdp.d_qtype) {
165 dnsResult.ips.emplace_back(i->first.getContent()->getZoneRepresentation());
d712a557 166 }
ba2b8a7a
FM
167 if (i->first.d_place == 2 && i->first.d_type == QType::SOA) {
168 dnsResult.seenauthsoa = true;
169 }
170 if (!g_quiet) {
171 cout << i->first.d_place - 1 << "\t" << i->first.d_name << "\tIN\t" << DNSRecordContent::NumberToType(i->first.d_type);
172 cout << "\t" << i->first.d_ttl << "\t" << i->first.getContent()->getZoneRepresentation() << "\n";
b3bd2e75 173 }
4dd35bb1 174 }
d65e98fd 175
ba2b8a7a
FM
176 iden = mdp.d_header.id;
177 d_idqueue.push_back(iden);
d65e98fd 178
014516a0 179 return true;
4dd35bb1 180 }
ba2b8a7a 181
014516a0 182 return false;
4dd35bb1 183 }
d65e98fd 184
a4382ef3
BH
185 void deliverTimeout(const Identifier& id)
186 {
a9456e80 187 if(!g_quiet) {
188 cout<<"Timeout for id "<<id<<endl;
189 }
a4382ef3
BH
190 d_idqueue.push_back(id);
191 }
d65e98fd 192
ba2b8a7a 193 void deliverAnswer(TypedQuery& domain, const DNSResult& dnsResult, unsigned int usec)
4dd35bb1 194 {
ba2b8a7a
FM
195 (*d_acc)(usec / 1000.0);
196 // if(usec > 1000000)
197 // cerr<<"Slow: "<<domain<<" ("<<usec/1000.0<<" ms)\n";
198 if (!g_quiet) {
199 cout << domain.name << "|" << DNSRecordContent::NumberToType(domain.type) << ": (" << usec / 1000.0 << " ms) rcode: " << dnsResult.rcode;
200 for (const ComboAddress& comboAddress : dnsResult.ips) {
201 cout << ", " << comboAddress.toString();
b3bd2e75 202 }
ba2b8a7a 203 cout << endl;
4dd35bb1 204 }
ba2b8a7a 205 if (dnsResult.rcode == RCode::NXDomain) {
d712a557
BH
206 d_nxdomains++;
207 }
ba2b8a7a 208 else if (dnsResult.rcode != 0) {
910d27bb 209 d_errors++;
d712a557 210 }
ba2b8a7a 211 else if (dnsResult.ips.empty() && dnsResult.seenauthsoa) {
d712a557 212 d_nodatas++;
ba2b8a7a
FM
213 }
214 else if (!dnsResult.ips.empty()) {
910d27bb 215 d_oks++;
ba2b8a7a 216 }
9cb47734 217 else {
ba2b8a7a
FM
218 if (!g_quiet) {
219 cout << "UNKNOWN!! ^^" << endl;
220 }
9cb47734
BH
221 d_unknowns++;
222 }
4dd35bb1
BH
223 }
224};
225
050e6877 226static void usage(po::options_description &desc) {
d2c701a0
PL
227 cerr << "Usage: dnsbulktest [OPTION].. IPADDRESS PORTNUMBER [LIMIT]"<<endl;
228 cerr << desc << "\n";
229}
230
910d27bb 231int main(int argc, char** argv)
fc9152ee 232try
4dd35bb1 233{
fe810bcf
RG
234 ::arg().set("rng", "Specify random number generator to use. Valid values are auto,sodium,openssl,getrandom,arc4random,urandom.")="auto";
235 ::arg().set("entropy-source", "If set, read entropy from this file")="/dev/urandom";
236
b3bd2e75
PD
237 po::options_description desc("Allowed options");
238 desc.add_options()
239 ("help,h", "produce help message")
240 ("quiet,q", "be quiet about individual queries")
62921444 241 ("type,t", po::value<string>()->default_value("A"), "What type to query for")
b3bd2e75 242 ("envoutput,e", "write report in shell environment format")
d2c701a0 243 ("version", "show the version number")
503c33df 244 ("www", po::value<bool>()->default_value(true), "duplicate all queries with an additional 'www.' in front")
b3bd2e75
PD
245 ;
246
247 po::options_description alloptions;
248 po::options_description hidden("hidden options");
249 hidden.add_options()
250 ("ip-address", po::value<string>(), "ip-address")
251 ("portnumber", po::value<uint16_t>(), "portnumber")
252 ("limit", po::value<uint32_t>()->default_value(0), "limit");
253
254 alloptions.add(desc).add(hidden);
255 po::positional_options_description p;
256 p.add("ip-address", 1);
257 p.add("portnumber", 1);
258 p.add("limit", 1);
259
260 po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm);
261 po::notify(g_vm);
262
263 if (g_vm.count("help")) {
d2c701a0 264 usage(desc);
b3bd2e75 265 return EXIT_SUCCESS;
ac379ae2 266 }
d2c701a0
PL
267
268 if (g_vm.count("version")) {
269 cerr<<"dnsbulktest "<<VERSION<<endl;
270 return EXIT_SUCCESS;
271 }
272
b3bd2e75
PD
273 if(!g_vm.count("portnumber")) {
274 cerr<<"Fatal, need to specify ip-address and portnumber"<<endl;
d2c701a0 275 usage(desc);
b3bd2e75
PD
276 return EXIT_FAILURE;
277 }
278
faf7fa43 279 bool doWww = g_vm["www"].as<bool>();
62921444
BH
280 g_quiet = g_vm.count("quiet") > 0;
281 g_envoutput = g_vm.count("envoutput") > 0;
282 uint16_t qtype;
283 reportAllTypes();
284 try {
285 qtype = DNSRecordContent::TypeToNumber(g_vm["type"].as<string>());
286 }
287 catch(std::exception& e) {
288 cerr << e.what() << endl;
289 return EXIT_FAILURE;
290 }
b3bd2e75
PD
291
292 SendReceive sr(g_vm["ip-address"].as<string>(), g_vm["portnumber"].as<uint16_t>());
293 unsigned int limit = g_vm["limit"].as<unsigned int>();
d65e98fd 294
62921444 295 vector<TypedQuery> domains;
d65e98fd 296
62921444 297 Inflighter<vector<TypedQuery>, SendReceive> inflighter(domains, sr);
0eafd0fe 298 inflighter.d_maxInFlight = 1000;
bf2aaa52 299 inflighter.d_timeoutSeconds = 3;
0eafd0fe 300 inflighter.d_burst = 100;
4dd35bb1 301 string line;
d65e98fd 302
4dd35bb1 303 pair<string, string> split;
013f96e9 304 string::size_type pos;
4dd35bb1 305 while(stringfgets(stdin, line)) {
910d27bb
BH
306 if(limit && domains.size() >= limit)
307 break;
246ccf7b 308
dc593046 309 boost::trim_right(line);
246ccf7b
KM
310 if(line.empty() || line[0] == '#')
311 continue;
4dd35bb1 312 split=splitField(line,',');
246ccf7b
KM
313 if (split.second.empty())
314 split=splitField(line,'\t');
1ee9e847 315 if(split.second.find('.') == 0) // skip 'Hidden profile' in quantcast list.
246ccf7b 316 continue;
013f96e9 317 pos=split.second.find('/');
246ccf7b 318 if(pos != string::npos) // alexa has whole urls in the list now.
013f96e9 319 split.second.resize(pos);
6fc9c5ea
BH
320 if(find_if(split.second.begin(), split.second.end(), isalpha) == split.second.end())
321 {
322 continue; // this was an IP address
323 }
62921444 324 domains.push_back(TypedQuery(split.second, qtype));
faf7fa43 325 if(doWww)
326 domains.push_back(TypedQuery("www."+split.second, qtype));
4dd35bb1
BH
327 }
328 cerr<<"Read "<<domains.size()<<" domains!"<<endl;
d720eb8a 329 shuffle(domains.begin(), domains.end(), pdns::dns_random_engine());
4dd35bb1 330
bf2aaa52
BH
331 boost::format datafmt("%s %|20t|%+15s %|40t|%s %|60t|%+15s\n");
332
4dd35bb1
BH
333 for(;;) {
334 try {
335 inflighter.run();
336 break;
337 }
830515d8 338 catch(std::exception& e) {
4dd35bb1
BH
339 cerr<<"Caught exception: "<<e.what()<<endl;
340 }
341 }
9cb47734 342
bf2aaa52 343 cerr<< datafmt % "Sending" % "" % "Receiving" % "";
46d77a35 344 cerr<< datafmt % " Queued " % domains.size() % " Received" % sr.d_received;
bf2aaa52
BH
345 cerr<< datafmt % " Error -/-" % sr.d_senderrors % " Timeouts" % inflighter.getTimeouts();
346 cerr<< datafmt % " " % "" % " Unexpected" % inflighter.getUnexpecteds();
d65e98fd 347
46d77a35 348 cerr<< datafmt % " Sent" % (domains.size() - sr.d_senderrors) % " Total" % (sr.d_received + inflighter.getTimeouts() + inflighter.getUnexpecteds());
d65e98fd
FM
349
350 cerr<<endl;
bf2aaa52
BH
351 cerr<< datafmt % "DNS Status" % "" % "" % "";
352 cerr<< datafmt % " OK" % sr.d_oks % "" % "";
d65e98fd
FM
353 cerr<< datafmt % " Error" % sr.d_errors % "" % "";
354 cerr<< datafmt % " No Data" % sr.d_nodatas % "" % "";
bf2aaa52 355 cerr<< datafmt % " NXDOMAIN" % sr.d_nxdomains % "" % "";
d65e98fd 356 cerr<< datafmt % " Unknowns" % sr.d_unknowns % "" % "";
bf2aaa52
BH
357 cerr<< datafmt % "Answers" % (sr.d_oks + sr.d_errors + sr.d_nodatas + sr.d_nxdomains + sr.d_unknowns) % "" % "";
358 cerr<< datafmt % " Timeouts " % (inflighter.getTimeouts()) % "" % "";
359 cerr<< datafmt % "Total " % (sr.d_oks + sr.d_errors + sr.d_nodatas + sr.d_nxdomains + sr.d_unknowns + inflighter.getTimeouts()) % "" % "";
d65e98fd 360
830515d8 361 cerr<<"\n";
9273c94d 362 cerr<< "Mean response time: "<<mean(*sr.d_acc) << " ms"<<", median: "<<median(*sr.d_acc)<< " ms\n";
d65e98fd 363
9273c94d 364 boost::format statfmt("Time < %6.03f ms %|30t|%6.03f%% cumulative\n");
d65e98fd 365
014516a0 366 for (unsigned int i = 0; i < SendReceive::s_probs.size(); ++i) {
e2ecb440 367 cerr << statfmt % extended_p_square(*sr.d_acc)[i] % (100*SendReceive::s_probs.at(i));
014516a0 368 }
830515d8 369
014516a0 370 if (g_envoutput) {
b3bd2e75
PD
371 cout<<"DBT_QUEUED="<<domains.size()<<endl;
372 cout<<"DBT_SENDERRORS="<<sr.d_senderrors<<endl;
46d77a35 373 cout<<"DBT_RECEIVED="<<sr.d_received<<endl;
aa395172 374 cout<<"DBT_NXDOMAINS="<<sr.d_nxdomains<<endl;
375 cout<<"DBT_NODATAS="<<sr.d_nodatas<<endl;
376 cout<<"DBT_UNKNOWNS="<<sr.d_unknowns<<endl;
377 cout<<"DBT_OKS="<<sr.d_oks<<endl;
378 cout<<"DBT_ERRORS="<<sr.d_errors<<endl;
b3bd2e75
PD
379 cout<<"DBT_TIMEOUTS="<<inflighter.getTimeouts()<<endl;
380 cout<<"DBT_UNEXPECTEDS="<<inflighter.getUnexpecteds()<<endl;
bbb6ce1f 381 cout<<"DBT_OKPERCENTAGE="<<((float)sr.d_oks/domains.size()*100)<<endl;
dfddcdaf 382 cout<<"DBT_OKPERCENTAGEINT="<<(int)((float)sr.d_oks/domains.size()*100)<<endl;
b3bd2e75 383 }
4dd35bb1 384}
014516a0 385catch (const PDNSException& exp)
fc9152ee 386{
014516a0 387 cerr<<"Fatal error: "<<exp.reason<<endl;
e2ecb440 388 _exit(EXIT_FAILURE);
014516a0
RG
389}
390catch (const std::exception& exp) {
391 cerr<<"Fatal error: "<<exp.what()<<endl;
e2ecb440 392 _exit(EXIT_FAILURE);
fc9152ee 393}