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