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
)
82 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}};
84 d_acc
= new acc_t(boost::accumulators::tag::extended_p_square::probabilities
=d_probs
);
87 d_socket
= socket(AF_INET
, SOCK_DGRAM
, 0);
89 setsockopt(d_socket
, SOL_SOCKET
, SO_REUSEADDR
, &val
, sizeof(val
));
91 ComboAddress
remote(remoteAddr
, port
);
92 connect(d_socket
, (struct sockaddr
*)&remote
, remote
.getSocklen());
93 d_oks
= d_errors
= d_nodatas
= d_nxdomains
= d_unknowns
= 0;
94 d_receiveds
= d_receiveerrors
= d_senderrors
= 0;
95 for(unsigned int id
=0 ; id
< std::numeric_limits
<uint16_t>::max(); ++id
)
96 d_idqueue
.push_back(id
);
104 Identifier
send(TypedQuery
& domain
)
106 //cerr<<"Sending query for '"<<domain<<"'"<<endl;
108 // send it, copy code from 'sdig'
109 vector
<uint8_t> packet
;
111 DNSPacketWriter
pw(packet
, domain
.name
, domain
.type
);
113 if(d_idqueue
.empty()) {
114 cerr
<<"Exhausted ids!"<<endl
;
117 pw
.getHeader()->id
= d_idqueue
.front();
118 d_idqueue
.pop_front();
119 pw
.getHeader()->rd
= 1;
120 pw
.getHeader()->qr
= 0;
122 if(::send(d_socket
, &*packet
.begin(), packet
.size(), 0) < 0)
126 cout
<<"Sent out query for '"<<domain
.name
<<"' with id "<<pw
.getHeader()->id
<<endl
;
127 return pw
.getHeader()->id
;
130 bool receive(Identifier
& id
, DNSResult
& dr
)
132 if(waitForData(d_socket
, 0, 500000) > 0) {
135 int len
= recv(d_socket
, buf
, sizeof(buf
), 0);
143 // parse packet, set 'id', fill out 'ip'
145 MOADNSParser
mdp(false, string(buf
, len
));
147 cout
<<"Reply to question for qname='"<<mdp
.d_qname
<<"', qtype="<<DNSRecordContent::NumberToType(mdp
.d_qtype
)<<endl
;
148 cout
<<"Rcode: "<<mdp
.d_header
.rcode
<<", RD: "<<mdp
.d_header
.rd
<<", QR: "<<mdp
.d_header
.qr
;
149 cout
<<", TC: "<<mdp
.d_header
.tc
<<", AA: "<<mdp
.d_header
.aa
<<", opcode: "<<mdp
.d_header
.opcode
<<endl
;
151 dr
.rcode
= mdp
.d_header
.rcode
;
152 for(MOADNSParser::answers_t::const_iterator i
=mdp
.d_answers
.begin(); i
!=mdp
.d_answers
.end(); ++i
) {
153 if(i
->first
.d_place
== 1 && i
->first
.d_type
== mdp
.d_qtype
)
154 dr
.ips
.push_back(ComboAddress(i
->first
.d_content
->getZoneRepresentation()));
155 if(i
->first
.d_place
== 2 && i
->first
.d_type
== QType::SOA
) {
160 cout
<<i
->first
.d_place
-1<<"\t"<<i
->first
.d_name
<<"\tIN\t"<<DNSRecordContent::NumberToType(i
->first
.d_type
);
161 cout
<<"\t"<<i
->first
.d_ttl
<<"\t"<< i
->first
.d_content
->getZoneRepresentation()<<"\n";
165 id
= mdp
.d_header
.id
;
166 d_idqueue
.push_back(id
);
173 void deliverTimeout(const Identifier
& id
)
176 cout
<<"Timeout for id "<<id
<<endl
;
178 d_idqueue
.push_back(id
);
181 void deliverAnswer(TypedQuery
& domain
, const DNSResult
& dr
, unsigned int usec
)
183 (*d_acc
)(usec
/1000.0);
184 // if(usec > 1000000)
185 // cerr<<"Slow: "<<domain<<" ("<<usec/1000.0<<" msec)\n";
187 cout
<<domain
.name
<<"|"<<DNSRecordContent::NumberToType(domain
.type
)<<": ("<<usec
/1000.0<<"msec) rcode: "<<dr
.rcode
;
188 for(const ComboAddress
& ca
: dr
.ips
) {
189 cout
<<", "<<ca
.toString();
193 if(dr
.rcode
== RCode::NXDomain
) {
199 else if(dr
.ips
.empty() && dr
.seenauthsoa
)
201 else if(!dr
.ips
.empty())
204 if(!g_quiet
) cout
<<"UNKNOWN!! ^^"<<endl
;
208 unsigned int d_errors
, d_nxdomains
, d_nodatas
, d_oks
, d_unknowns
;
209 unsigned int d_receiveds
, d_receiveerrors
, d_senderrors
;
212 void usage(po::options_description
&desc
) {
213 cerr
<< "Usage: dnsbulktest [OPTION].. IPADDRESS PORTNUMBER [LIMIT]"<<endl
;
214 cerr
<< desc
<< "\n";
217 int main(int argc
, char** argv
)
220 po::options_description
desc("Allowed options");
222 ("help,h", "produce help message")
223 ("quiet,q", "be quiet about individual queries")
224 ("type,t", po::value
<string
>()->default_value("A"), "What type to query for")
225 ("envoutput,e", "write report in shell environment format")
226 ("version", "show the version number")
227 ("www", po::value
<bool>()->default_value("true"), "duplicate all queries with an additional 'www.' in front")
230 po::options_description alloptions
;
231 po::options_description
hidden("hidden options");
233 ("ip-address", po::value
<string
>(), "ip-address")
234 ("portnumber", po::value
<uint16_t>(), "portnumber")
235 ("limit", po::value
<uint32_t>()->default_value(0), "limit");
237 alloptions
.add(desc
).add(hidden
);
238 po::positional_options_description p
;
239 p
.add("ip-address", 1);
240 p
.add("portnumber", 1);
243 po::store(po::command_line_parser(argc
, argv
).options(alloptions
).positional(p
).run(), g_vm
);
246 if (g_vm
.count("help")) {
251 if (g_vm
.count("version")) {
252 cerr
<<"dnsbulktest "<<VERSION
<<endl
;
256 if(!g_vm
.count("portnumber")) {
257 cerr
<<"Fatal, need to specify ip-address and portnumber"<<endl
;
262 bool doWww
= g_vm
["www"].as
<bool>();
263 g_quiet
= g_vm
.count("quiet") > 0;
264 g_envoutput
= g_vm
.count("envoutput") > 0;
268 qtype
= DNSRecordContent::TypeToNumber(g_vm
["type"].as
<string
>());
270 catch(std::exception
& e
) {
271 cerr
<< e
.what() << endl
;
275 SendReceive
sr(g_vm
["ip-address"].as
<string
>(), g_vm
["portnumber"].as
<uint16_t>());
276 unsigned int limit
= g_vm
["limit"].as
<unsigned int>();
278 vector
<TypedQuery
> domains
;
280 Inflighter
<vector
<TypedQuery
>, SendReceive
> inflighter(domains
, sr
);
281 inflighter
.d_maxInFlight
= 1000;
282 inflighter
.d_timeoutSeconds
= 3;
283 inflighter
.d_burst
= 100;
286 pair
<string
, string
> split
;
287 string::size_type pos
;
288 while(stringfgets(stdin
, line
)) {
289 if(limit
&& domains
.size() >= limit
)
293 if(line
.empty() || line
[0] == '#')
295 split
=splitField(line
,',');
296 if (split
.second
.empty())
297 split
=splitField(line
,'\t');
298 if(!split
.second
.find('.')) // skip 'Hidden profile' in quantcast list.
300 pos
=split
.second
.find('/');
301 if(pos
!= string::npos
) // alexa has whole urls in the list now.
302 split
.second
.resize(pos
);
303 if(find_if(split
.second
.begin(), split
.second
.end(), isalpha
) == split
.second
.end())
305 continue; // this was an IP address
307 domains
.push_back(TypedQuery(split
.second
, qtype
));
309 domains
.push_back(TypedQuery("www."+split
.second
, qtype
));
311 cerr
<<"Read "<<domains
.size()<<" domains!"<<endl
;
312 random_shuffle(domains
.begin(), domains
.end());
314 boost::format
datafmt("%s %|20t|%+15s %|40t|%s %|60t|%+15s\n");
321 catch(std::exception
& e
) {
322 cerr
<<"Caught exception: "<<e
.what()<<endl
;
326 cerr
<< datafmt
% "Sending" % "" % "Receiving" % "";
327 cerr
<< datafmt
% " Queued " % domains
.size() % " Received" % sr
.d_receiveds
;
328 cerr
<< datafmt
% " Error -/-" % sr
.d_senderrors
% " Timeouts" % inflighter
.getTimeouts();
329 cerr
<< datafmt
% " " % "" % " Unexpected" % inflighter
.getUnexpecteds();
331 cerr
<< datafmt
% " Sent" % (domains
.size() - sr
.d_senderrors
) % " Total" % (sr
.d_receiveds
+ inflighter
.getTimeouts() + inflighter
.getUnexpecteds());
334 cerr
<< datafmt
% "DNS Status" % "" % "" % "";
335 cerr
<< datafmt
% " OK" % sr
.d_oks
% "" % "";
336 cerr
<< datafmt
% " Error" % sr
.d_errors
% "" % "";
337 cerr
<< datafmt
% " No Data" % sr
.d_nodatas
% "" % "";
338 cerr
<< datafmt
% " NXDOMAIN" % sr
.d_nxdomains
% "" % "";
339 cerr
<< datafmt
% " Unknowns" % sr
.d_unknowns
% "" % "";
340 cerr
<< datafmt
% "Answers" % (sr
.d_oks
+ sr
.d_errors
+ sr
.d_nodatas
+ sr
.d_nxdomains
+ sr
.d_unknowns
) % "" % "";
341 cerr
<< datafmt
% " Timeouts " % (inflighter
.getTimeouts()) % "" % "";
342 cerr
<< datafmt
% "Total " % (sr
.d_oks
+ sr
.d_errors
+ sr
.d_nodatas
+ sr
.d_nxdomains
+ sr
.d_unknowns
+ inflighter
.getTimeouts()) % "" % "";
345 cerr
<< "Mean response time: "<<mean(*sr
.d_acc
) << " msec"<<", median: "<<median(*sr
.d_acc
)<< " msec\n";
347 boost::format
statfmt("Time < %6.03f msec %|30t|%6.03f%% cumulative\n");
349 for (unsigned int i
= 0; i
< sr
.d_probs
.size(); ++i
) {
350 cerr
<< statfmt
% extended_p_square(*sr
.d_acc
)[i
] % (100*sr
.d_probs
[i
]);
354 cout
<<"DBT_QUEUED="<<domains
.size()<<endl
;
355 cout
<<"DBT_SENDERRORS="<<sr
.d_senderrors
<<endl
;
356 cout
<<"DBT_RECEIVED="<<sr
.d_receiveds
<<endl
;
357 cout
<<"DBT_NXDOMAINS="<<sr
.d_nxdomains
<<endl
;
358 cout
<<"DBT_NODATAS="<<sr
.d_nodatas
<<endl
;
359 cout
<<"DBT_UNKNOWNS="<<sr
.d_unknowns
<<endl
;
360 cout
<<"DBT_OKS="<<sr
.d_oks
<<endl
;
361 cout
<<"DBT_ERRORS="<<sr
.d_errors
<<endl
;
362 cout
<<"DBT_TIMEOUTS="<<inflighter
.getTimeouts()<<endl
;
363 cout
<<"DBT_UNEXPECTEDS="<<inflighter
.getUnexpecteds()<<endl
;
364 cout
<<"DBT_OKPERCENTAGE="<<((float)sr
.d_oks
/domains
.size()*100)<<endl
;
365 cout
<<"DBT_OKPERCENTAGEINT="<<(int)((float)sr
.d_oks
/domains
.size()*100)<<endl
;
368 catch(PDNSException
& pe
)
370 cerr
<<"Fatal error: "<<pe
.reason
<<endl
;