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.
28 #include "dnsrecords.hh"
29 #include "dnsparser.hh"
36 #include "namespaces.hh"
37 #include "namespaces.hh"
41 static struct tm
* pdns_localtime_r(const uint32_t* then
, struct tm
* tm
)
45 return localtime_r(&t
, tm
);
48 int32_t g_clientQuestions
, g_clientResponses
, g_serverQuestions
, g_serverResponses
, g_skipped
;
49 struct pdns_timeval g_lastanswerTime
, g_lastquestionTime
;
51 static void makeReport(const struct pdns_timeval
& tv
)
53 int64_t clientdiff
= g_clientQuestions
- g_clientResponses
;
54 int64_t serverdiff
= g_serverQuestions
- g_serverResponses
;
56 if(clientdiff
> 1 && clientdiff
> 0.02*g_clientQuestions
) {
58 struct tm tm
=*pdns_localtime_r(&tv
.tv_sec
, &tm
);
59 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
61 cout
<< tmp
<< ": Resolver dropped too many questions ("
62 << g_clientQuestions
<<" vs " << g_clientResponses
<< "), diff: " <<clientdiff
<<endl
;
64 tm
=*pdns_localtime_r(&g_lastanswerTime
.tv_sec
, &tm
);
65 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
67 cout
<<"Last answer: "<<tmp
<<"."<<g_lastanswerTime
.tv_usec
/1000000.0<<endl
;
69 tm
=*pdns_localtime_r(&g_lastquestionTime
.tv_sec
, &tm
);
70 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
72 cout
<<"Last question: "<<tmp
<<"."<<g_lastquestionTime
.tv_usec
/1000000.0<<endl
;
75 if(serverdiff
> 1 && serverdiff
> 0.02*g_serverQuestions
) {
77 struct tm tm
=*pdns_localtime_r(&tv
.tv_sec
, &tm
);
78 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
80 cout
<< tmp
<< ": Auth server dropped too many questions ("
81 << g_serverQuestions
<<" vs " << g_serverResponses
<< "), diff: " <<serverdiff
<<endl
;
83 tm
=*pdns_localtime_r(&g_lastanswerTime
.tv_sec
, &tm
);
84 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
86 cout
<<"Last answer: "<<tmp
<<"."<<g_lastanswerTime
.tv_usec
/1000000.0<<endl
;
88 tm
=*pdns_localtime_r(&g_lastquestionTime
.tv_sec
, &tm
);
89 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
91 cout
<<"Last question: "<<tmp
<<"."<<g_lastquestionTime
.tv_usec
/1000000.0<<endl
;
93 // cout <<"Recursive questions: "<<g_clientQuestions<<", recursive responses: " << g_clientResponses<<
94 // ", server questions: "<<g_serverQuestions<<", server responses: "<<g_serverResponses<<endl;
97 // cerr << tv.tv_sec << " " <<g_clientQuestions<<" " << g_clientResponses<< " "<<g_serverQuestions<<" "<<g_serverResponses<<" "<<g_skipped<<endl;
98 g_clientQuestions
=g_clientResponses
=g_serverQuestions
=g_serverResponses
=0;
102 static void usage() {
103 cerr
<<"syntax: dnsgram INFILE..."<<endl
;
106 int main(int argc
, char** argv
)
109 // Parse possible options
115 for(int n
=1 ; n
< argc
; ++n
) {
116 if ((string
) argv
[n
] == "--help") {
121 if ((string
) argv
[n
] == "--version") {
122 cerr
<<"dnsgram "<<VERSION
<<endl
;
128 for(int n
=1 ; n
< argc
; ++n
) {
130 unsigned int parseErrors
=0, totalQueries
=0, skipped
=0;
131 PcapPacketReader
pr(argv
[n
]);
132 // PcapPacketWriter pw(argv[n]+string(".out"), pr);
133 /* four sorts of packets:
134 "rd": question from a client pc
135 "rd qr": answer to a client pc
136 "": question from the resolver
137 "qr": answer to the resolver */
139 /* what are interesting events to note? */
140 /* we measure every 60 seconds, each interval with 10% less answers than questions is interesting */
143 struct pdns_timeval lastreport
;
145 typedef set
<pair
<DNSName
, uint16_t> > queries_t
;
146 queries_t questions
, answers
;
148 // unsigned int count = 50000;
150 map
<pair
<DNSName
, uint16_t>, int> counts
;
152 map
<double, int> rdqcounts
, rdacounts
;
154 while(pr
.getUDPPacket()) {
155 if((ntohs(pr
.d_udp
->uh_dport
)==5300 || ntohs(pr
.d_udp
->uh_sport
)==5300 ||
156 ntohs(pr
.d_udp
->uh_dport
)==53 || ntohs(pr
.d_udp
->uh_sport
)==53) &&
159 MOADNSParser
mdp(false, (const char*)pr
.d_payload
, pr
.d_len
);
161 if(lastreport
.tv_sec
== 0) {
162 lastreport
= pr
.d_pheader
.ts
;
165 if(mdp
.d_header
.rd
&& !mdp
.d_header
.qr
) {
166 rdqcounts
[pr
.d_pheader
.ts
.tv_sec
+ 0.01*(pr
.d_pheader
.ts
.tv_usec
/10000.0)]++;
167 g_lastquestionTime
=pr
.d_pheader
.ts
;
170 counts
[pair(mdp
.d_qname
, mdp
.d_qtype
)]++;
171 questions
.emplace(mdp
.d_qname
, mdp
.d_qtype
);
173 else if(mdp
.d_header
.rd
&& mdp
.d_header
.qr
) {
174 rdacounts
[pr
.d_pheader
.ts
.tv_sec
+ 0.01*(pr
.d_pheader
.ts
.tv_usec
/10000.0)]++;
175 g_lastanswerTime
=pr
.d_pheader
.ts
;
177 answers
.emplace(mdp
.d_qname
, mdp
.d_qtype
);
179 else if(!mdp
.d_header
.rd
&& !mdp
.d_header
.qr
) {
180 g_lastquestionTime
=pr
.d_pheader
.ts
;
182 counts
[pair(mdp
.d_qname
, mdp
.d_qtype
)]++;
183 questions
.emplace(mdp
.d_qname
, mdp
.d_qtype
);
186 else if(!mdp
.d_header
.rd
&& mdp
.d_header
.qr
) {
187 answers
.emplace(mdp
.d_qname
, mdp
.d_qtype
);
191 if(pr
.d_pheader
.ts
.tv_sec
- lastreport
.tv_sec
>= 1) {
192 makeReport(pr
.d_pheader
.ts
);
193 lastreport
= pr
.d_pheader
.ts
;
196 catch(const MOADNSException
&mde
) {
197 // cerr<<"error parsing packet: "<<mde.what()<<endl;
201 catch(std::exception
& e
) {
202 cerr
<< e
.what() << endl
;
208 map
<double, pair
<int, int>> splot
;
210 for(auto& a
: rdqcounts
) {
211 splot
[a
.first
].first
= a
.second
;
213 for(auto& a
: rdacounts
) {
214 splot
[a
.first
].second
= a
.second
;
217 cerr
<<"Writing out sub-second rd query/response stats to ./rdqaplot"<<endl
;
218 ofstream
plot("rdqaplot");
220 for(auto& a
: splot
) {
221 plot
<< a
.first
<<"\t"<<a
.second
.first
<<"\t"<<a
.second
.second
<<endl
;
223 cerr
<<"Parse errors: "<<parseErrors
<<", total queries: "<<totalQueries
<<endl
;
224 typedef vector
<queries_t::value_type
> diff_t
;
226 set_difference(questions
.begin(), questions
.end(), answers
.begin(), answers
.end(), back_inserter(diff
));
227 cerr
<<questions
.size()<<" different rd questions, "<< answers
.size()<<" different rd answers, diff: "<<diff
.size()<<endl
;
228 cerr
<<skipped
<<" skipped\n";
230 cerr
<<"Generating 'failed' file with failed queries and counts\n";
231 ofstream
failed("failed");
232 failed
<<"name\ttype\tnumber\n";
233 for(diff_t::const_iterator i
= diff
.begin(); i
!= diff
.end() ; ++i
) {
234 failed
<< i
->first
<< "\t" << DNSRecordContent::NumberToType(i
->second
) << "\t"<< counts
[pair(i
->first
, i
->second
)]<<"\n";
239 set_difference(answers
.begin(), answers
.end(), questions
.begin(), questions
.end(), back_inserter(diff
));
240 cerr
<<diff
.size()<<" answers w/o questions\n";
242 cerr
<<"Generating 'succeeded' file with all unique answers and counts\n";
243 ofstream
succeeded("succeeded");
244 succeeded
<<"name\ttype\tnumber\n";
245 for(queries_t::const_iterator i
= answers
.begin(); i
!= answers
.end() ; ++i
) {
246 succeeded
<< i
->first
<< "\t" <<DNSRecordContent::NumberToType(i
->second
) << "\t" << counts
[pair(i
->first
, i
->second
)]<<"\n";
250 catch(std::exception
& e
)
252 cerr
<<"Fatal: "<<e
.what()<<endl
;