]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/dnsreplay.cc
Merge pull request #6100 from pieterlexis/ipv4-ipv6-equiv
[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 static void generateOptRR(const std::string& optRData, string& res)
557 {
558 const uint8_t name = 0;
559 dnsrecordheader dh;
560 EDNS0Record edns0;
561 edns0.extRCode = 0;
562 edns0.version = 0;
563 edns0.Z = 0;
564
565 dh.d_type = htons(QType::OPT);
566 dh.d_class = htons(1280);
567 memcpy(&dh.d_ttl, &edns0, sizeof edns0);
568 dh.d_clen = htons((uint16_t) optRData.length());
569 res.assign((const char *) &name, sizeof name);
570 res.append((const char *) &dh, sizeof dh);
571 res.append(optRData.c_str(), optRData.length());
572 }
573
574 static void addECSOption(char* packet, const size_t& packetSize, uint16_t* len, const ComboAddress& remote, int stamp)
575 {
576 string EDNSRR;
577 struct dnsheader* dh = (struct dnsheader*) packet;
578
579 EDNSSubnetOpts eso;
580 if(stamp < 0)
581 eso.source = Netmask(remote);
582 else {
583 ComboAddress stamped(remote);
584 *((char*)&stamped.sin4.sin_addr.s_addr)=stamp;
585 eso.source = Netmask(stamped);
586 }
587 string optRData=makeEDNSSubnetOptsString(eso);
588 string record;
589 generateEDNSOption(EDNSOptionCode::ECS, optRData, record);
590 generateOptRR(record, EDNSRR);
591
592
593 uint16_t arcount = ntohs(dh->arcount);
594 /* does it fit in the existing buffer? */
595 if (packetSize - *len > EDNSRR.size()) {
596 arcount++;
597 dh->arcount = htons(arcount);
598 memcpy(packet + *len, EDNSRR.c_str(), EDNSRR.size());
599 *len += EDNSRR.size();
600 }
601 }
602
603 static bool g_rdSelector;
604 static uint16_t g_pcapDnsPort;
605
606 static bool sendPacketFromPR(PcapPacketReader& pr, const ComboAddress& remote, int stamp)
607 {
608 dnsheader* dh=(dnsheader*)pr.d_payload;
609 bool sent=false;
610 if((ntohs(pr.d_udp->uh_dport)!=g_pcapDnsPort && ntohs(pr.d_udp->uh_sport)!=g_pcapDnsPort) || dh->rd != g_rdSelector || (unsigned int)pr.d_len <= sizeof(dnsheader))
611 return sent;
612
613 QuestionData qd;
614 try {
615 // yes, we send out ALWAYS. Even if we don't do anything with it later,
616 if(!dh->qr) { // this is to stress out the reference server with all the pain
617 s_questions++;
618 qd.d_assignedID = s_idmanager.getID();
619 uint16_t tmp=dh->id;
620 dh->id=htons(qd.d_assignedID);
621 // dh->rd=1; // useful to replay traffic to auths to a recursor
622 uint16_t dlen = pr.d_len;
623
624 if (stamp >= 0) addECSOption((char*)pr.d_payload, 1500, &dlen, pr.getSource(), stamp);
625 pr.d_len=dlen;
626 s_socket->sendTo((const char*)pr.d_payload, dlen, remote);
627 sent=true;
628 dh->id=tmp;
629 }
630 MOADNSParser mdp(false, (const char*)pr.d_payload, pr.d_len);
631 QuestionIdentifier qi=QuestionIdentifier::create(pr.getSource(), pr.getDest(), mdp);
632
633 if(!mdp.d_header.qr) {
634
635 if(qids.count(qi)) {
636 if(!g_quiet)
637 cout<<"Saw an exact duplicate question in PCAP "<<qi<< endl;
638 s_duplicates++;
639 s_idmanager.releaseID(qd.d_assignedID); // release = puts at back of pool
640 return sent;
641 }
642 // new question - ID assigned above already
643 qd.d_qi=qi;
644 gettimeofday(&qd.d_resentTime,0);
645 qids.insert(qd);
646 }
647 else {
648 s_origanswers++;
649 qids_t::const_iterator iter=qids.find(qi);
650 if(iter != qids.end()) {
651 QuestionData eqd=*iter;
652 eqd.d_origAnswers=mdp.d_answers;
653 eqd.d_origRcode=mdp.d_header.rcode;
654
655 if(!dh->ra) {
656 s_norecursionavailable++;
657 eqd.d_norecursionavailable=true;
658 }
659 qids.replace(iter, eqd);
660
661 if(eqd.d_newRcode!=-1) {
662 measureResultAndClean(iter);
663 }
664
665 return sent;
666 }
667 else {
668 s_origunmatched++;
669 if(!g_quiet)
670 cout<<"Unmatched original answer "<<qi<<endl;
671 }
672 }
673 }
674 catch(MOADNSException &e)
675 {
676 if(!g_quiet)
677 cerr<<"Error parsing packet: "<<e.what()<<endl;
678 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
679 s_origdnserrors++;
680 }
681 catch(std::exception &e)
682 {
683 if(!g_quiet)
684 cerr<<"Error parsing packet: "<<e.what()<<endl;
685
686 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
687 s_origdnserrors++;
688 }
689
690 return sent;
691 }
692
693 void usage(po::options_description &desc) {
694 cerr << "Usage: dnsreplay [OPTIONS] FILENAME [IP-ADDRESS] [PORT]"<<endl;
695 cerr << desc << "\n";
696 }
697
698 int main(int argc, char** argv)
699 try
700 {
701 po::options_description desc("Allowed options");
702 desc.add_options()
703 ("help,h", "produce help message")
704 ("version", "show version number")
705 ("packet-limit", po::value<uint32_t>()->default_value(0), "stop after this many packets")
706 ("pcap-dns-port", po::value<uint16_t>()->default_value(53), "look at packets from or to this port in the PCAP (defaults to 53)")
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 g_pcapDnsPort = g_vm["pcap-dns-port"].as<uint16_t>();
754
755 g_quiet = g_vm["quiet"].as<bool>();
756
757 signal(SIGINT, pleaseQuitHandler);
758 float speedup=g_vm["speedup"].as<float>();
759 g_timeoutMsec=g_vm["timeout-msec"].as<uint32_t>();
760
761 PcapPacketReader pr(g_vm["pcap-source"].as<string>());
762 s_socket= new Socket(AF_INET, SOCK_DGRAM);
763
764 s_socket->setNonBlocking();
765
766 if(g_vm.count("source-ip") && !g_vm["source-ip"].as<string>().empty())
767 s_socket->bind(ComboAddress(g_vm["source-ip"].as<string>(), g_vm["source-port"].as<uint16_t>()));
768
769 setSocketReceiveBuffer(s_socket->getHandle(), 2000000);
770 setSocketSendBuffer(s_socket->getHandle(), 2000000);
771
772 ComboAddress remote(g_vm["target-ip"].as<string>(),
773 g_vm["target-port"].as<uint16_t>());
774
775 int stamp = -1;
776 if(g_vm.count("ecs-stamp") && g_vm.count("ecs-mask"))
777 stamp=g_vm["ecs-mask"].as<uint16_t>();
778
779 cerr<<"Replaying packets to: '"<<g_vm["target-ip"].as<string>()<<"', port "<<g_vm["target-port"].as<uint16_t>()<<endl;
780
781 unsigned int once=0;
782 struct timeval mental_time;
783 mental_time.tv_sec=0; mental_time.tv_usec=0;
784
785 if(!pr.getUDPPacket()) // we do this here so we error out more cleanly on no packets
786 return 0;
787 unsigned int count=0;
788 bool first = true;
789 for(;;) {
790 if(g_pleaseQuit) {
791 cerr<<"Interrupted from terminal"<<endl;
792 break;
793 }
794 if(!((once++)%100))
795 houseKeeping();
796
797 struct timeval packet_ts;
798 packet_ts.tv_sec = 0;
799 packet_ts.tv_usec = 0;
800
801 while(packet_ts < mental_time) {
802 if(!first && !pr.getUDPPacket()) // otherwise we miss the first packet
803 goto out;
804 first=false;
805
806 packet_ts.tv_sec = pr.d_pheader.ts.tv_sec;
807 packet_ts.tv_usec = pr.d_pheader.ts.tv_usec;
808
809 if(sendPacketFromPR(pr, remote, stamp))
810 count++;
811 }
812 if(packetLimit && count >= packetLimit)
813 break;
814
815 mental_time=packet_ts;
816 struct timeval then, now;
817 gettimeofday(&then,0);
818
819 receiveFromReference();
820
821 gettimeofday(&now, 0);
822
823 mental_time= mental_time + speedup * (now-then);
824 }
825 out:;
826 sleep(1);
827 receiveFromReference();
828 printStats();
829 emitFlightTimes();
830 }
831 catch(std::exception& e)
832 {
833 cerr<<"Fatal: "<<e.what()<<endl;
834 }
835