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"
30 #include <boost/tuple/tuple.hpp>
31 #include <boost/tuple/tuple_comparison.hpp>
38 #include "namespaces.hh"
39 #include "namespaces.hh"
43 struct tm
* pdns_localtime_r(const uint32_t* then
, struct tm
* tm
)
47 return localtime_r(&t
, tm
);
50 int32_t g_clientQuestions
, g_clientResponses
, g_serverQuestions
, g_serverResponses
, g_skipped
;
51 struct pdns_timeval g_lastanswerTime
, g_lastquestionTime
;
52 void makeReport(const struct pdns_timeval
& tv
)
54 int64_t clientdiff
= g_clientQuestions
- g_clientResponses
;
55 int64_t serverdiff
= g_serverQuestions
- g_serverResponses
;
57 if(clientdiff
> 1 && clientdiff
> 0.02*g_clientQuestions
) {
59 struct tm tm
=*pdns_localtime_r(&tv
.tv_sec
, &tm
);
60 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
62 cout
<< tmp
<< ": Resolver dropped too many questions ("
63 << g_clientQuestions
<<" vs " << g_clientResponses
<< "), diff: " <<clientdiff
<<endl
;
65 tm
=*pdns_localtime_r(&g_lastanswerTime
.tv_sec
, &tm
);
66 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
68 cout
<<"Last answer: "<<tmp
<<"."<<g_lastanswerTime
.tv_usec
/1000000.0<<endl
;
70 tm
=*pdns_localtime_r(&g_lastquestionTime
.tv_sec
, &tm
);
71 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
73 cout
<<"Last question: "<<tmp
<<"."<<g_lastquestionTime
.tv_usec
/1000000.0<<endl
;
76 if(serverdiff
> 1 && serverdiff
> 0.02*g_serverQuestions
) {
78 struct tm tm
=*pdns_localtime_r(&tv
.tv_sec
, &tm
);
79 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
81 cout
<< tmp
<< ": Auth server dropped too many questions ("
82 << g_serverQuestions
<<" vs " << g_serverResponses
<< "), diff: " <<serverdiff
<<endl
;
84 tm
=*pdns_localtime_r(&g_lastanswerTime
.tv_sec
, &tm
);
85 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
87 cout
<<"Last answer: "<<tmp
<<"."<<g_lastanswerTime
.tv_usec
/1000000.0<<endl
;
89 tm
=*pdns_localtime_r(&g_lastquestionTime
.tv_sec
, &tm
);
90 strftime(tmp
, sizeof(tmp
) - 1, "%F %H:%M:%S", &tm
);
92 cout
<<"Last question: "<<tmp
<<"."<<g_lastquestionTime
.tv_usec
/1000000.0<<endl
;
94 // cout <<"Recursive questions: "<<g_clientQuestions<<", recursive responses: " << g_clientResponses<<
95 // ", server questions: "<<g_serverQuestions<<", server responses: "<<g_serverResponses<<endl;
98 // cerr << tv.tv_sec << " " <<g_clientQuestions<<" " << g_clientResponses<< " "<<g_serverQuestions<<" "<<g_serverResponses<<" "<<g_skipped<<endl;
99 g_clientQuestions
=g_clientResponses
=g_serverQuestions
=g_serverResponses
=0;
104 cerr
<<"syntax: dnsgram INFILE..."<<endl
;
107 int main(int argc
, char** argv
)
110 // Parse possible options
116 for(int n
=1 ; n
< argc
; ++n
) {
117 if ((string
) argv
[n
] == "--help") {
122 if ((string
) argv
[n
] == "--version") {
123 cerr
<<"dnsgram "<<VERSION
<<endl
;
129 for(int n
=1 ; n
< argc
; ++n
) {
131 unsigned int parseErrors
=0, totalQueries
=0, skipped
=0;
132 PcapPacketReader
pr(argv
[n
]);
133 // PcapPacketWriter pw(argv[n]+string(".out"), pr);
134 /* four sorts of packets:
135 "rd": question from a client pc
136 "rd qr": answer to a client pc
137 "": question from the resolver
138 "qr": answer to the resolver */
140 /* what are interesting events to note? */
141 /* we measure every 60 seconds, each interval with 10% less answers than questions is interesting */
144 struct pdns_timeval lastreport
;
146 typedef set
<pair
<DNSName
, uint16_t> > queries_t
;
147 queries_t questions
, answers
;
149 // unsigned int count = 50000;
151 map
<pair
<DNSName
, uint16_t>, int> counts
;
153 map
<double, int> rdqcounts
, rdacounts
;
155 while(pr
.getUDPPacket()) {
156 if((ntohs(pr
.d_udp
->uh_dport
)==5300 || ntohs(pr
.d_udp
->uh_sport
)==5300 ||
157 ntohs(pr
.d_udp
->uh_dport
)==53 || ntohs(pr
.d_udp
->uh_sport
)==53) &&
160 MOADNSParser
mdp(false, (const char*)pr
.d_payload
, pr
.d_len
);
162 if(lastreport
.tv_sec
== 0) {
163 lastreport
= pr
.d_pheader
.ts
;
166 if(mdp
.d_header
.rd
&& !mdp
.d_header
.qr
) {
167 rdqcounts
[pr
.d_pheader
.ts
.tv_sec
+ 0.01*(pr
.d_pheader
.ts
.tv_usec
/10000)]++;
168 g_lastquestionTime
=pr
.d_pheader
.ts
;
171 counts
[make_pair(mdp
.d_qname
, mdp
.d_qtype
)]++;
172 questions
.insert(make_pair(mdp
.d_qname
, mdp
.d_qtype
));
174 else if(mdp
.d_header
.rd
&& mdp
.d_header
.qr
) {
175 rdacounts
[pr
.d_pheader
.ts
.tv_sec
+ 0.01*(pr
.d_pheader
.ts
.tv_usec
/10000)]++;
176 g_lastanswerTime
=pr
.d_pheader
.ts
;
178 answers
.insert(make_pair(mdp
.d_qname
, mdp
.d_qtype
));
180 else if(!mdp
.d_header
.rd
&& !mdp
.d_header
.qr
) {
181 g_lastquestionTime
=pr
.d_pheader
.ts
;
183 counts
[make_pair(mdp
.d_qname
, mdp
.d_qtype
)]++;
184 questions
.insert(make_pair(mdp
.d_qname
, mdp
.d_qtype
));
187 else if(!mdp
.d_header
.rd
&& mdp
.d_header
.qr
) {
188 answers
.insert(make_pair(mdp
.d_qname
, mdp
.d_qtype
));
192 if(pr
.d_pheader
.ts
.tv_sec
- lastreport
.tv_sec
>= 1) {
193 makeReport(pr
.d_pheader
.ts
);
194 lastreport
= pr
.d_pheader
.ts
;
197 catch(MOADNSException
& mde
) {
198 // cerr<<"error parsing packet: "<<mde.what()<<endl;
202 catch(std::exception
& e
) {
203 cerr
<< e
.what() << endl
;
209 map
<double, pair
<int, int>> splot
;
211 for(auto& a
: rdqcounts
) {
212 splot
[a
.first
].first
= a
.second
;
214 for(auto& a
: rdacounts
) {
215 splot
[a
.first
].second
= a
.second
;
218 cerr
<<"Writing out sub-second rd query/response stats to ./rdqaplot"<<endl
;
219 ofstream
plot("rdqaplot");
221 for(auto& a
: splot
) {
222 plot
<< a
.first
<<"\t"<<a
.second
.first
<<"\t"<<a
.second
.second
<<endl
;
224 cerr
<<"Parse errors: "<<parseErrors
<<", total queries: "<<totalQueries
<<endl
;
225 typedef vector
<queries_t::value_type
> diff_t
;
227 set_difference(questions
.begin(), questions
.end(), answers
.begin(), answers
.end(), back_inserter(diff
));
228 cerr
<<questions
.size()<<" different rd questions, "<< answers
.size()<<" different rd answers, diff: "<<diff
.size()<<endl
;
229 cerr
<<skipped
<<" skipped\n";
231 cerr
<<"Generating 'failed' file with failed queries and counts\n";
232 ofstream
failed("failed");
233 failed
<<"name\ttype\tnumber\n";
234 for(diff_t::const_iterator i
= diff
.begin(); i
!= diff
.end() ; ++i
) {
235 failed
<< i
->first
<< "\t" << DNSRecordContent::NumberToType(i
->second
) << "\t"<< counts
[make_pair(i
->first
, i
->second
)]<<"\n";
240 set_difference(answers
.begin(), answers
.end(), questions
.begin(), questions
.end(), back_inserter(diff
));
241 cerr
<<diff
.size()<<" answers w/o questions\n";
243 cerr
<<"Generating 'succeeded' file with all unique answers and counts\n";
244 ofstream
succeeded("succeeded");
245 succeeded
<<"name\ttype\tnumber\n";
246 for(queries_t::const_iterator i
= answers
.begin(); i
!= answers
.end() ; ++i
) {
247 succeeded
<< i
->first
<< "\t" <<DNSRecordContent::NumberToType(i
->second
) << "\t" << counts
[make_pair(i
->first
, i
->second
)]<<"\n";
251 catch(std::exception
& e
)
253 cerr
<<"Fatal: "<<e
.what()<<endl
;