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