]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsbulktest.cc
spelling: syscall
[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
830515d8
BH
25#include <boost/accumulators/accumulators.hpp>
26#include <boost/array.hpp>
27#include <boost/accumulators/statistics.hpp>
b3bd2e75 28#include <boost/program_options.hpp>
4dd35bb1 29#include "inflighter.cc"
a4382ef3 30#include <deque>
4dd35bb1
BH
31#include "namespaces.hh"
32#include "dnsparser.hh"
33#include "sstuff.hh"
34#include "misc.hh"
35#include "dnswriter.hh"
36#include "dnsrecords.hh"
37
830515d8 38using namespace boost::accumulators;
b3bd2e75 39namespace po = boost::program_options;
de43ec0f 40
b3bd2e75 41po::variables_map g_vm;
830515d8 42
4dd35bb1
BH
43StatBag S;
44
b3bd2e75
PD
45bool g_quiet=false;
46bool g_envoutput=false;
47
4dd35bb1
BH
48struct DNSResult
49{
50 vector<ComboAddress> ips;
51 int rcode;
d712a557 52 bool seenauthsoa;
4dd35bb1
BH
53};
54
62921444
BH
55struct TypedQuery
56{
57 TypedQuery(const string& name_, uint16_t type_) : name(name_), type(type_){}
a9456e80 58 DNSName name;
62921444
BH
59 uint16_t type;
60};
830515d8 61
4dd35bb1
BH
62struct SendReceive
63{
64 typedef int Identifier;
65 typedef DNSResult Answer; // ip
66 int d_socket;
a4382ef3 67 deque<uint16_t> d_idqueue;
62921444 68
830515d8
BH
69 typedef accumulator_set<
70 double
71 , stats<boost::accumulators::tag::extended_p_square,
232f0877 72 boost::accumulators::tag::median(with_p_square_quantile),
830515d8 73 boost::accumulators::tag::mean(immediate)
232f0877 74 >
830515d8
BH
75 > acc_t;
76 acc_t* d_acc;
77
78 boost::array<double, 11> d_probs;
79
8a70e507 80 SendReceive(const std::string& remoteAddr, uint16_t port) : d_probs({{0.001,0.01, 0.025, 0.1, 0.25,0.5,0.75,0.9,0.975, 0.99,0.9999}})
4dd35bb1 81 {
830515d8
BH
82 d_acc = new acc_t(boost::accumulators::tag::extended_p_square::probabilities=d_probs);
83 //
84 //d_acc = acc_t
4dd35bb1
BH
85 d_socket = socket(AF_INET, SOCK_DGRAM, 0);
86 int val=1;
87 setsockopt(d_socket, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
88
910d27bb 89 ComboAddress remote(remoteAddr, port);
4dd35bb1 90 connect(d_socket, (struct sockaddr*)&remote, remote.getSocklen());
9cb47734 91 d_oks = d_errors = d_nodatas = d_nxdomains = d_unknowns = 0;
bf2aaa52 92 d_receiveds = d_receiveerrors = d_senderrors = 0;
04bbeaa0 93 for(unsigned int id =0 ; id < std::numeric_limits<uint16_t>::max(); ++id)
a4382ef3 94 d_idqueue.push_back(id);
4dd35bb1
BH
95 }
96
97 ~SendReceive()
98 {
99 close(d_socket);
100 }
101
62921444 102 Identifier send(TypedQuery& domain)
4dd35bb1
BH
103 {
104 //cerr<<"Sending query for '"<<domain<<"'"<<endl;
105
106 // send it, copy code from 'sdig'
107 vector<uint8_t> packet;
108
62921444 109 DNSPacketWriter pw(packet, domain.name, domain.type);
bf2aaa52
BH
110
111 if(d_idqueue.empty()) {
112 cerr<<"Exhausted ids!"<<endl;
113 exit(1);
114 }
a4382ef3
BH
115 pw.getHeader()->id = d_idqueue.front();
116 d_idqueue.pop_front();
4dd35bb1
BH
117 pw.getHeader()->rd = 1;
118 pw.getHeader()->qr = 0;
119
bf2aaa52
BH
120 if(::send(d_socket, &*packet.begin(), packet.size(), 0) < 0)
121 d_senderrors++;
4dd35bb1 122
a9456e80 123 if(!g_quiet)
124 cout<<"Sent out query for '"<<domain.name<<"' with id "<<pw.getHeader()->id<<endl;
4dd35bb1
BH
125 return pw.getHeader()->id;
126 }
127
128 bool receive(Identifier& id, DNSResult& dr)
129 {
130 if(waitForData(d_socket, 0, 500000) > 0) {
131 char buf[512];
9cb47734 132
4dd35bb1 133 int len = recv(d_socket, buf, sizeof(buf), 0);
9cb47734
BH
134 if(len < 0) {
135 d_receiveerrors++;
4dd35bb1 136 return 0;
9cb47734
BH
137 }
138 else {
139 d_receiveds++;
140 }
4dd35bb1
BH
141 // parse packet, set 'id', fill out 'ip'
142
27c0050c 143 MOADNSParser mdp(false, string(buf, len));
b3bd2e75 144 if(!g_quiet) {
c48816ca 145 cout<<"Reply to question for qname='"<<mdp.d_qname<<"', qtype="<<DNSRecordContent::NumberToType(mdp.d_qtype)<<endl;
b3bd2e75
PD
146 cout<<"Rcode: "<<mdp.d_header.rcode<<", RD: "<<mdp.d_header.rd<<", QR: "<<mdp.d_header.qr;
147 cout<<", TC: "<<mdp.d_header.tc<<", AA: "<<mdp.d_header.aa<<", opcode: "<<mdp.d_header.opcode<<endl;
148 }
4dd35bb1
BH
149 dr.rcode = mdp.d_header.rcode;
150 for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {
62921444 151 if(i->first.d_place == 1 && i->first.d_type == mdp.d_qtype)
4dd35bb1 152 dr.ips.push_back(ComboAddress(i->first.d_content->getZoneRepresentation()));
9cb47734 153 if(i->first.d_place == 2 && i->first.d_type == QType::SOA) {
d712a557
BH
154 dr.seenauthsoa = 1;
155 }
b3bd2e75
PD
156 if(!g_quiet)
157 {
c48816ca 158 cout<<i->first.d_place-1<<"\t"<<i->first.d_name<<"\tIN\t"<<DNSRecordContent::NumberToType(i->first.d_type);
b3bd2e75
PD
159 cout<<"\t"<<i->first.d_ttl<<"\t"<< i->first.d_content->getZoneRepresentation()<<"\n";
160 }
4dd35bb1 161 }
d712a557 162
4dd35bb1 163 id = mdp.d_header.id;
bf2aaa52 164 d_idqueue.push_back(id);
d712a557 165
4dd35bb1
BH
166 return 1;
167 }
168 return 0;
169 }
170
a4382ef3
BH
171 void deliverTimeout(const Identifier& id)
172 {
a9456e80 173 if(!g_quiet) {
174 cout<<"Timeout for id "<<id<<endl;
175 }
a4382ef3
BH
176 d_idqueue.push_back(id);
177 }
178
62921444 179 void deliverAnswer(TypedQuery& domain, const DNSResult& dr, unsigned int usec)
4dd35bb1 180 {
830515d8
BH
181 (*d_acc)(usec/1000.0);
182// if(usec > 1000000)
183 // cerr<<"Slow: "<<domain<<" ("<<usec/1000.0<<" msec)\n";
b3bd2e75 184 if(!g_quiet) {
62921444 185 cout<<domain.name<<"|"<<DNSRecordContent::NumberToType(domain.type)<<": ("<<usec/1000.0<<"msec) rcode: "<<dr.rcode;
ef7cd021 186 for(const ComboAddress& ca : dr.ips) {
b3bd2e75
PD
187 cout<<", "<<ca.toString();
188 }
189 cout<<endl;
4dd35bb1 190 }
d712a557
BH
191 if(dr.rcode == RCode::NXDomain) {
192 d_nxdomains++;
193 }
194 else if(dr.rcode) {
910d27bb 195 d_errors++;
d712a557
BH
196 }
197 else if(dr.ips.empty() && dr.seenauthsoa)
198 d_nodatas++;
910d27bb
BH
199 else if(!dr.ips.empty())
200 d_oks++;
9cb47734 201 else {
b3bd2e75 202 if(!g_quiet) cout<<"UNKNOWN!! ^^"<<endl;
9cb47734
BH
203 d_unknowns++;
204 }
4dd35bb1 205 }
9cb47734 206 unsigned int d_errors, d_nxdomains, d_nodatas, d_oks, d_unknowns;
bf2aaa52 207 unsigned int d_receiveds, d_receiveerrors, d_senderrors;
4dd35bb1
BH
208};
209
d2c701a0
PL
210void usage(po::options_description &desc) {
211 cerr << "Usage: dnsbulktest [OPTION].. IPADDRESS PORTNUMBER [LIMIT]"<<endl;
212 cerr << desc << "\n";
213}
214
910d27bb 215int main(int argc, char** argv)
fc9152ee 216try
4dd35bb1 217{
b3bd2e75
PD
218 po::options_description desc("Allowed options");
219 desc.add_options()
220 ("help,h", "produce help message")
221 ("quiet,q", "be quiet about individual queries")
62921444 222 ("type,t", po::value<string>()->default_value("A"), "What type to query for")
b3bd2e75 223 ("envoutput,e", "write report in shell environment format")
d2c701a0 224 ("version", "show the version number")
faf7fa43 225 ("www", po::value<bool>()->default_value("true"), "duplicate all queries with an additional 'www.' in front")
b3bd2e75
PD
226 ;
227
228 po::options_description alloptions;
229 po::options_description hidden("hidden options");
230 hidden.add_options()
231 ("ip-address", po::value<string>(), "ip-address")
232 ("portnumber", po::value<uint16_t>(), "portnumber")
233 ("limit", po::value<uint32_t>()->default_value(0), "limit");
234
235 alloptions.add(desc).add(hidden);
236 po::positional_options_description p;
237 p.add("ip-address", 1);
238 p.add("portnumber", 1);
239 p.add("limit", 1);
240
241 po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm);
242 po::notify(g_vm);
243
244 if (g_vm.count("help")) {
d2c701a0 245 usage(desc);
b3bd2e75 246 return EXIT_SUCCESS;
ac379ae2 247 }
d2c701a0
PL
248
249 if (g_vm.count("version")) {
250 cerr<<"dnsbulktest "<<VERSION<<endl;
251 return EXIT_SUCCESS;
252 }
253
b3bd2e75
PD
254 if(!g_vm.count("portnumber")) {
255 cerr<<"Fatal, need to specify ip-address and portnumber"<<endl;
d2c701a0 256 usage(desc);
b3bd2e75
PD
257 return EXIT_FAILURE;
258 }
259
faf7fa43 260 bool doWww = g_vm["www"].as<bool>();
62921444
BH
261 g_quiet = g_vm.count("quiet") > 0;
262 g_envoutput = g_vm.count("envoutput") > 0;
263 uint16_t qtype;
264 reportAllTypes();
265 try {
266 qtype = DNSRecordContent::TypeToNumber(g_vm["type"].as<string>());
267 }
268 catch(std::exception& e) {
269 cerr << e.what() << endl;
270 return EXIT_FAILURE;
271 }
b3bd2e75
PD
272
273 SendReceive sr(g_vm["ip-address"].as<string>(), g_vm["portnumber"].as<uint16_t>());
274 unsigned int limit = g_vm["limit"].as<unsigned int>();
910d27bb 275
62921444 276 vector<TypedQuery> domains;
4dd35bb1 277
62921444 278 Inflighter<vector<TypedQuery>, SendReceive> inflighter(domains, sr);
0eafd0fe 279 inflighter.d_maxInFlight = 1000;
bf2aaa52 280 inflighter.d_timeoutSeconds = 3;
0eafd0fe 281 inflighter.d_burst = 100;
4dd35bb1
BH
282 string line;
283
284 pair<string, string> split;
013f96e9 285 string::size_type pos;
4dd35bb1 286 while(stringfgets(stdin, line)) {
910d27bb
BH
287 if(limit && domains.size() >= limit)
288 break;
246ccf7b 289
4dd35bb1 290 trim_right(line);
246ccf7b
KM
291 if(line.empty() || line[0] == '#')
292 continue;
4dd35bb1 293 split=splitField(line,',');
246ccf7b
KM
294 if (split.second.empty())
295 split=splitField(line,'\t');
296 if(!split.second.find('.')) // skip 'Hidden profile' in quantcast list.
297 continue;
013f96e9 298 pos=split.second.find('/');
246ccf7b 299 if(pos != string::npos) // alexa has whole urls in the list now.
013f96e9 300 split.second.resize(pos);
6fc9c5ea
BH
301 if(find_if(split.second.begin(), split.second.end(), isalpha) == split.second.end())
302 {
303 continue; // this was an IP address
304 }
62921444 305 domains.push_back(TypedQuery(split.second, qtype));
faf7fa43 306 if(doWww)
307 domains.push_back(TypedQuery("www."+split.second, qtype));
4dd35bb1
BH
308 }
309 cerr<<"Read "<<domains.size()<<" domains!"<<endl;
d712a557 310 random_shuffle(domains.begin(), domains.end());
4dd35bb1 311
bf2aaa52
BH
312 boost::format datafmt("%s %|20t|%+15s %|40t|%s %|60t|%+15s\n");
313
4dd35bb1
BH
314 for(;;) {
315 try {
316 inflighter.run();
317 break;
318 }
830515d8 319 catch(std::exception& e) {
4dd35bb1
BH
320 cerr<<"Caught exception: "<<e.what()<<endl;
321 }
322 }
9cb47734 323
bf2aaa52
BH
324 cerr<< datafmt % "Sending" % "" % "Receiving" % "";
325 cerr<< datafmt % " Queued " % domains.size() % " Received" % sr.d_receiveds;
326 cerr<< datafmt % " Error -/-" % sr.d_senderrors % " Timeouts" % inflighter.getTimeouts();
327 cerr<< datafmt % " " % "" % " Unexpected" % inflighter.getUnexpecteds();
328
329 cerr<< datafmt % " Sent" % (domains.size() - sr.d_senderrors) % " Total" % (sr.d_receiveds + inflighter.getTimeouts() + inflighter.getUnexpecteds());
330
331 cerr<<endl;
332 cerr<< datafmt % "DNS Status" % "" % "" % "";
333 cerr<< datafmt % " OK" % sr.d_oks % "" % "";
334 cerr<< datafmt % " Error" % sr.d_errors % "" % "";
335 cerr<< datafmt % " No Data" % sr.d_nodatas % "" % "";
336 cerr<< datafmt % " NXDOMAIN" % sr.d_nxdomains % "" % "";
337 cerr<< datafmt % " Unknowns" % sr.d_unknowns % "" % "";
338 cerr<< datafmt % "Answers" % (sr.d_oks + sr.d_errors + sr.d_nodatas + sr.d_nxdomains + sr.d_unknowns) % "" % "";
339 cerr<< datafmt % " Timeouts " % (inflighter.getTimeouts()) % "" % "";
340 cerr<< datafmt % "Total " % (sr.d_oks + sr.d_errors + sr.d_nodatas + sr.d_nxdomains + sr.d_unknowns + inflighter.getTimeouts()) % "" % "";
830515d8
BH
341
342 cerr<<"\n";
343 cerr<< "Mean response time: "<<mean(*sr.d_acc) << " msec"<<", median: "<<median(*sr.d_acc)<< " msec\n";
830515d8 344
d497340c 345 boost::format statfmt("Time < %6.03f msec %|30t|%6.03f%% cumulative\n");
830515d8
BH
346
347 for (unsigned int i = 0; i < sr.d_probs.size(); ++i) {
348 cerr << statfmt % extended_p_square(*sr.d_acc)[i] % (100*sr.d_probs[i]);
349 }
350
b3bd2e75
PD
351 if(g_envoutput) {
352 cout<<"DBT_QUEUED="<<domains.size()<<endl;
353 cout<<"DBT_SENDERRORS="<<sr.d_senderrors<<endl;
354 cout<<"DBT_RECEIVED="<<sr.d_receiveds<<endl;
aa395172 355 cout<<"DBT_NXDOMAINS="<<sr.d_nxdomains<<endl;
356 cout<<"DBT_NODATAS="<<sr.d_nodatas<<endl;
357 cout<<"DBT_UNKNOWNS="<<sr.d_unknowns<<endl;
358 cout<<"DBT_OKS="<<sr.d_oks<<endl;
359 cout<<"DBT_ERRORS="<<sr.d_errors<<endl;
b3bd2e75
PD
360 cout<<"DBT_TIMEOUTS="<<inflighter.getTimeouts()<<endl;
361 cout<<"DBT_UNEXPECTEDS="<<inflighter.getUnexpecteds()<<endl;
bbb6ce1f 362 cout<<"DBT_OKPERCENTAGE="<<((float)sr.d_oks/domains.size()*100)<<endl;
dfddcdaf 363 cout<<"DBT_OKPERCENTAGEINT="<<(int)((float)sr.d_oks/domains.size()*100)<<endl;
b3bd2e75 364 }
4dd35bb1 365}
fc9152ee 366catch(PDNSException& pe)
367{
368 cerr<<"Fatal error: "<<pe.reason<<endl;
369 exit(EXIT_FAILURE);
370}