]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsreplay.cc
rec: Don't account chained queries more than once
[thirdparty/pdns.git] / pdns / dnsreplay.cc
CommitLineData
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 */
60f851c0
BH
22/**
23Replay all recursion-desired DNS questions to a specified IP address.
24
25Track all outgoing questions, remap id to one of ours.
26Also track all recorded answers, and map them to that same id, the 'expectation'.
27
6fb6f693 28When we see a question, parse it, give it a QuestionIdentifier, and and an id from the free-id list.
60f851c0
BH
29
30When we see an answer in the tcpdump, parse it, make QI, and add it to the original QI
31 and check
32
33When we see an answer from the socket, use the id to match it up to the original QI
34 and check
35
36There is one central object, which has (when complete)
37 our assigned id
38 QI
39 Original answer
40 Socket answer
41
42What to do with timeouts. We keep around at most 65536 outstanding answers.
43*/
44
192d405b
BH
45/*
46 mental_clock=0;
47 for(;;) {
48
49 do {
50 read a packet
51 send a packet
52 } while(time_of_last_packet_sent < mental_clock)
53 mental_clock=time_of_last_packet_sent;
54
55 wait for a response packet for 0.1 seconds
56 note how much time has passed
57 mental_clock+=time_passed;
58 }
59
60 */
61
870a0fe4
AT
62#ifdef HAVE_CONFIG_H
63#include "config.h"
64#endif
60f851c0
BH
65#include <bitset>
66#include "statbag.hh"
67#include "dnspcap.hh"
68#include "sstuff.hh"
69#include "anadns.hh"
b9f65b4f 70#include <boost/program_options.hpp>
520d87b9 71#include "dnsrecords.hh"
976ec823 72#include "ednssubnet.hh"
73#include "ednsoptions.hh"
f9f05db4
BH
74// this is needed because boost multi_index also uses 'L', as do we (which is sad enough)
75#undef L
76
60f851c0
BH
77#include <set>
78#include <deque>
f9f05db4 79
efa1eb45 80#include <boost/format.hpp>
60f851c0
BH
81#include <boost/utility.hpp>
82#include <boost/multi_index_container.hpp>
83#include <boost/multi_index/ordered_index.hpp>
84#include <boost/multi_index/key_extractors.hpp>
85
61b26744 86#include "namespaces.hh"
60f851c0 87using namespace ::boost::multi_index;
10f4eea8 88#include "namespaces.hh"
60f851c0
BH
89
90StatBag S;
f751bd2d 91bool g_quiet=true;
2a8fb096 92int g_timeoutMsec=0;
60f851c0 93
b9f65b4f
BH
94namespace po = boost::program_options;
95
96po::variables_map g_vm;
192d405b 97
520d87b9 98const struct timeval operator*(float fact, const struct timeval& rhs)
192d405b
BH
99{
100 // cout<<"In: "<<rhs.tv_sec<<" + "<<rhs.tv_usec<<"\n";
101 struct timeval ret;
102 if( (2000000000 / fact) < rhs.tv_usec) {
103 double d=1.0 * rhs.tv_usec * fact;
104 ret.tv_sec=fact * rhs.tv_sec;
105 ret.tv_sec+=(int) (d/1000000);
106 d/=1000000;
107 d-=(int)d;
108
13f508a5 109 ret.tv_usec=(unsigned int)(1000000*d);
192d405b
BH
110 normalizeTV(ret);
111
112 cout<<"out complex: "<<ret.tv_sec<<" + "<<ret.tv_usec<<"\n";
113
114 return ret;
115 }
116
117 ret.tv_sec=rhs.tv_sec * fact;
118 ret.tv_usec=rhs.tv_usec * fact;
119
120 normalizeTV(ret);
121 // cout<<"out simple: "<<ret.tv_sec<<" + "<<ret.tv_usec<<"\n";
122 return ret;
123}
124
1451ac8a 125bool g_pleaseQuit;
126void pleaseQuitHandler(int)
127{
128 g_pleaseQuit=true;
129}
192d405b 130
60f851c0
BH
131class DNSIDManager : public boost::noncopyable
132{
133public:
134 DNSIDManager()
135 {
136 for(unsigned int i=0; i < 65536; ++i)
137 d_available.push_back(i);
60f851c0
BH
138 }
139
c9980579 140 uint16_t peakID()
60f851c0
BH
141 {
142 uint16_t ret;
143 if(!d_available.empty()) {
144 ret=d_available.front();
60f851c0
BH
145 return ret;
146 }
147 else
192d405b 148 throw runtime_error("out of ids!"); // XXX FIXME
60f851c0
BH
149 }
150
c9980579
BH
151 uint16_t getID()
152 {
153 uint16_t ret=peakID();
154 d_available.pop_front();
155 return ret;
156 }
157
60f851c0
BH
158 void releaseID(uint16_t id)
159 {
160 d_available.push_back(id);
161 }
162
163private:
164 deque<uint16_t> d_available;
165
166} s_idmanager;
167
168
1451ac8a 169void setSocketBuffer(int fd, int optname, uint32_t size)
170{
171 uint32_t psize=0;
172 socklen_t len=sizeof(psize);
173
174 if(!getsockopt(fd, SOL_SOCKET, optname, (char*)&psize, &len) && psize > size) {
175 cerr<<"Not decreasing socket buffer size from "<<psize<<" to "<<size<<endl;
176 return;
177 }
178
179 if (setsockopt(fd, SOL_SOCKET, optname, (char*)&size, sizeof(size)) < 0 )
180 cerr<<"Warning: unable to raise socket buffer size to "<<size<<": "<<strerror(errno)<<endl;
181}
182
183static void setSocketReceiveBuffer(int fd, uint32_t size)
184{
185 setSocketBuffer(fd, SO_RCVBUF, size);
186}
187
188static void setSocketSendBuffer(int fd, uint32_t size)
189{
190 setSocketBuffer(fd, SO_SNDBUF, size);
191}
192
193
60f851c0
BH
194struct AssignedIDTag{};
195struct QuestionTag{};
196
197struct QuestionData
198{
199 QuestionData() : d_assignedID(-1), d_origRcode(-1), d_newRcode(-1), d_norecursionavailable(false), d_origlate(false), d_newlate(false)
200 {
201 }
202 QuestionIdentifier d_qi;
203 int d_assignedID;
204 MOADNSParser::answers_t d_origAnswers, d_newAnswers;
205 int d_origRcode, d_newRcode;
206 struct timeval d_resentTime;
207 bool d_norecursionavailable;
208 bool d_origlate, d_newlate;
209};
210
211typedef multi_index_container<
212 QuestionData,
213 indexed_by<
214 ordered_unique<tag<QuestionTag>, BOOST_MULTI_INDEX_MEMBER(QuestionData, QuestionIdentifier, d_qi) > ,
4957a608 215 ordered_unique<tag<AssignedIDTag>, BOOST_MULTI_INDEX_MEMBER(QuestionData, int, d_assignedID) >
60f851c0
BH
216 >
217> qids_t;
232f0877 218
60f851c0 219qids_t qids;
efa1eb45
BH
220bool g_throttled;
221
222unsigned int s_questions, s_origanswers, s_weanswers, s_wetimedout, s_perfect, s_mostly, s_origtimedout;
60f851c0
BH
223unsigned int s_wenever, s_orignever;
224unsigned int s_webetter, s_origbetter, s_norecursionavailable;
225unsigned int s_weunmatched, s_origunmatched;
b6ca86bd 226unsigned int s_wednserrors, s_origdnserrors, s_duplicates;
60f851c0 227
60f851c0
BH
228
229
efa1eb45 230void WeOrigSlowQueriesDelta(int& weOutstanding, int& origOutstanding, int& weSlow, int& origSlow)
60f851c0
BH
231{
232 struct timeval now;
233 gettimeofday(&now, 0);
234
efa1eb45
BH
235 weOutstanding=origOutstanding=weSlow=origSlow=0;
236
60f851c0
BH
237 for(qids_t::iterator i=qids.begin(); i!=qids.end(); ++i) {
238 double dt=DiffTime(i->d_resentTime, now);
efa1eb45
BH
239 if(dt < 2.0) {
240 if(i->d_newRcode == -1)
4957a608 241 weOutstanding++;
efa1eb45 242 if(i->d_origRcode == -1)
4957a608 243 origOutstanding++;
efa1eb45
BH
244 }
245 else {
60f851c0 246 if(i->d_newRcode == -1) {
4957a608
BH
247 weSlow++;
248 if(!i->d_newlate) {
249 QuestionData qd=*i;
250 qd.d_newlate=true;
251 qids.replace(i, qd);
252
253 s_wetimedout++;
254 }
60f851c0
BH
255 }
256 if(i->d_origRcode == -1) {
4957a608
BH
257 origSlow++;
258 if(!i->d_origlate) {
259 QuestionData qd=*i;
260 qd.d_origlate=true;
261 qids.replace(i, qd);
262
263 s_origtimedout++;
264 }
60f851c0
BH
265 }
266 }
267 }
60f851c0
BH
268}
269
270void compactAnswerSet(MOADNSParser::answers_t orig, set<DNSRecord>& compacted)
271{
272 for(MOADNSParser::answers_t::const_iterator i=orig.begin(); i != orig.end(); ++i)
e693ff5a 273 if(i->first.d_place==DNSResourceRecord::ANSWER)
60f851c0
BH
274 compacted.insert(i->first);
275}
276
277bool isRcodeOk(int rcode)
278{
279 return rcode==0 || rcode==3;
280}
281
7ae71e06 282set<pair<DNSName,uint16_t> > s_origbetterset;
60f851c0 283
13f508a5
BH
284bool isRootReferral(const MOADNSParser::answers_t& answers)
285{
286 if(answers.empty())
287 return false;
288
289 bool ok=true;
290 for(MOADNSParser::answers_t::const_iterator iter = answers.begin(); iter != answers.end(); ++iter) {
f809c028 291 // cerr<<(int)iter->first.d_place<<", "<<iter->first.d_name<<" "<<iter->first.d_type<<", # "<<answers.size()<<endl;
13f508a5
BH
292 if(iter->first.d_place!=2)
293 ok=false;
f809c028 294 if(!iter->first.d_name.isRoot() || iter->first.d_type!=QType::NS)
13f508a5
BH
295 ok=false;
296 }
297 return ok;
298}
299
1451ac8a 300vector<uint32_t> flightTimes;
301void accountFlightTime(qids_t::const_iterator iter)
302{
303 if(flightTimes.empty())
304 flightTimes.resize(2050);
305
306 struct timeval now;
307 gettimeofday(&now, 0);
308 unsigned int mdiff = 1000*DiffTime(iter->d_resentTime, now);
309 if(mdiff > flightTimes.size()-2)
310 mdiff= flightTimes.size()-1;
311
312 flightTimes[mdiff]++;
313}
314
315uint64_t countLessThan(unsigned int msec)
316{
317 uint64_t ret=0;
318 for(unsigned int i = 0 ; i < msec && i < flightTimes.size() ; ++i) {
319 ret += flightTimes[i];
320 }
321 return ret;
322}
323
324void emitFlightTimes()
d28b71ce 325{
1451ac8a 326 uint64_t totals = countLessThan(flightTimes.size());
60efa636 327 unsigned int limits[]={1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 500, 1000, (unsigned int) flightTimes.size()};
1451ac8a 328 uint64_t sofar=0;
329 cout.setf(std::ios::fixed);
330 cout.precision(2);
331 for(unsigned int i =0 ; i < sizeof(limits)/sizeof(limits[0]); ++i) {
332 if(limits[i]!=flightTimes.size())
333 cout<<"Within "<<limits[i]<<" msec: ";
334 else
335 cout<<"Beyond "<<limits[i]-2<<" msec: ";
336 uint64_t here = countLessThan(limits[i]);
337 cout<<100.0*here/totals<<"% ("<<100.0*(here-sofar)/totals<<"%)"<<endl;
338 sofar=here;
339
340 }
341}
342
343void measureResultAndClean(qids_t::const_iterator iter)
344{
345 const QuestionData& qd=*iter;
346 accountFlightTime(iter);
60f851c0
BH
347
348 set<DNSRecord> canonicOrig, canonicNew;
349 compactAnswerSet(qd.d_origAnswers, canonicOrig);
350 compactAnswerSet(qd.d_newAnswers, canonicNew);
4957a608 351
f751bd2d 352 if(!g_quiet) {
1451ac8a 353 cout<<qd.d_qi<<", orig rcode: "<<qd.d_origRcode<<", ours: "<<qd.d_newRcode;
60f851c0
BH
354 cout<<", "<<canonicOrig.size()<< " vs " << canonicNew.size()<<", perfect: ";
355 }
356
357 if(canonicOrig==canonicNew) {
358 s_perfect++;
f751bd2d 359 if(!g_quiet)
60f851c0
BH
360 cout<<"yes\n";
361 }
362 else {
f751bd2d 363 if(!g_quiet)
60f851c0
BH
364 cout<<"no\n";
365
366 if(qd.d_norecursionavailable)
f751bd2d 367 if(!g_quiet)
4957a608 368 cout<<"\t* original nameserver did not provide recursion for this question *"<<endl;
60f851c0 369 if(qd.d_origRcode == qd.d_newRcode ) {
f751bd2d 370 if(!g_quiet)
4957a608 371 cout<<"\t* mostly correct *"<<endl;
60f851c0
BH
372 s_mostly++;
373 }
374
375 if(!isRcodeOk(qd.d_origRcode) && isRcodeOk(qd.d_newRcode)) {
f751bd2d 376 if(!g_quiet)
4957a608 377 cout<<"\t* we better *"<<endl;
60f851c0
BH
378 s_webetter++;
379 }
13f508a5 380 if(isRcodeOk(qd.d_origRcode) && !isRcodeOk(qd.d_newRcode) && !isRootReferral(qd.d_origAnswers)) {
f751bd2d 381 if(!g_quiet)
4957a608 382 cout<<"\t* orig better *"<<endl;
60f851c0 383 s_origbetter++;
f751bd2d 384 if(!g_quiet)
1451ac8a 385 if(s_origbetterset.insert(make_pair(qd.d_qi.d_qname, qd.d_qi.d_qtype)).second) {
7f2ebc5d 386 cout<<"orig better: " << qd.d_qi.d_qname<<" "<< qd.d_qi.d_qtype<<endl;
4957a608 387 }
60f851c0
BH
388 }
389
f751bd2d 390 if(!g_quiet) {
520d87b9 391 cout<<"orig: rcode="<<qd.d_origRcode<<"\n";
60f851c0 392 for(set<DNSRecord>::const_iterator i=canonicOrig.begin(); i!=canonicOrig.end(); ++i)
7f2ebc5d 393 cout<<"\t"<<i->d_name<<"\t"<<DNSRecordContent::NumberToType(i->d_type)<<"\t'" << (i->d_content ? i->d_content->getZoneRepresentation() : "") <<"'\n";
520d87b9 394 cout<<"new: rcode="<<qd.d_newRcode<<"\n";
60f851c0 395 for(set<DNSRecord>::const_iterator i=canonicNew.begin(); i!=canonicNew.end(); ++i)
7f2ebc5d 396 cout<<"\t"<<i->d_name<<"\t"<<DNSRecordContent::NumberToType(i->d_type)<<"\t'" << (i->d_content ? i->d_content->getZoneRepresentation() : "") <<"'\n";
60f851c0 397 cout<<"\n";
520d87b9
BH
398 cout<<"-\n";
399
60f851c0
BH
400 }
401 }
402
1451ac8a 403 int releaseID=qd.d_assignedID;
404 qids.erase(iter); // qd invalid now
405 s_idmanager.releaseID(releaseID);
60f851c0
BH
406}
407
408
409Socket *s_socket;
410
411void receiveFromReference()
412try
413{
414 string packet;
4eb84a82 415 ComboAddress remote;
2a8fb096
BH
416 int res=waitForData(s_socket->getHandle(), g_timeoutMsec/1000, 1000*(g_timeoutMsec%1000));
417
192d405b
BH
418 if(res < 0 || res==0)
419 return;
420
60f851c0
BH
421 while(s_socket->recvFromAsync(packet, remote)) {
422 try {
efa1eb45 423 s_weanswers++;
27c0050c 424 MOADNSParser mdp(false, packet.c_str(), packet.length());
60f851c0 425 if(!mdp.d_header.qr) {
4957a608
BH
426 cout<<"Received a question from our reference nameserver!"<<endl;
427 continue;
60f851c0
BH
428 }
429
430 typedef qids_t::index<AssignedIDTag>::type qids_by_id_index_t;
431 qids_by_id_index_t& idindex=qids.get<AssignedIDTag>();
432 qids_by_id_index_t::const_iterator found=idindex.find(ntohs(mdp.d_header.id));
433 if(found == idindex.end()) {
c6b30b4f 434 if(!g_quiet)
7f2ebc5d 435 cout<<"Received an answer ("<<mdp.d_qname<<") from reference nameserver with id "<<mdp.d_header.id<<" which we can't match to a question!"<<endl;
4957a608
BH
436 s_weunmatched++;
437 continue;
60f851c0 438 }
1451ac8a 439
440 QuestionData qd=*found; // we have to make a copy because we reinsert below
60f851c0
BH
441 qd.d_newAnswers=mdp.d_answers;
442 qd.d_newRcode=mdp.d_header.rcode;
443 idindex.replace(found, qd);
444 if(qd.d_origRcode!=-1) {
1451ac8a 445 qids_t::const_iterator iter= qids.project<0>(found);
446 measureResultAndClean(iter);
60f851c0
BH
447 }
448 }
449 catch(MOADNSException &e)
450 {
451 s_wednserrors++;
452 }
10f4eea8 453 catch(std::out_of_range &e)
60f851c0
BH
454 {
455 s_wednserrors++;
456 }
2b2ce344 457 catch(std::exception& e)
458 {
459 s_wednserrors++;
460 }
60f851c0
BH
461 }
462}
adc10f99 463catch(std::exception& e)
60f851c0 464{
1451ac8a 465 cerr<<"Receiver function died: "<<e.what()<<endl;
60f851c0
BH
466 exit(1);
467}
468catch(...)
469{
1451ac8a 470 cerr<<"Receiver function died with unknown exception"<<endl;
60f851c0
BH
471 exit(1);
472}
473
474void pruneQids()
475{
476 struct timeval now;
477 gettimeofday(&now, 0);
478
479 for(qids_t::iterator i=qids.begin(); i!=qids.end(); ) {
a1e7d16d 480 if(DiffTime(i->d_resentTime, now) < 10)
60f851c0
BH
481 ++i;
482 else {
483 s_idmanager.releaseID(i->d_assignedID);
484 if(i->d_newRcode==-1) {
485 s_wenever++;
486 }
487 if(i->d_origRcode==-1) {
4957a608 488 s_orignever++;
60f851c0
BH
489 }
490
491 qids.erase(i++);
492 }
493 }
494}
495
27b014bc
BH
496void printStats(uint64_t origWaitingFor=0, uint64_t weWaitingFor=0)
497{
27b014bc
BH
498 format headerfmt ("%|9t|Questions - Pend. - Drop = Answers = (On time + Late) = (Err + Ok)\n");
499 format datafmt("%s%|9t|%d %|21t|%d %|29t|%d %|36t|%d %|47t|%d %|57t|%d %|66t|%d %|72t|%d\n");
500
27b014bc
BH
501 cerr<<headerfmt;
502 cerr<<(datafmt % "Orig" % s_questions % origWaitingFor % s_orignever % s_origanswers % 0 % s_origtimedout % 0 % 0);
503 cerr<<(datafmt % "Refer." % s_questions % weWaitingFor % s_wenever % s_weanswers % 0 % s_wetimedout % 0 % 0);
504
505 cerr<<weWaitingFor<<" queries that could still come in on time, "<<qids.size()<<" outstanding"<<endl;
506
507 cerr<<"we late: "<<s_wetimedout<<", orig late: "<< s_origtimedout<<", "<<s_questions<<" questions sent, "<<s_origanswers
508 <<" original answers, "<<s_perfect<<" perfect, "<<s_mostly<<" mostly correct"<<", "<<s_webetter<<" we better, "<<s_origbetter<<" orig better ("<<s_origbetterset.size()<<" diff)"<<endl;
509 cerr<<"we never: "<<s_wenever<<", orig never: "<<s_orignever<<endl;
510 cerr<<"original questions from IP addresses for which recursion was not available: "<<s_norecursionavailable<<endl;
511 cerr<<"Unmatched from us: "<<s_weunmatched<<", unmatched from original: "<<s_origunmatched << " ( - decoding err: "<<s_origunmatched-s_origdnserrors<<")"<<endl;
512 cerr<<"DNS decoding errors from us: "<<s_wednserrors<<", from original: "<<s_origdnserrors<<", exact duplicates from client: "<<s_duplicates<<endl<<endl;
d28b71ce 513
27b014bc
BH
514}
515
60f851c0
BH
516void houseKeeping()
517{
efa1eb45
BH
518 static timeval last;
519
520 struct timeval now;
521 gettimeofday(&now, 0);
522
523 if(DiffTime(last, now) < 0.3)
524 return;
525
526 int weWaitingFor, origWaitingFor, weSlow, origSlow;
527 WeOrigSlowQueriesDelta(weWaitingFor, origWaitingFor, weSlow, origSlow);
60f851c0 528
efa1eb45
BH
529 if(!g_throttled) {
530 if( weWaitingFor > 1000) {
531 cerr<<"Too many questions ("<<weWaitingFor<<") outstanding, throttling"<<endl;
532 g_throttled=true;
533 }
60f851c0 534 }
efa1eb45
BH
535 else if(weWaitingFor < 750) {
536 cerr<<"Unthrottling ("<<weWaitingFor<<")"<<endl;
537 g_throttled=false;
538 }
539
540 if(DiffTime(last, now) < 2)
541 return;
542
543 last=now;
544
545 /*
546 Questions - Pend. - Drop = Answers = (On time + Late) = (Err + Ok)
547Orig 9 21 29 36 47 57 66 72
548
549
550 */
551
27b014bc 552 printStats(origWaitingFor, weWaitingFor);
60f851c0 553 pruneQids();
60f851c0
BH
554}
555
f751bd2d
BH
556
557bool g_rdSelector;
558
976ec823 559static void generateOptRR(const std::string& optRData, string& res)
560{
561 const uint8_t name = 0;
562 dnsrecordheader dh;
563 EDNS0Record edns0;
564 edns0.extRCode = 0;
565 edns0.version = 0;
566 edns0.Z = 0;
567
568 dh.d_type = htons(QType::OPT);
569 dh.d_class = htons(1280);
570 memcpy(&dh.d_ttl, &edns0, sizeof edns0);
571 dh.d_clen = htons((uint16_t) optRData.length());
572 res.assign((const char *) &name, sizeof name);
573 res.append((const char *) &dh, sizeof dh);
574 res.append(optRData.c_str(), optRData.length());
575}
576
577static void addECSOption(char* packet, const size_t& packetSize, uint16_t* len, const ComboAddress& remote, int stamp)
578{
579 string EDNSRR;
580 struct dnsheader* dh = (struct dnsheader*) packet;
581
582 EDNSSubnetOpts eso;
583 if(stamp < 0)
584 eso.source = Netmask(remote);
585 else {
586 ComboAddress stamped(remote);
587 *((char*)&stamped.sin4.sin_addr.s_addr)=stamp;
588 eso.source = Netmask(stamped);
589 }
590 string optRData=makeEDNSSubnetOptsString(eso);
591 string record;
592 generateEDNSOption(EDNSOptionCode::ECS, optRData, record);
593 generateOptRR(record, EDNSRR);
594
595
596 uint16_t arcount = ntohs(dh->arcount);
597 /* does it fit in the existing buffer? */
598 if (packetSize - *len > EDNSRR.size()) {
599 arcount++;
600 dh->arcount = htons(arcount);
601 memcpy(packet + *len, EDNSRR.c_str(), EDNSRR.size());
602 *len += EDNSRR.size();
603 }
604}
605
606
607bool sendPacketFromPR(PcapPacketReader& pr, const ComboAddress& remote, int stamp)
60f851c0 608{
7f922f6f 609 dnsheader* dh=(dnsheader*)pr.d_payload;
f751bd2d 610 bool sent=false;
f751bd2d
BH
611 if((ntohs(pr.d_udp->uh_dport)!=53 && ntohs(pr.d_udp->uh_sport)!=53) || dh->rd != g_rdSelector || (unsigned int)pr.d_len <= sizeof(dnsheader))
612 return sent;
13f508a5 613
c9980579 614 QuestionData qd;
60f851c0 615 try {
1451ac8a 616 // yes, we send out ALWAYS. Even if we don't do anything with it later,
617 if(!dh->qr) { // this is to stress out the reference server with all the pain
bc9e03a0 618 s_questions++;
1451ac8a 619 qd.d_assignedID = s_idmanager.getID();
c9980579
BH
620 uint16_t tmp=dh->id;
621 dh->id=htons(qd.d_assignedID);
590a5b4a 622 // dh->rd=1; // useful to replay traffic to auths to a recursor
976ec823 623 uint16_t dlen = pr.d_len;
624
61fe43f7 625 if (stamp >= 0) addECSOption((char*)pr.d_payload, 1500, &dlen, pr.getSource(), stamp);
976ec823 626 pr.d_len=dlen;
627 s_socket->sendTo((const char*)pr.d_payload, dlen, remote);
f751bd2d 628 sent=true;
c9980579
BH
629 dh->id=tmp;
630 }
27c0050c 631 MOADNSParser mdp(false, (const char*)pr.d_payload, pr.d_len);
a5d9353e 632 QuestionIdentifier qi=QuestionIdentifier::create(pr.getSource(), pr.getDest(), mdp);
1451ac8a 633
60f851c0 634 if(!mdp.d_header.qr) {
bc9e03a0 635
60f851c0 636 if(qids.count(qi)) {
4957a608 637 if(!g_quiet)
1451ac8a 638 cout<<"Saw an exact duplicate question in PCAP "<<qi<< endl;
4957a608 639 s_duplicates++;
1451ac8a 640 s_idmanager.releaseID(qd.d_assignedID); // release = puts at back of pool
4957a608 641 return sent;
60f851c0 642 }
1451ac8a 643 // new question - ID assigned above already
60f851c0
BH
644 qd.d_qi=qi;
645 gettimeofday(&qd.d_resentTime,0);
60f851c0 646 qids.insert(qd);
60f851c0
BH
647 }
648 else {
efa1eb45 649 s_origanswers++;
1451ac8a 650 qids_t::const_iterator iter=qids.find(qi);
651 if(iter != qids.end()) {
2010ac95
RG
652 QuestionData eqd=*iter;
653 eqd.d_origAnswers=mdp.d_answers;
654 eqd.d_origRcode=mdp.d_header.rcode;
4957a608
BH
655
656 if(!dh->ra) {
657 s_norecursionavailable++;
2010ac95 658 eqd.d_norecursionavailable=true;
4957a608 659 }
2010ac95 660 qids.replace(iter, eqd);
4957a608 661
2010ac95 662 if(eqd.d_newRcode!=-1) {
1451ac8a 663 measureResultAndClean(iter);
4957a608
BH
664 }
665
666 return sent;
60f851c0
BH
667 }
668 else {
4957a608
BH
669 s_origunmatched++;
670 if(!g_quiet)
671 cout<<"Unmatched original answer "<<qi<<endl;
60f851c0
BH
672 }
673 }
674 }
675 catch(MOADNSException &e)
676 {
976ec823 677 if(!g_quiet)
678 cerr<<"Error parsing packet: "<<e.what()<<endl;
1451ac8a 679 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
60f851c0
BH
680 s_origdnserrors++;
681 }
2b2ce344 682 catch(std::exception &e)
60f851c0 683 {
976ec823 684 if(!g_quiet)
685 cerr<<"Error parsing packet: "<<e.what()<<endl;
686
1451ac8a 687 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
bc9e03a0 688 s_origdnserrors++;
60f851c0 689 }
1451ac8a 690
f751bd2d 691 return sent;
60f851c0
BH
692}
693
2a6faa4f
PL
694void usage(po::options_description &desc) {
695 cerr << "Usage: dnsreplay [OPTIONS] FILENAME [IP-ADDRESS] [PORT]"<<endl;
696 cerr << desc << "\n";
697}
698
60f851c0
BH
699int main(int argc, char** argv)
700try
701{
b9f65b4f
BH
702 po::options_description desc("Allowed options");
703 desc.add_options()
704 ("help,h", "produce help message")
2a6faa4f 705 ("version", "show version number")
f751bd2d
BH
706 ("packet-limit", po::value<uint32_t>()->default_value(0), "stop after this many packets")
707 ("quiet", po::value<bool>()->default_value(true), "don't be too noisy")
708 ("recursive", po::value<bool>()->default_value(true), "look at recursion desired packets, or not (defaults true)")
2a8fb096 709 ("speedup", po::value<float>()->default_value(1), "replay at this speedup")
976ec823 710 ("timeout-msec", po::value<uint32_t>()->default_value(500), "wait at least this many milliseconds for a reply")
711 ("ecs-stamp", "Add original IP address to ECS in replay")
658b9c44
RG
712 ("ecs-mask", po::value<uint16_t>(), "Replace first octet of src IP address with this value in ECS")
713 ("source-ip", po::value<string>()->default_value(""), "IP to send the replayed packet from")
714 ("source-port", po::value<uint16_t>()->default_value(0), "Port to send the replayed packet from");
b9f65b4f
BH
715
716 po::options_description alloptions;
717 po::options_description hidden("hidden options");
718 hidden.add_options()
719 ("pcap-source", po::value<string>(), "PCAP source file")
720 ("target-ip", po::value<string>()->default_value("127.0.0.1"), "target-ip")
721 ("target-port", po::value<uint16_t>()->default_value(5300), "target port");
722
723 alloptions.add(desc).add(hidden);
724 po::positional_options_description p;
725 p.add("pcap-source", 1);
726 p.add("target-ip", 1);
727 p.add("target-port", 1);
728
729 po::store(po::command_line_parser(argc, argv).options(alloptions).positional(p).run(), g_vm);
730 po::notify(g_vm);
f751bd2d 731
520d87b9
BH
732 reportAllTypes();
733
b9f65b4f 734 if (g_vm.count("help")) {
2a6faa4f 735 usage(desc);
b9f65b4f 736 return EXIT_SUCCESS;
60f851c0 737 }
2a6faa4f
PL
738
739 if (g_vm.count("version")) {
740 cerr<<"dnsreplay "<<VERSION<<endl;
741 return EXIT_SUCCESS;
742 }
743
b9f65b4f
BH
744 if(!g_vm.count("pcap-source")) {
745 cerr<<"Fatal, need to specify at least a PCAP source file"<<endl;
2a6faa4f 746 usage(desc);
b9f65b4f
BH
747 return EXIT_FAILURE;
748 }
749
f751bd2d
BH
750 uint32_t packetLimit = g_vm["packet-limit"].as<uint32_t>();
751
752 g_rdSelector = g_vm["recursive"].as<bool>();
27b014bc 753
f751bd2d
BH
754 g_quiet = g_vm["quiet"].as<bool>();
755
1451ac8a 756 signal(SIGINT, pleaseQuitHandler);
520d87b9 757 float speedup=g_vm["speedup"].as<float>();
2a8fb096 758 g_timeoutMsec=g_vm["timeout-msec"].as<uint32_t>();
b9f65b4f
BH
759
760 PcapPacketReader pr(g_vm["pcap-source"].as<string>());
34460aee 761 s_socket= new Socket(AF_INET, SOCK_DGRAM);
60f851c0
BH
762
763 s_socket->setNonBlocking();
658b9c44
RG
764
765 if(g_vm.count("source-ip") && !g_vm["source-ip"].as<string>().empty())
766 s_socket->bind(ComboAddress(g_vm["source-ip"].as<string>(), g_vm["source-port"].as<uint16_t>()));
767
1451ac8a 768 setSocketReceiveBuffer(s_socket->getHandle(), 2000000);
769 setSocketSendBuffer(s_socket->getHandle(), 2000000);
770
4eb84a82 771 ComboAddress remote(g_vm["target-ip"].as<string>(),
232f0877 772 g_vm["target-port"].as<uint16_t>());
60f851c0 773
976ec823 774 int stamp = -1;
775 if(g_vm.count("ecs-stamp") && g_vm.count("ecs-mask"))
776 stamp=g_vm["ecs-mask"].as<uint16_t>();
777
b9f65b4f 778 cerr<<"Replaying packets to: '"<<g_vm["target-ip"].as<string>()<<"', port "<<g_vm["target-port"].as<uint16_t>()<<endl;
60f851c0
BH
779
780 unsigned int once=0;
192d405b
BH
781 struct timeval mental_time;
782 mental_time.tv_sec=0; mental_time.tv_usec=0;
783
cc4d4e8f 784 if(!pr.getUDPPacket()) // we do this here so we error out more cleanly on no packets
192d405b 785 return 0;
f751bd2d 786 unsigned int count=0;
bc9e03a0 787 bool first = true;
60f851c0 788 for(;;) {
1451ac8a 789 if(g_pleaseQuit) {
790 cerr<<"Interrupted from terminal"<<endl;
791 break;
792 }
efa1eb45 793 if(!((once++)%100))
60f851c0 794 houseKeeping();
f751bd2d 795
a1e7d16d
BH
796 struct timeval packet_ts;
797 packet_ts.tv_sec = 0;
798 packet_ts.tv_usec = 0;
bc9e03a0 799
a1e7d16d 800 while(packet_ts < mental_time) {
cc4d4e8f 801 if(!first && !pr.getUDPPacket()) // otherwise we miss the first packet
4957a608 802 goto out;
cc4d4e8f 803 first=false;
976ec823 804
a1e7d16d
BH
805 packet_ts.tv_sec = pr.d_pheader.ts.tv_sec;
806 packet_ts.tv_usec = pr.d_pheader.ts.tv_usec;
807
976ec823 808 if(sendPacketFromPR(pr, remote, stamp))
4957a608 809 count++;
192d405b 810 }
3a798afb 811 if(packetLimit && count >= packetLimit)
f751bd2d 812 break;
192d405b 813
a1e7d16d 814 mental_time=packet_ts;
192d405b
BH
815 struct timeval then, now;
816 gettimeofday(&then,0);
60f851c0 817
60f851c0 818 receiveFromReference();
192d405b
BH
819
820 gettimeofday(&now, 0);
821
b9f65b4f 822 mental_time= mental_time + speedup * (now-then);
60f851c0 823 }
192d405b 824 out:;
27b014bc
BH
825 sleep(1);
826 receiveFromReference();
827 printStats();
1451ac8a 828 emitFlightTimes();
60f851c0 829}
adc10f99 830catch(std::exception& e)
60f851c0
BH
831{
832 cerr<<"Fatal: "<<e.what()<<endl;
833}
b9f65b4f 834