]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsreplay.cc
b2b-migrate did not open a transaction, breaking it for lmdb
[thirdparty/pdns.git] / pdns / dnsreplay.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
60f851c0
BH
22/**
23Replay all recursion-desired DNS questions to a specified IP address.
24
25Track all outgoing questions, remap id to one of ours.
26Also track all recorded answers, and map them to that same id, the 'expectation'.
27
6fb6f693 28When we see a question, parse it, give it a QuestionIdentifier, and and an id from the free-id list.
60f851c0
BH
29
30When we see an answer in the tcpdump, parse it, make QI, and add it to the original QI
31 and check
32
33When we see an answer from the socket, use the id to match it up to the original QI
34 and check
35
36There is one central object, which has (when complete)
37 our assigned id
38 QI
39 Original answer
40 Socket answer
41
42What to do with timeouts. We keep around at most 65536 outstanding answers.
43*/
44
192d405b
BH
45/*
46 mental_clock=0;
47 for(;;) {
48
49 do {
50 read a packet
51 send a packet
52 } while(time_of_last_packet_sent < mental_clock)
53 mental_clock=time_of_last_packet_sent;
54
55 wait for a response packet for 0.1 seconds
56 note how much time has passed
57 mental_clock+=time_passed;
58 }
59
60 */
61
870a0fe4
AT
62#ifdef HAVE_CONFIG_H
63#include "config.h"
64#endif
60f851c0
BH
65#include <bitset>
66#include "statbag.hh"
67#include "dnspcap.hh"
68#include "sstuff.hh"
69#include "anadns.hh"
b9f65b4f 70#include <boost/program_options.hpp>
520d87b9 71#include "dnsrecords.hh"
976ec823 72#include "ednssubnet.hh"
73#include "ednsoptions.hh"
f9f05db4 74
60f851c0
BH
75#include <set>
76#include <deque>
f9f05db4 77
efa1eb45 78#include <boost/format.hpp>
60f851c0
BH
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
61b26744 84#include "namespaces.hh"
60f851c0 85using namespace ::boost::multi_index;
10f4eea8 86#include "namespaces.hh"
60f851c0
BH
87
88StatBag S;
f751bd2d 89bool g_quiet=true;
2a8fb096 90int g_timeoutMsec=0;
60f851c0 91
b9f65b4f
BH
92namespace po = boost::program_options;
93
94po::variables_map g_vm;
192d405b 95
520d87b9 96const struct timeval operator*(float fact, const struct timeval& rhs)
192d405b
BH
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
13f508a5 107 ret.tv_usec=(unsigned int)(1000000*d);
192d405b
BH
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
1451ac8a 123bool g_pleaseQuit;
124void pleaseQuitHandler(int)
125{
126 g_pleaseQuit=true;
127}
192d405b 128
60f851c0
BH
129class DNSIDManager : public boost::noncopyable
130{
131public:
132 DNSIDManager()
133 {
134 for(unsigned int i=0; i < 65536; ++i)
135 d_available.push_back(i);
60f851c0
BH
136 }
137
c9980579 138 uint16_t peakID()
60f851c0
BH
139 {
140 uint16_t ret;
141 if(!d_available.empty()) {
142 ret=d_available.front();
60f851c0
BH
143 return ret;
144 }
145 else
192d405b 146 throw runtime_error("out of ids!"); // XXX FIXME
60f851c0
BH
147 }
148
c9980579
BH
149 uint16_t getID()
150 {
151 uint16_t ret=peakID();
152 d_available.pop_front();
153 return ret;
154 }
155
60f851c0
BH
156 void releaseID(uint16_t id)
157 {
158 d_available.push_back(id);
159 }
160
161private:
162 deque<uint16_t> d_available;
163
164} s_idmanager;
165
166
1451ac8a 167void 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<<": "<<strerror(errno)<<endl;
179}
180
181static void setSocketReceiveBuffer(int fd, uint32_t size)
182{
183 setSocketBuffer(fd, SO_RCVBUF, size);
184}
185
186static void setSocketSendBuffer(int fd, uint32_t size)
187{
188 setSocketBuffer(fd, SO_SNDBUF, size);
189}
190
191
60f851c0
BH
192struct AssignedIDTag{};
193struct QuestionTag{};
194
195struct 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
209typedef multi_index_container<
210 QuestionData,
211 indexed_by<
212 ordered_unique<tag<QuestionTag>, BOOST_MULTI_INDEX_MEMBER(QuestionData, QuestionIdentifier, d_qi) > ,
4957a608 213 ordered_unique<tag<AssignedIDTag>, BOOST_MULTI_INDEX_MEMBER(QuestionData, int, d_assignedID) >
60f851c0
BH
214 >
215> qids_t;
232f0877 216
60f851c0 217qids_t qids;
efa1eb45
BH
218bool g_throttled;
219
220unsigned int s_questions, s_origanswers, s_weanswers, s_wetimedout, s_perfect, s_mostly, s_origtimedout;
60f851c0
BH
221unsigned int s_wenever, s_orignever;
222unsigned int s_webetter, s_origbetter, s_norecursionavailable;
223unsigned int s_weunmatched, s_origunmatched;
b6ca86bd 224unsigned int s_wednserrors, s_origdnserrors, s_duplicates;
60f851c0 225
60f851c0
BH
226
227
efa1eb45 228void WeOrigSlowQueriesDelta(int& weOutstanding, int& origOutstanding, int& weSlow, int& origSlow)
60f851c0
BH
229{
230 struct timeval now;
231 gettimeofday(&now, 0);
232
efa1eb45
BH
233 weOutstanding=origOutstanding=weSlow=origSlow=0;
234
60f851c0
BH
235 for(qids_t::iterator i=qids.begin(); i!=qids.end(); ++i) {
236 double dt=DiffTime(i->d_resentTime, now);
efa1eb45
BH
237 if(dt < 2.0) {
238 if(i->d_newRcode == -1)
4957a608 239 weOutstanding++;
efa1eb45 240 if(i->d_origRcode == -1)
4957a608 241 origOutstanding++;
efa1eb45
BH
242 }
243 else {
60f851c0 244 if(i->d_newRcode == -1) {
4957a608
BH
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 }
60f851c0
BH
253 }
254 if(i->d_origRcode == -1) {
4957a608
BH
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 }
60f851c0
BH
263 }
264 }
265 }
60f851c0
BH
266}
267
268void compactAnswerSet(MOADNSParser::answers_t orig, set<DNSRecord>& compacted)
269{
270 for(MOADNSParser::answers_t::const_iterator i=orig.begin(); i != orig.end(); ++i)
e693ff5a 271 if(i->first.d_place==DNSResourceRecord::ANSWER)
60f851c0
BH
272 compacted.insert(i->first);
273}
274
275bool isRcodeOk(int rcode)
276{
277 return rcode==0 || rcode==3;
278}
279
7ae71e06 280set<pair<DNSName,uint16_t> > s_origbetterset;
60f851c0 281
13f508a5
BH
282bool 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) {
f809c028 289 // cerr<<(int)iter->first.d_place<<", "<<iter->first.d_name<<" "<<iter->first.d_type<<", # "<<answers.size()<<endl;
13f508a5
BH
290 if(iter->first.d_place!=2)
291 ok=false;
f809c028 292 if(!iter->first.d_name.isRoot() || iter->first.d_type!=QType::NS)
13f508a5
BH
293 ok=false;
294 }
295 return ok;
296}
297
1451ac8a 298vector<uint32_t> flightTimes;
299void 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
313uint64_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
322void emitFlightTimes()
d28b71ce 323{
1451ac8a 324 uint64_t totals = countLessThan(flightTimes.size());
60efa636 325 unsigned int limits[]={1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 500, 1000, (unsigned int) flightTimes.size()};
1451ac8a 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
341void measureResultAndClean(qids_t::const_iterator iter)
342{
343 const QuestionData& qd=*iter;
344 accountFlightTime(iter);
60f851c0
BH
345
346 set<DNSRecord> canonicOrig, canonicNew;
347 compactAnswerSet(qd.d_origAnswers, canonicOrig);
348 compactAnswerSet(qd.d_newAnswers, canonicNew);
4957a608 349
f751bd2d 350 if(!g_quiet) {
1451ac8a 351 cout<<qd.d_qi<<", orig rcode: "<<qd.d_origRcode<<", ours: "<<qd.d_newRcode;
60f851c0
BH
352 cout<<", "<<canonicOrig.size()<< " vs " << canonicNew.size()<<", perfect: ";
353 }
354
355 if(canonicOrig==canonicNew) {
356 s_perfect++;
f751bd2d 357 if(!g_quiet)
60f851c0
BH
358 cout<<"yes\n";
359 }
360 else {
f751bd2d 361 if(!g_quiet)
60f851c0
BH
362 cout<<"no\n";
363
364 if(qd.d_norecursionavailable)
f751bd2d 365 if(!g_quiet)
4957a608 366 cout<<"\t* original nameserver did not provide recursion for this question *"<<endl;
60f851c0 367 if(qd.d_origRcode == qd.d_newRcode ) {
f751bd2d 368 if(!g_quiet)
4957a608 369 cout<<"\t* mostly correct *"<<endl;
60f851c0
BH
370 s_mostly++;
371 }
372
373 if(!isRcodeOk(qd.d_origRcode) && isRcodeOk(qd.d_newRcode)) {
f751bd2d 374 if(!g_quiet)
4957a608 375 cout<<"\t* we better *"<<endl;
60f851c0
BH
376 s_webetter++;
377 }
13f508a5 378 if(isRcodeOk(qd.d_origRcode) && !isRcodeOk(qd.d_newRcode) && !isRootReferral(qd.d_origAnswers)) {
f751bd2d 379 if(!g_quiet)
4957a608 380 cout<<"\t* orig better *"<<endl;
60f851c0 381 s_origbetter++;
f751bd2d 382 if(!g_quiet)
1451ac8a 383 if(s_origbetterset.insert(make_pair(qd.d_qi.d_qname, qd.d_qi.d_qtype)).second) {
7f2ebc5d 384 cout<<"orig better: " << qd.d_qi.d_qname<<" "<< qd.d_qi.d_qtype<<endl;
4957a608 385 }
60f851c0
BH
386 }
387
f751bd2d 388 if(!g_quiet) {
520d87b9 389 cout<<"orig: rcode="<<qd.d_origRcode<<"\n";
60f851c0 390 for(set<DNSRecord>::const_iterator i=canonicOrig.begin(); i!=canonicOrig.end(); ++i)
7f2ebc5d 391 cout<<"\t"<<i->d_name<<"\t"<<DNSRecordContent::NumberToType(i->d_type)<<"\t'" << (i->d_content ? i->d_content->getZoneRepresentation() : "") <<"'\n";
520d87b9 392 cout<<"new: rcode="<<qd.d_newRcode<<"\n";
60f851c0 393 for(set<DNSRecord>::const_iterator i=canonicNew.begin(); i!=canonicNew.end(); ++i)
7f2ebc5d 394 cout<<"\t"<<i->d_name<<"\t"<<DNSRecordContent::NumberToType(i->d_type)<<"\t'" << (i->d_content ? i->d_content->getZoneRepresentation() : "") <<"'\n";
60f851c0 395 cout<<"\n";
520d87b9
BH
396 cout<<"-\n";
397
60f851c0
BH
398 }
399 }
400
1451ac8a 401 int releaseID=qd.d_assignedID;
402 qids.erase(iter); // qd invalid now
403 s_idmanager.releaseID(releaseID);
60f851c0
BH
404}
405
406
407Socket *s_socket;
408
409void receiveFromReference()
410try
411{
412 string packet;
4eb84a82 413 ComboAddress remote;
2a8fb096
BH
414 int res=waitForData(s_socket->getHandle(), g_timeoutMsec/1000, 1000*(g_timeoutMsec%1000));
415
192d405b
BH
416 if(res < 0 || res==0)
417 return;
418
60f851c0
BH
419 while(s_socket->recvFromAsync(packet, remote)) {
420 try {
efa1eb45 421 s_weanswers++;
27c0050c 422 MOADNSParser mdp(false, packet.c_str(), packet.length());
60f851c0 423 if(!mdp.d_header.qr) {
4957a608
BH
424 cout<<"Received a question from our reference nameserver!"<<endl;
425 continue;
60f851c0
BH
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()) {
c6b30b4f 432 if(!g_quiet)
7f2ebc5d 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;
4957a608
BH
434 s_weunmatched++;
435 continue;
60f851c0 436 }
1451ac8a 437
438 QuestionData qd=*found; // we have to make a copy because we reinsert below
60f851c0
BH
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) {
1451ac8a 443 qids_t::const_iterator iter= qids.project<0>(found);
444 measureResultAndClean(iter);
60f851c0
BH
445 }
446 }
16ce7f18 447 catch(const MOADNSException &mde)
60f851c0
BH
448 {
449 s_wednserrors++;
450 }
10f4eea8 451 catch(std::out_of_range &e)
60f851c0
BH
452 {
453 s_wednserrors++;
454 }
2b2ce344 455 catch(std::exception& e)
456 {
457 s_wednserrors++;
458 }
60f851c0
BH
459 }
460}
adc10f99 461catch(std::exception& e)
60f851c0 462{
1451ac8a 463 cerr<<"Receiver function died: "<<e.what()<<endl;
60f851c0
BH
464 exit(1);
465}
466catch(...)
467{
1451ac8a 468 cerr<<"Receiver function died with unknown exception"<<endl;
60f851c0
BH
469 exit(1);
470}
471
472void pruneQids()
473{
474 struct timeval now;
475 gettimeofday(&now, 0);
476
477 for(qids_t::iterator i=qids.begin(); i!=qids.end(); ) {
a1e7d16d 478 if(DiffTime(i->d_resentTime, now) < 10)
60f851c0
BH
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) {
4957a608 486 s_orignever++;
60f851c0
BH
487 }
488
489 qids.erase(i++);
490 }
491 }
492}
493
27b014bc
BH
494void printStats(uint64_t origWaitingFor=0, uint64_t weWaitingFor=0)
495{
27b014bc
BH
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
27b014bc
BH
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;
d28b71ce 511
27b014bc
BH
512}
513
60f851c0
BH
514void houseKeeping()
515{
efa1eb45
BH
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);
60f851c0 526
efa1eb45
BH
527 if(!g_throttled) {
528 if( weWaitingFor > 1000) {
529 cerr<<"Too many questions ("<<weWaitingFor<<") outstanding, throttling"<<endl;
530 g_throttled=true;
531 }
60f851c0 532 }
efa1eb45
BH
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)
545Orig 9 21 29 36 47 57 66 72
546
547
548 */
549
27b014bc 550 printStats(origWaitingFor, weWaitingFor);
60f851c0 551 pruneQids();
60f851c0
BH
552}
553
976ec823 554static 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;
d6c335ab 561 edns0.extFlags = 0;
976ec823 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
fe92c902 572static void addECSOption(char* packet, const size_t packetSize, uint16_t* len, const ComboAddress& remote, int stamp)
976ec823 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? */
fe92c902 593 if (packetSize > *len && (packetSize - *len) > EDNSRR.size()) {
976ec823 594 arcount++;
595 dh->arcount = htons(arcount);
596 memcpy(packet + *len, EDNSRR.c_str(), EDNSRR.size());
597 *len += EDNSRR.size();
598 }
599}
600
cb07837f
RG
601static bool g_rdSelector;
602static uint16_t g_pcapDnsPort;
976ec823 603
cb07837f 604static bool sendPacketFromPR(PcapPacketReader& pr, const ComboAddress& remote, int stamp)
60f851c0 605{
f751bd2d 606 bool sent=false;
fe92c902
RG
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)
f751bd2d 616 return sent;
13f508a5 617
c9980579 618 QuestionData qd;
60f851c0 619 try {
1451ac8a 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
bc9e03a0 622 s_questions++;
1451ac8a 623 qd.d_assignedID = s_idmanager.getID();
c9980579
BH
624 uint16_t tmp=dh->id;
625 dh->id=htons(qd.d_assignedID);
590a5b4a 626 // dh->rd=1; // useful to replay traffic to auths to a recursor
976ec823 627 uint16_t dlen = pr.d_len;
628
fe92c902
RG
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
976ec823 639 s_socket->sendTo((const char*)pr.d_payload, dlen, remote);
f751bd2d 640 sent=true;
c9980579
BH
641 dh->id=tmp;
642 }
27c0050c 643 MOADNSParser mdp(false, (const char*)pr.d_payload, pr.d_len);
a5d9353e 644 QuestionIdentifier qi=QuestionIdentifier::create(pr.getSource(), pr.getDest(), mdp);
1451ac8a 645
60f851c0 646 if(!mdp.d_header.qr) {
bc9e03a0 647
60f851c0 648 if(qids.count(qi)) {
4957a608 649 if(!g_quiet)
1451ac8a 650 cout<<"Saw an exact duplicate question in PCAP "<<qi<< endl;
4957a608 651 s_duplicates++;
1451ac8a 652 s_idmanager.releaseID(qd.d_assignedID); // release = puts at back of pool
4957a608 653 return sent;
60f851c0 654 }
1451ac8a 655 // new question - ID assigned above already
60f851c0
BH
656 qd.d_qi=qi;
657 gettimeofday(&qd.d_resentTime,0);
60f851c0 658 qids.insert(qd);
60f851c0
BH
659 }
660 else {
efa1eb45 661 s_origanswers++;
1451ac8a 662 qids_t::const_iterator iter=qids.find(qi);
663 if(iter != qids.end()) {
2010ac95
RG
664 QuestionData eqd=*iter;
665 eqd.d_origAnswers=mdp.d_answers;
666 eqd.d_origRcode=mdp.d_header.rcode;
4957a608
BH
667
668 if(!dh->ra) {
669 s_norecursionavailable++;
2010ac95 670 eqd.d_norecursionavailable=true;
4957a608 671 }
2010ac95 672 qids.replace(iter, eqd);
4957a608 673
2010ac95 674 if(eqd.d_newRcode!=-1) {
1451ac8a 675 measureResultAndClean(iter);
4957a608
BH
676 }
677
678 return sent;
60f851c0
BH
679 }
680 else {
4957a608
BH
681 s_origunmatched++;
682 if(!g_quiet)
683 cout<<"Unmatched original answer "<<qi<<endl;
60f851c0
BH
684 }
685 }
686 }
16ce7f18 687 catch(const MOADNSException &mde)
60f851c0 688 {
976ec823 689 if(!g_quiet)
16ce7f18 690 cerr<<"Error parsing packet: "<<mde.what()<<endl;
1451ac8a 691 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
60f851c0
BH
692 s_origdnserrors++;
693 }
2b2ce344 694 catch(std::exception &e)
60f851c0 695 {
976ec823 696 if(!g_quiet)
697 cerr<<"Error parsing packet: "<<e.what()<<endl;
698
1451ac8a 699 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
bc9e03a0 700 s_origdnserrors++;
60f851c0 701 }
1451ac8a 702
f751bd2d 703 return sent;
60f851c0
BH
704}
705
2a6faa4f
PL
706void usage(po::options_description &desc) {
707 cerr << "Usage: dnsreplay [OPTIONS] FILENAME [IP-ADDRESS] [PORT]"<<endl;
708 cerr << desc << "\n";
709}
710
60f851c0
BH
711int main(int argc, char** argv)
712try
713{
b9f65b4f
BH
714 po::options_description desc("Allowed options");
715 desc.add_options()
716 ("help,h", "produce help message")
2a6faa4f 717 ("version", "show version number")
f751bd2d 718 ("packet-limit", po::value<uint32_t>()->default_value(0), "stop after this many packets")
cb07837f 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)")
f751bd2d
BH
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)")
2a8fb096 722 ("speedup", po::value<float>()->default_value(1), "replay at this speedup")
976ec823 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")
658b9c44
RG
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");
b9f65b4f
BH
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);
f751bd2d 744
520d87b9
BH
745 reportAllTypes();
746
b9f65b4f 747 if (g_vm.count("help")) {
2a6faa4f 748 usage(desc);
b9f65b4f 749 return EXIT_SUCCESS;
60f851c0 750 }
2a6faa4f
PL
751
752 if (g_vm.count("version")) {
753 cerr<<"dnsreplay "<<VERSION<<endl;
754 return EXIT_SUCCESS;
755 }
756
b9f65b4f
BH
757 if(!g_vm.count("pcap-source")) {
758 cerr<<"Fatal, need to specify at least a PCAP source file"<<endl;
2a6faa4f 759 usage(desc);
b9f65b4f
BH
760 return EXIT_FAILURE;
761 }
762
f751bd2d
BH
763 uint32_t packetLimit = g_vm["packet-limit"].as<uint32_t>();
764
765 g_rdSelector = g_vm["recursive"].as<bool>();
cb07837f 766 g_pcapDnsPort = g_vm["pcap-dns-port"].as<uint16_t>();
27b014bc 767
f751bd2d
BH
768 g_quiet = g_vm["quiet"].as<bool>();
769
1451ac8a 770 signal(SIGINT, pleaseQuitHandler);
520d87b9 771 float speedup=g_vm["speedup"].as<float>();
2a8fb096 772 g_timeoutMsec=g_vm["timeout-msec"].as<uint32_t>();
b9f65b4f
BH
773
774 PcapPacketReader pr(g_vm["pcap-source"].as<string>());
34460aee 775 s_socket= new Socket(AF_INET, SOCK_DGRAM);
60f851c0
BH
776
777 s_socket->setNonBlocking();
658b9c44
RG
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
1451ac8a 782 setSocketReceiveBuffer(s_socket->getHandle(), 2000000);
783 setSocketSendBuffer(s_socket->getHandle(), 2000000);
784
4eb84a82 785 ComboAddress remote(g_vm["target-ip"].as<string>(),
232f0877 786 g_vm["target-port"].as<uint16_t>());
60f851c0 787
976ec823 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
b9f65b4f 792 cerr<<"Replaying packets to: '"<<g_vm["target-ip"].as<string>()<<"', port "<<g_vm["target-port"].as<uint16_t>()<<endl;
60f851c0
BH
793
794 unsigned int once=0;
192d405b
BH
795 struct timeval mental_time;
796 mental_time.tv_sec=0; mental_time.tv_usec=0;
797
cc4d4e8f 798 if(!pr.getUDPPacket()) // we do this here so we error out more cleanly on no packets
192d405b 799 return 0;
f751bd2d 800 unsigned int count=0;
bc9e03a0 801 bool first = true;
60f851c0 802 for(;;) {
1451ac8a 803 if(g_pleaseQuit) {
804 cerr<<"Interrupted from terminal"<<endl;
805 break;
806 }
efa1eb45 807 if(!((once++)%100))
60f851c0 808 houseKeeping();
f751bd2d 809
a1e7d16d
BH
810 struct timeval packet_ts;
811 packet_ts.tv_sec = 0;
812 packet_ts.tv_usec = 0;
bc9e03a0 813
a1e7d16d 814 while(packet_ts < mental_time) {
cc4d4e8f 815 if(!first && !pr.getUDPPacket()) // otherwise we miss the first packet
4957a608 816 goto out;
cc4d4e8f 817 first=false;
976ec823 818
a1e7d16d
BH
819 packet_ts.tv_sec = pr.d_pheader.ts.tv_sec;
820 packet_ts.tv_usec = pr.d_pheader.ts.tv_usec;
821
976ec823 822 if(sendPacketFromPR(pr, remote, stamp))
4957a608 823 count++;
192d405b 824 }
3a798afb 825 if(packetLimit && count >= packetLimit)
f751bd2d 826 break;
192d405b 827
a1e7d16d 828 mental_time=packet_ts;
192d405b
BH
829 struct timeval then, now;
830 gettimeofday(&then,0);
60f851c0 831
60f851c0 832 receiveFromReference();
192d405b
BH
833
834 gettimeofday(&now, 0);
835
b9f65b4f 836 mental_time= mental_time + speedup * (now-then);
60f851c0 837 }
192d405b 838 out:;
27b014bc
BH
839 sleep(1);
840 receiveFromReference();
841 printStats();
1451ac8a 842 emitFlightTimes();
60f851c0 843}
adc10f99 844catch(std::exception& e)
60f851c0
BH
845{
846 cerr<<"Fatal: "<<e.what()<<endl;
847}
b9f65b4f 848