]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsreplay.cc
Merge pull request #5270 from cmouse/geoip-test
[thirdparty/pdns.git] / pdns / dnsreplay.cc
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 */
22 /**
23 Replay all recursion-desired DNS questions to a specified IP address.
24
25 Track all outgoing questions, remap id to one of ours.
26 Also track all recorded answers, and map them to that same id, the 'expectation'.
27
28 When we see a question, parse it, give it a QuestionIdentifier, and and an id from the free-id list.
29
30 When we see an answer in the tcpdump, parse it, make QI, and add it to the original QI
31 and check
32
33 When we see an answer from the socket, use the id to match it up to the original QI
34 and check
35
36 There is one central object, which has (when complete)
37 our assigned id
38 QI
39 Original answer
40 Socket answer
41
42 What to do with timeouts. We keep around at most 65536 outstanding answers.
43 */
44
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
62 #ifdef HAVE_CONFIG_H
63 #include "config.h"
64 #endif
65 #include <bitset>
66 #include "statbag.hh"
67 #include "dnspcap.hh"
68 #include "sstuff.hh"
69 #include "anadns.hh"
70 #include <boost/program_options.hpp>
71 #include "dnsrecords.hh"
72 #include "ednssubnet.hh"
73 #include "ednsoptions.hh"
74 // this is needed because boost multi_index also uses 'L', as do we (which is sad enough)
75 #undef L
76
77 #include <set>
78 #include <deque>
79
80 #include <boost/format.hpp>
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
86 #include "namespaces.hh"
87 using namespace ::boost::multi_index;
88 #include "namespaces.hh"
89
90 StatBag S;
91 bool g_quiet=true;
92 int g_timeoutMsec=0;
93
94 namespace po = boost::program_options;
95
96 po::variables_map g_vm;
97
98 const struct timeval operator*(float fact, const struct timeval& rhs)
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
109 ret.tv_usec=(unsigned int)(1000000*d);
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
125 bool g_pleaseQuit;
126 void pleaseQuitHandler(int)
127 {
128 g_pleaseQuit=true;
129 }
130
131 class DNSIDManager : public boost::noncopyable
132 {
133 public:
134 DNSIDManager()
135 {
136 for(unsigned int i=0; i < 65536; ++i)
137 d_available.push_back(i);
138 }
139
140 uint16_t peakID()
141 {
142 uint16_t ret;
143 if(!d_available.empty()) {
144 ret=d_available.front();
145 return ret;
146 }
147 else
148 throw runtime_error("out of ids!"); // XXX FIXME
149 }
150
151 uint16_t getID()
152 {
153 uint16_t ret=peakID();
154 d_available.pop_front();
155 return ret;
156 }
157
158 void releaseID(uint16_t id)
159 {
160 d_available.push_back(id);
161 }
162
163 private:
164 deque<uint16_t> d_available;
165
166 } s_idmanager;
167
168
169 void 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
183 static void setSocketReceiveBuffer(int fd, uint32_t size)
184 {
185 setSocketBuffer(fd, SO_RCVBUF, size);
186 }
187
188 static void setSocketSendBuffer(int fd, uint32_t size)
189 {
190 setSocketBuffer(fd, SO_SNDBUF, size);
191 }
192
193
194 struct AssignedIDTag{};
195 struct QuestionTag{};
196
197 struct 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
211 typedef multi_index_container<
212 QuestionData,
213 indexed_by<
214 ordered_unique<tag<QuestionTag>, BOOST_MULTI_INDEX_MEMBER(QuestionData, QuestionIdentifier, d_qi) > ,
215 ordered_unique<tag<AssignedIDTag>, BOOST_MULTI_INDEX_MEMBER(QuestionData, int, d_assignedID) >
216 >
217 > qids_t;
218
219 qids_t qids;
220 bool g_throttled;
221
222 unsigned int s_questions, s_origanswers, s_weanswers, s_wetimedout, s_perfect, s_mostly, s_origtimedout;
223 unsigned int s_wenever, s_orignever;
224 unsigned int s_webetter, s_origbetter, s_norecursionavailable;
225 unsigned int s_weunmatched, s_origunmatched;
226 unsigned int s_wednserrors, s_origdnserrors, s_duplicates;
227
228
229
230 void WeOrigSlowQueriesDelta(int& weOutstanding, int& origOutstanding, int& weSlow, int& origSlow)
231 {
232 struct timeval now;
233 gettimeofday(&now, 0);
234
235 weOutstanding=origOutstanding=weSlow=origSlow=0;
236
237 for(qids_t::iterator i=qids.begin(); i!=qids.end(); ++i) {
238 double dt=DiffTime(i->d_resentTime, now);
239 if(dt < 2.0) {
240 if(i->d_newRcode == -1)
241 weOutstanding++;
242 if(i->d_origRcode == -1)
243 origOutstanding++;
244 }
245 else {
246 if(i->d_newRcode == -1) {
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 }
255 }
256 if(i->d_origRcode == -1) {
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 }
265 }
266 }
267 }
268 }
269
270 void compactAnswerSet(MOADNSParser::answers_t orig, set<DNSRecord>& compacted)
271 {
272 for(MOADNSParser::answers_t::const_iterator i=orig.begin(); i != orig.end(); ++i)
273 if(i->first.d_place==DNSResourceRecord::ANSWER)
274 compacted.insert(i->first);
275 }
276
277 bool isRcodeOk(int rcode)
278 {
279 return rcode==0 || rcode==3;
280 }
281
282 set<pair<DNSName,uint16_t> > s_origbetterset;
283
284 bool 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) {
291 // cerr<<(int)iter->first.d_place<<", "<<iter->first.d_name<<" "<<iter->first.d_type<<", # "<<answers.size()<<endl;
292 if(iter->first.d_place!=2)
293 ok=false;
294 if(!iter->first.d_name.isRoot() || iter->first.d_type!=QType::NS)
295 ok=false;
296 }
297 return ok;
298 }
299
300 vector<uint32_t> flightTimes;
301 void 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
315 uint64_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
324 void emitFlightTimes()
325 {
326 uint64_t totals = countLessThan(flightTimes.size());
327 unsigned int limits[]={1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 500, 1000, (unsigned int) flightTimes.size()};
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
343 void measureResultAndClean(qids_t::const_iterator iter)
344 {
345 const QuestionData& qd=*iter;
346 accountFlightTime(iter);
347
348 set<DNSRecord> canonicOrig, canonicNew;
349 compactAnswerSet(qd.d_origAnswers, canonicOrig);
350 compactAnswerSet(qd.d_newAnswers, canonicNew);
351
352 if(!g_quiet) {
353 cout<<qd.d_qi<<", orig rcode: "<<qd.d_origRcode<<", ours: "<<qd.d_newRcode;
354 cout<<", "<<canonicOrig.size()<< " vs " << canonicNew.size()<<", perfect: ";
355 }
356
357 if(canonicOrig==canonicNew) {
358 s_perfect++;
359 if(!g_quiet)
360 cout<<"yes\n";
361 }
362 else {
363 if(!g_quiet)
364 cout<<"no\n";
365
366 if(qd.d_norecursionavailable)
367 if(!g_quiet)
368 cout<<"\t* original nameserver did not provide recursion for this question *"<<endl;
369 if(qd.d_origRcode == qd.d_newRcode ) {
370 if(!g_quiet)
371 cout<<"\t* mostly correct *"<<endl;
372 s_mostly++;
373 }
374
375 if(!isRcodeOk(qd.d_origRcode) && isRcodeOk(qd.d_newRcode)) {
376 if(!g_quiet)
377 cout<<"\t* we better *"<<endl;
378 s_webetter++;
379 }
380 if(isRcodeOk(qd.d_origRcode) && !isRcodeOk(qd.d_newRcode) && !isRootReferral(qd.d_origAnswers)) {
381 if(!g_quiet)
382 cout<<"\t* orig better *"<<endl;
383 s_origbetter++;
384 if(!g_quiet)
385 if(s_origbetterset.insert(make_pair(qd.d_qi.d_qname, qd.d_qi.d_qtype)).second) {
386 cout<<"orig better: " << qd.d_qi.d_qname<<" "<< qd.d_qi.d_qtype<<endl;
387 }
388 }
389
390 if(!g_quiet) {
391 cout<<"orig: rcode="<<qd.d_origRcode<<"\n";
392 for(set<DNSRecord>::const_iterator i=canonicOrig.begin(); i!=canonicOrig.end(); ++i)
393 cout<<"\t"<<i->d_name<<"\t"<<DNSRecordContent::NumberToType(i->d_type)<<"\t'" << (i->d_content ? i->d_content->getZoneRepresentation() : "") <<"'\n";
394 cout<<"new: rcode="<<qd.d_newRcode<<"\n";
395 for(set<DNSRecord>::const_iterator i=canonicNew.begin(); i!=canonicNew.end(); ++i)
396 cout<<"\t"<<i->d_name<<"\t"<<DNSRecordContent::NumberToType(i->d_type)<<"\t'" << (i->d_content ? i->d_content->getZoneRepresentation() : "") <<"'\n";
397 cout<<"\n";
398 cout<<"-\n";
399
400 }
401 }
402
403 int releaseID=qd.d_assignedID;
404 qids.erase(iter); // qd invalid now
405 s_idmanager.releaseID(releaseID);
406 }
407
408
409 Socket *s_socket;
410
411 void receiveFromReference()
412 try
413 {
414 string packet;
415 ComboAddress remote;
416 int res=waitForData(s_socket->getHandle(), g_timeoutMsec/1000, 1000*(g_timeoutMsec%1000));
417
418 if(res < 0 || res==0)
419 return;
420
421 while(s_socket->recvFromAsync(packet, remote)) {
422 try {
423 s_weanswers++;
424 MOADNSParser mdp(false, packet.c_str(), packet.length());
425 if(!mdp.d_header.qr) {
426 cout<<"Received a question from our reference nameserver!"<<endl;
427 continue;
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()) {
434 if(!g_quiet)
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;
436 s_weunmatched++;
437 continue;
438 }
439
440 QuestionData qd=*found; // we have to make a copy because we reinsert below
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) {
445 qids_t::const_iterator iter= qids.project<0>(found);
446 measureResultAndClean(iter);
447 }
448 }
449 catch(MOADNSException &e)
450 {
451 s_wednserrors++;
452 }
453 catch(std::out_of_range &e)
454 {
455 s_wednserrors++;
456 }
457 catch(std::exception& e)
458 {
459 s_wednserrors++;
460 }
461 }
462 }
463 catch(std::exception& e)
464 {
465 cerr<<"Receiver function died: "<<e.what()<<endl;
466 exit(1);
467 }
468 catch(...)
469 {
470 cerr<<"Receiver function died with unknown exception"<<endl;
471 exit(1);
472 }
473
474 void pruneQids()
475 {
476 struct timeval now;
477 gettimeofday(&now, 0);
478
479 for(qids_t::iterator i=qids.begin(); i!=qids.end(); ) {
480 if(DiffTime(i->d_resentTime, now) < 10)
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) {
488 s_orignever++;
489 }
490
491 qids.erase(i++);
492 }
493 }
494 }
495
496 void printStats(uint64_t origWaitingFor=0, uint64_t weWaitingFor=0)
497 {
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
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;
513
514 }
515
516 void houseKeeping()
517 {
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);
528
529 if(!g_throttled) {
530 if( weWaitingFor > 1000) {
531 cerr<<"Too many questions ("<<weWaitingFor<<") outstanding, throttling"<<endl;
532 g_throttled=true;
533 }
534 }
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)
547 Orig 9 21 29 36 47 57 66 72
548
549
550 */
551
552 printStats(origWaitingFor, weWaitingFor);
553 pruneQids();
554 }
555
556
557 bool g_rdSelector;
558
559 static 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
577 static 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
607 bool sendPacketFromPR(PcapPacketReader& pr, const ComboAddress& remote, int stamp)
608 {
609 dnsheader* dh=(dnsheader*)pr.d_payload;
610 bool sent=false;
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;
613
614 QuestionData qd;
615 try {
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
618 s_questions++;
619 qd.d_assignedID = s_idmanager.getID();
620 uint16_t tmp=dh->id;
621 dh->id=htons(qd.d_assignedID);
622 // dh->rd=1; // useful to replay traffic to auths to a recursor
623 uint16_t dlen = pr.d_len;
624
625 if (stamp >= 0) addECSOption((char*)pr.d_payload, 1500, &dlen, pr.getSource(), stamp);
626 pr.d_len=dlen;
627 s_socket->sendTo((const char*)pr.d_payload, dlen, remote);
628 sent=true;
629 dh->id=tmp;
630 }
631 MOADNSParser mdp(false, (const char*)pr.d_payload, pr.d_len);
632 QuestionIdentifier qi=QuestionIdentifier::create(pr.getSource(), pr.getDest(), mdp);
633
634 if(!mdp.d_header.qr) {
635
636 if(qids.count(qi)) {
637 if(!g_quiet)
638 cout<<"Saw an exact duplicate question in PCAP "<<qi<< endl;
639 s_duplicates++;
640 s_idmanager.releaseID(qd.d_assignedID); // release = puts at back of pool
641 return sent;
642 }
643 // new question - ID assigned above already
644 qd.d_qi=qi;
645 gettimeofday(&qd.d_resentTime,0);
646 qids.insert(qd);
647 }
648 else {
649 s_origanswers++;
650 qids_t::const_iterator iter=qids.find(qi);
651 if(iter != qids.end()) {
652 QuestionData qd=*iter;
653 qd.d_origAnswers=mdp.d_answers;
654 qd.d_origRcode=mdp.d_header.rcode;
655
656 if(!dh->ra) {
657 s_norecursionavailable++;
658 qd.d_norecursionavailable=true;
659 }
660 qids.replace(iter, qd);
661
662 if(qd.d_newRcode!=-1) {
663 measureResultAndClean(iter);
664 }
665
666 return sent;
667 }
668 else {
669 s_origunmatched++;
670 if(!g_quiet)
671 cout<<"Unmatched original answer "<<qi<<endl;
672 }
673 }
674 }
675 catch(MOADNSException &e)
676 {
677 if(!g_quiet)
678 cerr<<"Error parsing packet: "<<e.what()<<endl;
679 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
680 s_origdnserrors++;
681 }
682 catch(std::exception &e)
683 {
684 if(!g_quiet)
685 cerr<<"Error parsing packet: "<<e.what()<<endl;
686
687 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
688 s_origdnserrors++;
689 }
690
691 return sent;
692 }
693
694 void usage(po::options_description &desc) {
695 cerr << "Usage: dnsreplay [OPTIONS] FILENAME [IP-ADDRESS] [PORT]"<<endl;
696 cerr << desc << "\n";
697 }
698
699 int main(int argc, char** argv)
700 try
701 {
702 po::options_description desc("Allowed options");
703 desc.add_options()
704 ("help,h", "produce help message")
705 ("version", "show version number")
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)")
709 ("speedup", po::value<float>()->default_value(1), "replay at this speedup")
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")
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");
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);
731
732 reportAllTypes();
733
734 if (g_vm.count("help")) {
735 usage(desc);
736 return EXIT_SUCCESS;
737 }
738
739 if (g_vm.count("version")) {
740 cerr<<"dnsreplay "<<VERSION<<endl;
741 return EXIT_SUCCESS;
742 }
743
744 if(!g_vm.count("pcap-source")) {
745 cerr<<"Fatal, need to specify at least a PCAP source file"<<endl;
746 usage(desc);
747 return EXIT_FAILURE;
748 }
749
750 uint32_t packetLimit = g_vm["packet-limit"].as<uint32_t>();
751
752 g_rdSelector = g_vm["recursive"].as<bool>();
753
754 g_quiet = g_vm["quiet"].as<bool>();
755
756 signal(SIGINT, pleaseQuitHandler);
757 float speedup=g_vm["speedup"].as<float>();
758 g_timeoutMsec=g_vm["timeout-msec"].as<uint32_t>();
759
760 PcapPacketReader pr(g_vm["pcap-source"].as<string>());
761 s_socket= new Socket(AF_INET, SOCK_DGRAM);
762
763 s_socket->setNonBlocking();
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
768 setSocketReceiveBuffer(s_socket->getHandle(), 2000000);
769 setSocketSendBuffer(s_socket->getHandle(), 2000000);
770
771 ComboAddress remote(g_vm["target-ip"].as<string>(),
772 g_vm["target-port"].as<uint16_t>());
773
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
778 cerr<<"Replaying packets to: '"<<g_vm["target-ip"].as<string>()<<"', port "<<g_vm["target-port"].as<uint16_t>()<<endl;
779
780 unsigned int once=0;
781 struct timeval mental_time;
782 mental_time.tv_sec=0; mental_time.tv_usec=0;
783
784 if(!pr.getUDPPacket()) // we do this here so we error out more cleanly on no packets
785 return 0;
786 unsigned int count=0;
787 bool first = true;
788 for(;;) {
789 if(g_pleaseQuit) {
790 cerr<<"Interrupted from terminal"<<endl;
791 break;
792 }
793 if(!((once++)%100))
794 houseKeeping();
795
796 struct timeval packet_ts;
797 packet_ts.tv_sec = 0;
798 packet_ts.tv_usec = 0;
799
800 while(packet_ts < mental_time) {
801 if(!first && !pr.getUDPPacket()) // otherwise we miss the first packet
802 goto out;
803 first=false;
804
805 packet_ts.tv_sec = pr.d_pheader.ts.tv_sec;
806 packet_ts.tv_usec = pr.d_pheader.ts.tv_usec;
807
808 if(sendPacketFromPR(pr, remote, stamp))
809 count++;
810 }
811 if(packetLimit && count >= packetLimit)
812 break;
813
814 mental_time=packet_ts;
815 struct timeval then, now;
816 gettimeofday(&then,0);
817
818 receiveFromReference();
819
820 gettimeofday(&now, 0);
821
822 mental_time= mental_time + speedup * (now-then);
823 }
824 out:;
825 sleep(1);
826 receiveFromReference();
827 printStats();
828 emitFlightTimes();
829 }
830 catch(std::exception& e)
831 {
832 cerr<<"Fatal: "<<e.what()<<endl;
833 }
834