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