2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
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.
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.
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.
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.
25 #include <boost/accumulators/accumulators.hpp>
26 #include <boost/array.hpp>
27 #include <boost/accumulators/statistics.hpp>
28 #include <boost/program_options.hpp>
29 #include "inflighter.cc"
31 #include "namespaces.hh"
32 #include "dnsparser.hh"
35 #include "dnswriter.hh"
36 #include "dnsrecords.hh"
38 using namespace boost::accumulators
;
39 namespace po
= boost::program_options
;
41 po::variables_map g_vm
;
46 bool g_envoutput
=false;
50 vector
<ComboAddress
> ips
;
57 TypedQuery(const string
& name_
, uint16_t type_
) : name(name_
), type(type_
){}
64 typedef int Identifier
;
65 typedef DNSResult Answer
; // ip
67 deque
<uint16_t> d_idqueue
;
69 typedef accumulator_set
<
71 , stats
<boost::accumulators::tag::extended_p_square
,
72 boost::accumulators::tag::median(with_p_square_quantile
),
73 boost::accumulators::tag::mean(immediate
)
78 boost::array
<double, 11> d_probs
;
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}})
82 d_acc
= new acc_t(boost::accumulators::tag::extended_p_square::probabilities
=d_probs
);
85 d_socket
= socket(AF_INET
, SOCK_DGRAM
, 0);
87 setsockopt(d_socket
, SOL_SOCKET
, SO_REUSEADDR
, &val
, sizeof(val
));
89 ComboAddress
remote(remoteAddr
, port
);
90 connect(d_socket
, (struct sockaddr
*)&remote
, remote
.getSocklen());
91 d_oks
= d_errors
= d_nodatas
= d_nxdomains
= d_unknowns
= 0;
92 d_receiveds
= d_receiveerrors
= d_senderrors
= 0;
93 for(unsigned int id
=0 ; id
< std::numeric_limits
<uint16_t>::max(); ++id
)
94 d_idqueue
.push_back(id
);
102 Identifier
send(TypedQuery
& domain
)
104 //cerr<<"Sending query for '"<<domain<<"'"<<endl;
106 // send it, copy code from 'sdig'
107 vector
<uint8_t> packet
;
109 DNSPacketWriter
pw(packet
, domain
.name
, domain
.type
);
111 if(d_idqueue
.empty()) {
112 cerr
<<"Exhausted ids!"<<endl
;
115 pw
.getHeader()->id
= d_idqueue
.front();
116 d_idqueue
.pop_front();
117 pw
.getHeader()->rd
= 1;
118 pw
.getHeader()->qr
= 0;
120 if(::send(d_socket
, &*packet
.begin(), packet
.size(), 0) < 0)
124 cout
<<"Sent out query for '"<<domain
.name
<<"' with id "<<pw
.getHeader()->id
<<endl
;
125 return pw
.getHeader()->id
;
128 bool receive(Identifier
& id
, DNSResult
& dr
)
130 if(waitForData(d_socket
, 0, 500000) > 0) {
133 int len
= recv(d_socket
, buf
, sizeof(buf
), 0);
141 // parse packet, set 'id', fill out 'ip'
143 MOADNSParser
mdp(false, string(buf
, len
));
145 cout
<<"Reply to question for qname='"<<mdp
.d_qname
<<"', qtype="<<DNSRecordContent::NumberToType(mdp
.d_qtype
)<<endl
;
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
;
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
) {
151 if(i
->first
.d_place
== 1 && i
->first
.d_type
== mdp
.d_qtype
)
152 dr
.ips
.push_back(ComboAddress(i
->first
.d_content
->getZoneRepresentation()));
153 if(i
->first
.d_place
== 2 && i
->first
.d_type
== QType::SOA
) {
158 cout
<<i
->first
.d_place
-1<<"\t"<<i
->first
.d_name
<<"\tIN\t"<<DNSRecordContent::NumberToType(i
->first
.d_type
);
159 cout
<<"\t"<<i
->first
.d_ttl
<<"\t"<< i
->first
.d_content
->getZoneRepresentation()<<"\n";
163 id
= mdp
.d_header
.id
;
164 d_idqueue
.push_back(id
);
171 void deliverTimeout(const Identifier
& id
)
174 cout
<<"Timeout for id "<<id
<<endl
;
176 d_idqueue
.push_back(id
);
179 void deliverAnswer(TypedQuery
& domain
, const DNSResult
& dr
, unsigned int usec
)
181 (*d_acc
)(usec
/1000.0);
182 // if(usec > 1000000)
183 // cerr<<"Slow: "<<domain<<" ("<<usec/1000.0<<" msec)\n";
185 cout
<<domain
.name
<<"|"<<DNSRecordContent::NumberToType(domain
.type
)<<": ("<<usec
/1000.0<<"msec) rcode: "<<dr
.rcode
;
186 for(const ComboAddress
& ca
: dr
.ips
) {
187 cout
<<", "<<ca
.toString();
191 if(dr
.rcode
== RCode::NXDomain
) {
197 else if(dr
.ips
.empty() && dr
.seenauthsoa
)
199 else if(!dr
.ips
.empty())
202 if(!g_quiet
) cout
<<"UNKNOWN!! ^^"<<endl
;
206 unsigned int d_errors
, d_nxdomains
, d_nodatas
, d_oks
, d_unknowns
;
207 unsigned int d_receiveds
, d_receiveerrors
, d_senderrors
;
210 void usage(po::options_description
&desc
) {
211 cerr
<< "Usage: dnsbulktest [OPTION].. IPADDRESS PORTNUMBER [LIMIT]"<<endl
;
212 cerr
<< desc
<< "\n";
215 int main(int argc
, char** argv
)
218 po::options_description
desc("Allowed options");
220 ("help,h", "produce help message")
221 ("quiet,q", "be quiet about individual queries")
222 ("type,t", po::value
<string
>()->default_value("A"), "What type to query for")
223 ("envoutput,e", "write report in shell environment format")
224 ("version", "show the version number")
225 ("www", po::value
<bool>()->default_value(true), "duplicate all queries with an additional 'www.' in front")
228 po::options_description alloptions
;
229 po::options_description
hidden("hidden 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");
235 alloptions
.add(desc
).add(hidden
);
236 po::positional_options_description p
;
237 p
.add("ip-address", 1);
238 p
.add("portnumber", 1);
241 po::store(po::command_line_parser(argc
, argv
).options(alloptions
).positional(p
).run(), g_vm
);
244 if (g_vm
.count("help")) {
249 if (g_vm
.count("version")) {
250 cerr
<<"dnsbulktest "<<VERSION
<<endl
;
254 if(!g_vm
.count("portnumber")) {
255 cerr
<<"Fatal, need to specify ip-address and portnumber"<<endl
;
260 bool doWww
= g_vm
["www"].as
<bool>();
261 g_quiet
= g_vm
.count("quiet") > 0;
262 g_envoutput
= g_vm
.count("envoutput") > 0;
266 qtype
= DNSRecordContent::TypeToNumber(g_vm
["type"].as
<string
>());
268 catch(std::exception
& e
) {
269 cerr
<< e
.what() << endl
;
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>();
276 vector
<TypedQuery
> domains
;
278 Inflighter
<vector
<TypedQuery
>, SendReceive
> inflighter(domains
, sr
);
279 inflighter
.d_maxInFlight
= 1000;
280 inflighter
.d_timeoutSeconds
= 3;
281 inflighter
.d_burst
= 100;
284 pair
<string
, string
> split
;
285 string::size_type pos
;
286 while(stringfgets(stdin
, line
)) {
287 if(limit
&& domains
.size() >= limit
)
291 if(line
.empty() || line
[0] == '#')
293 split
=splitField(line
,',');
294 if (split
.second
.empty())
295 split
=splitField(line
,'\t');
296 if(split
.second
.find('.') == 0) // skip 'Hidden profile' in quantcast list.
298 pos
=split
.second
.find('/');
299 if(pos
!= string::npos
) // alexa has whole urls in the list now.
300 split
.second
.resize(pos
);
301 if(find_if(split
.second
.begin(), split
.second
.end(), isalpha
) == split
.second
.end())
303 continue; // this was an IP address
305 domains
.push_back(TypedQuery(split
.second
, qtype
));
307 domains
.push_back(TypedQuery("www."+split
.second
, qtype
));
309 cerr
<<"Read "<<domains
.size()<<" domains!"<<endl
;
310 random_shuffle(domains
.begin(), domains
.end());
312 boost::format
datafmt("%s %|20t|%+15s %|40t|%s %|60t|%+15s\n");
319 catch(std::exception
& e
) {
320 cerr
<<"Caught exception: "<<e
.what()<<endl
;
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();
329 cerr
<< datafmt
% " Sent" % (domains
.size() - sr
.d_senderrors
) % " Total" % (sr
.d_receiveds
+ inflighter
.getTimeouts() + inflighter
.getUnexpecteds());
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()) % "" % "";
343 cerr
<< "Mean response time: "<<mean(*sr
.d_acc
) << " msec"<<", median: "<<median(*sr
.d_acc
)<< " msec\n";
345 boost::format
statfmt("Time < %6.03f msec %|30t|%6.03f%% cumulative\n");
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
]);
352 cout
<<"DBT_QUEUED="<<domains
.size()<<endl
;
353 cout
<<"DBT_SENDERRORS="<<sr
.d_senderrors
<<endl
;
354 cout
<<"DBT_RECEIVED="<<sr
.d_receiveds
<<endl
;
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
;
360 cout
<<"DBT_TIMEOUTS="<<inflighter
.getTimeouts()<<endl
;
361 cout
<<"DBT_UNEXPECTEDS="<<inflighter
.getUnexpecteds()<<endl
;
362 cout
<<"DBT_OKPERCENTAGE="<<((float)sr
.d_oks
/domains
.size()*100)<<endl
;
363 cout
<<"DBT_OKPERCENTAGEINT="<<(int)((float)sr
.d_oks
/domains
.size()*100)<<endl
;
366 catch(PDNSException
& pe
)
368 cerr
<<"Fatal error: "<<pe
.reason
<<endl
;