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