]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/dnsreplay.cc
rec: mention rust compiler in compiling docs
[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
9912fa43 42What to do with timeouts. We keep around at most 65536 outstanding answers.
60f851c0
BH
43*/
44
9912fa43 45/*
192d405b
BH
46 mental_clock=0;
47 for(;;) {
48
49 do {
50 read a packet
51 send a packet
9912fa43 52 } while(time_of_last_packet_sent < mental_clock)
192d405b
BH
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;
9912fa43 90int g_timeoutMsec=0;
60f851c0 91
b9f65b4f
BH
92namespace po = boost::program_options;
93
94po::variables_map g_vm;
192d405b 95
050e6877 96static const 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 108 normalizeTV(ret);
9912fa43 109
192d405b 110 cout<<"out complex: "<<ret.tv_sec<<" + "<<ret.tv_usec<<"\n";
9912fa43 111
192d405b
BH
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;
050e6877 124static void pleaseQuitHandler(int)
1451ac8a 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:
dc593046 162 std::deque<uint16_t> d_available;
9912fa43 163
60f851c0
BH
164} s_idmanager;
165
60f851c0
BH
166struct AssignedIDTag{};
167struct QuestionTag{};
168
169struct 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;
ed5dfdee 178 struct timeval d_resentTime{};
60f851c0
BH
179 bool d_norecursionavailable;
180 bool d_origlate, d_newlate;
181};
182
183typedef multi_index_container<
9912fa43 184 QuestionData,
60f851c0
BH
185 indexed_by<
186 ordered_unique<tag<QuestionTag>, BOOST_MULTI_INDEX_MEMBER(QuestionData, QuestionIdentifier, d_qi) > ,
4957a608 187 ordered_unique<tag<AssignedIDTag>, BOOST_MULTI_INDEX_MEMBER(QuestionData, int, d_assignedID) >
60f851c0
BH
188 >
189> qids_t;
9912fa43 190
60f851c0 191qids_t qids;
efa1eb45
BH
192bool g_throttled;
193
194unsigned int s_questions, s_origanswers, s_weanswers, s_wetimedout, s_perfect, s_mostly, s_origtimedout;
60f851c0
BH
195unsigned int s_wenever, s_orignever;
196unsigned int s_webetter, s_origbetter, s_norecursionavailable;
197unsigned int s_weunmatched, s_origunmatched;
b6ca86bd 198unsigned int s_wednserrors, s_origdnserrors, s_duplicates;
60f851c0 199
60f851c0
BH
200
201
050e6877 202static void WeOrigSlowQueriesDelta(int& weOutstanding, int& origOutstanding, int& weSlow, int& origSlow)
60f851c0
BH
203{
204 struct timeval now;
205 gettimeofday(&now, 0);
206
efa1eb45
BH
207 weOutstanding=origOutstanding=weSlow=origSlow=0;
208
60f851c0
BH
209 for(qids_t::iterator i=qids.begin(); i!=qids.end(); ++i) {
210 double dt=DiffTime(i->d_resentTime, now);
efa1eb45 211 if(dt < 2.0) {
9912fa43 212 if(i->d_newRcode == -1)
4957a608 213 weOutstanding++;
efa1eb45 214 if(i->d_origRcode == -1)
4957a608 215 origOutstanding++;
efa1eb45
BH
216 }
217 else {
60f851c0 218 if(i->d_newRcode == -1) {
4957a608
BH
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 }
60f851c0
BH
227 }
228 if(i->d_origRcode == -1) {
4957a608
BH
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 }
60f851c0
BH
237 }
238 }
239 }
60f851c0
BH
240}
241
050e6877 242static void compactAnswerSet(MOADNSParser::answers_t orig, set<DNSRecord>& compacted)
60f851c0
BH
243{
244 for(MOADNSParser::answers_t::const_iterator i=orig.begin(); i != orig.end(); ++i)
e693ff5a 245 if(i->first.d_place==DNSResourceRecord::ANSWER)
60f851c0
BH
246 compacted.insert(i->first);
247}
248
050e6877 249static bool isRcodeOk(int rcode)
60f851c0
BH
250{
251 return rcode==0 || rcode==3;
252}
253
7ae71e06 254set<pair<DNSName,uint16_t> > s_origbetterset;
60f851c0 255
050e6877 256static bool isRootReferral(const MOADNSParser::answers_t& answers)
13f508a5
BH
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) {
f809c028 263 // cerr<<(int)iter->first.d_place<<", "<<iter->first.d_name<<" "<<iter->first.d_type<<", # "<<answers.size()<<endl;
13f508a5
BH
264 if(iter->first.d_place!=2)
265 ok=false;
f809c028 266 if(!iter->first.d_name.isRoot() || iter->first.d_type!=QType::NS)
13f508a5
BH
267 ok=false;
268 }
269 return ok;
270}
271
1451ac8a 272vector<uint32_t> flightTimes;
050e6877 273static void accountFlightTime(qids_t::const_iterator iter)
1451ac8a 274{
275 if(flightTimes.empty())
9912fa43 276 flightTimes.resize(2050);
1451ac8a 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
050e6877 287static uint64_t countLessThan(unsigned int msec)
1451ac8a 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
050e6877 296static void emitFlightTimes()
d28b71ce 297{
1451ac8a 298 uint64_t totals = countLessThan(flightTimes.size());
f17c3b1c
OM
299 if (totals == 0) {
300 // Avoid division by zero below
301 totals = 1;
302 }
60efa636 303 unsigned int limits[]={1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 100, 200, 500, 1000, (unsigned int) flightTimes.size()};
1451ac8a 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())
9273c94d 309 cout<<"Within "<<limits[i]<<" ms: ";
9912fa43 310 else
9273c94d 311 cout<<"Beyond "<<limits[i]-2<<" ms: ";
1451ac8a 312 uint64_t here = countLessThan(limits[i]);
313 cout<<100.0*here/totals<<"% ("<<100.0*(here-sofar)/totals<<"%)"<<endl;
314 sofar=here;
9912fa43 315
1451ac8a 316 }
317}
318
050e6877 319static void measureResultAndClean(qids_t::const_iterator iter)
1451ac8a 320{
321 const QuestionData& qd=*iter;
322 accountFlightTime(iter);
60f851c0
BH
323
324 set<DNSRecord> canonicOrig, canonicNew;
325 compactAnswerSet(qd.d_origAnswers, canonicOrig);
326 compactAnswerSet(qd.d_newAnswers, canonicNew);
9912fa43 327
f751bd2d 328 if(!g_quiet) {
9912fa43 329 cout<<qd.d_qi<<", orig rcode: "<<qd.d_origRcode<<", ours: "<<qd.d_newRcode;
60f851c0
BH
330 cout<<", "<<canonicOrig.size()<< " vs " << canonicNew.size()<<", perfect: ";
331 }
332
333 if(canonicOrig==canonicNew) {
334 s_perfect++;
f751bd2d 335 if(!g_quiet)
60f851c0
BH
336 cout<<"yes\n";
337 }
338 else {
f751bd2d 339 if(!g_quiet)
60f851c0 340 cout<<"no\n";
9912fa43 341
60f851c0 342 if(qd.d_norecursionavailable)
f751bd2d 343 if(!g_quiet)
4957a608 344 cout<<"\t* original nameserver did not provide recursion for this question *"<<endl;
60f851c0 345 if(qd.d_origRcode == qd.d_newRcode ) {
f751bd2d 346 if(!g_quiet)
4957a608 347 cout<<"\t* mostly correct *"<<endl;
60f851c0
BH
348 s_mostly++;
349 }
350
351 if(!isRcodeOk(qd.d_origRcode) && isRcodeOk(qd.d_newRcode)) {
f751bd2d 352 if(!g_quiet)
4957a608 353 cout<<"\t* we better *"<<endl;
60f851c0
BH
354 s_webetter++;
355 }
13f508a5 356 if(isRcodeOk(qd.d_origRcode) && !isRcodeOk(qd.d_newRcode) && !isRootReferral(qd.d_origAnswers)) {
f751bd2d 357 if(!g_quiet)
4957a608 358 cout<<"\t* orig better *"<<endl;
60f851c0 359 s_origbetter++;
e32a8d46
RP
360 if (!g_quiet)
361 if (s_origbetterset.emplace(qd.d_qi.d_qname, qd.d_qi.d_qtype).second) {
7f2ebc5d 362 cout<<"orig better: " << qd.d_qi.d_qname<<" "<< qd.d_qi.d_qtype<<endl;
4957a608 363 }
60f851c0
BH
364 }
365
f751bd2d 366 if(!g_quiet) {
520d87b9 367 cout<<"orig: rcode="<<qd.d_origRcode<<"\n";
60f851c0 368 for(set<DNSRecord>::const_iterator i=canonicOrig.begin(); i!=canonicOrig.end(); ++i)
d06dcda4 369 cout<<"\t"<<i->d_name<<"\t"<<DNSRecordContent::NumberToType(i->d_type)<<"\t'" << (i->getContent() ? i->getContent()->getZoneRepresentation() : "") <<"'\n";
520d87b9 370 cout<<"new: rcode="<<qd.d_newRcode<<"\n";
60f851c0 371 for(set<DNSRecord>::const_iterator i=canonicNew.begin(); i!=canonicNew.end(); ++i)
d06dcda4 372 cout<<"\t"<<i->d_name<<"\t"<<DNSRecordContent::NumberToType(i->d_type)<<"\t'" << (i->getContent() ? i->getContent()->getZoneRepresentation() : "") <<"'\n";
60f851c0 373 cout<<"\n";
520d87b9
BH
374 cout<<"-\n";
375
60f851c0
BH
376 }
377 }
9912fa43 378
1451ac8a 379 int releaseID=qd.d_assignedID;
380 qids.erase(iter); // qd invalid now
381 s_idmanager.releaseID(releaseID);
60f851c0
BH
382}
383
384
c2826d2e 385std::unique_ptr<Socket> s_socket = nullptr;
60f851c0 386
050e6877 387static void receiveFromReference()
60f851c0
BH
388try
389{
2e1c76aa 390 PacketBuffer packet;
4eb84a82 391 ComboAddress remote;
2a8fb096 392 int res=waitForData(s_socket->getHandle(), g_timeoutMsec/1000, 1000*(g_timeoutMsec%1000));
9912fa43 393
192d405b
BH
394 if(res < 0 || res==0)
395 return;
396
26dc9fdf 397 while (s_socket->recvFromAsync(packet, remote)) {
60f851c0 398 try {
efa1eb45 399 s_weanswers++;
2e1c76aa
RG
400 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
401 MOADNSParser mdp(false, reinterpret_cast<const char*>(packet.data()), packet.size());
60f851c0 402 if(!mdp.d_header.qr) {
4957a608
BH
403 cout<<"Received a question from our reference nameserver!"<<endl;
404 continue;
60f851c0
BH
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()) {
9912fa43 411 if(!g_quiet)
7f2ebc5d 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;
4957a608
BH
413 s_weunmatched++;
414 continue;
60f851c0 415 }
1451ac8a 416
9912fa43 417 QuestionData qd=*found; // we have to make a copy because we reinsert below
60f851c0
BH
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) {
1451ac8a 422 qids_t::const_iterator iter= qids.project<0>(found);
423 measureResultAndClean(iter);
60f851c0
BH
424 }
425 }
16ce7f18 426 catch(const MOADNSException &mde)
60f851c0
BH
427 {
428 s_wednserrors++;
429 }
10f4eea8 430 catch(std::out_of_range &e)
60f851c0
BH
431 {
432 s_wednserrors++;
433 }
9912fa43 434 catch(std::exception& e)
2b2ce344 435 {
436 s_wednserrors++;
437 }
60f851c0
BH
438 }
439}
adc10f99 440catch(std::exception& e)
60f851c0 441{
1451ac8a 442 cerr<<"Receiver function died: "<<e.what()<<endl;
60f851c0
BH
443 exit(1);
444}
445catch(...)
446{
1451ac8a 447 cerr<<"Receiver function died with unknown exception"<<endl;
60f851c0
BH
448 exit(1);
449}
450
050e6877 451static void pruneQids()
60f851c0
BH
452{
453 struct timeval now;
454 gettimeofday(&now, 0);
455
456 for(qids_t::iterator i=qids.begin(); i!=qids.end(); ) {
a1e7d16d 457 if(DiffTime(i->d_resentTime, now) < 10)
60f851c0
BH
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) {
4957a608 465 s_orignever++;
60f851c0
BH
466 }
467
468 qids.erase(i++);
469 }
470 }
471}
472
050e6877 473static void printStats(uint64_t origWaitingFor=0, uint64_t weWaitingFor=0)
27b014bc 474{
dc593046
OM
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");
27b014bc 477
27b014bc
BH
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;
9912fa43 483
27b014bc
BH
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;
d28b71ce 490
27b014bc
BH
491}
492
050e6877 493static void houseKeeping()
60f851c0 494{
efa1eb45
BH
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);
9912fa43 505
efa1eb45
BH
506 if(!g_throttled) {
507 if( weWaitingFor > 1000) {
508 cerr<<"Too many questions ("<<weWaitingFor<<") outstanding, throttling"<<endl;
509 g_throttled=true;
510 }
60f851c0 511 }
efa1eb45
BH
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)
524Orig 9 21 29 36 47 57 66 72
525
526
527 */
528
27b014bc 529 printStats(origWaitingFor, weWaitingFor);
60f851c0 530 pruneQids();
60f851c0
BH
531}
532
976ec823 533static 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;
d6c335ab 540 edns0.extFlags = 0;
9912fa43 541
976ec823 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
fe92c902 551static void addECSOption(char* packet, const size_t packetSize, uint16_t* len, const ComboAddress& remote, int stamp)
976ec823 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? */
fe92c902 572 if (packetSize > *len && (packetSize - *len) > EDNSRR.size()) {
976ec823 573 arcount++;
574 dh->arcount = htons(arcount);
575 memcpy(packet + *len, EDNSRR.c_str(), EDNSRR.size());
576 *len += EDNSRR.size();
577 }
578}
579
3da5caaf
RG
580static 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
cb07837f
RG
596static bool g_rdSelector;
597static uint16_t g_pcapDnsPort;
976ec823 598
aa87b287 599static bool sendPacketFromPR(PcapPacketReader& pr, const ComboAddress& remote, int stamp, [[maybe_unused]] bool usePCAPSourceIP)
60f851c0 600{
f751bd2d 601 bool sent=false;
fe92c902
RG
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)
f751bd2d 611 return sent;
13f508a5 612
c9980579 613 QuestionData qd;
60f851c0 614 try {
9912fa43 615 // yes, we send out ALWAYS. Even if we don't do anything with it later,
1451ac8a 616 if(!dh->qr) { // this is to stress out the reference server with all the pain
bc9e03a0 617 s_questions++;
1451ac8a 618 qd.d_assignedID = s_idmanager.getID();
c9980579
BH
619 uint16_t tmp=dh->id;
620 dh->id=htons(qd.d_assignedID);
590a5b4a 621 // dh->rd=1; // useful to replay traffic to auths to a recursor
976ec823 622 uint16_t dlen = pr.d_len;
623
fe92c902 624 if (stamp >= 0) {
e6935cd0 625 static_assert(sizeof(pr.d_readbuffer) >= 1500, "The size of the underlying buffer should be at least 1500 bytes");
fe92c902
RG
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 }
29ef4233
RG
633#ifdef IP_TRANSPARENT
634 if (usePCAPSourceIP) {
3da5caaf
RG
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());
29ef4233
RG
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 */
f751bd2d 646 sent=true;
c9980579
BH
647 dh->id=tmp;
648 }
27c0050c 649 MOADNSParser mdp(false, (const char*)pr.d_payload, pr.d_len);
a5d9353e 650 QuestionIdentifier qi=QuestionIdentifier::create(pr.getSource(), pr.getDest(), mdp);
1451ac8a 651
60f851c0 652 if(!mdp.d_header.qr) {
bc9e03a0 653
60f851c0 654 if(qids.count(qi)) {
4957a608 655 if(!g_quiet)
1451ac8a 656 cout<<"Saw an exact duplicate question in PCAP "<<qi<< endl;
4957a608 657 s_duplicates++;
1451ac8a 658 s_idmanager.releaseID(qd.d_assignedID); // release = puts at back of pool
4957a608 659 return sent;
60f851c0 660 }
1451ac8a 661 // new question - ID assigned above already
60f851c0
BH
662 qd.d_qi=qi;
663 gettimeofday(&qd.d_resentTime,0);
60f851c0 664 qids.insert(qd);
60f851c0
BH
665 }
666 else {
efa1eb45 667 s_origanswers++;
9912fa43 668 qids_t::const_iterator iter=qids.find(qi);
1451ac8a 669 if(iter != qids.end()) {
2010ac95
RG
670 QuestionData eqd=*iter;
671 eqd.d_origAnswers=mdp.d_answers;
672 eqd.d_origRcode=mdp.d_header.rcode;
9912fa43 673
4957a608
BH
674 if(!dh->ra) {
675 s_norecursionavailable++;
2010ac95 676 eqd.d_norecursionavailable=true;
4957a608 677 }
2010ac95 678 qids.replace(iter, eqd);
4957a608 679
2010ac95 680 if(eqd.d_newRcode!=-1) {
1451ac8a 681 measureResultAndClean(iter);
4957a608 682 }
9912fa43 683
4957a608 684 return sent;
60f851c0
BH
685 }
686 else {
4957a608
BH
687 s_origunmatched++;
688 if(!g_quiet)
689 cout<<"Unmatched original answer "<<qi<<endl;
60f851c0
BH
690 }
691 }
692 }
3da5caaf
RG
693 catch (const MOADNSException& mde) {
694 if (!g_quiet) {
16ce7f18 695 cerr<<"Error parsing packet: "<<mde.what()<<endl;
3da5caaf 696 }
1451ac8a 697 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
60f851c0
BH
698 s_origdnserrors++;
699 }
3da5caaf
RG
700 catch (std::exception& e) {
701 if (!g_quiet) {
976ec823 702 cerr<<"Error parsing packet: "<<e.what()<<endl;
3da5caaf 703 }
976ec823 704
1451ac8a 705 s_idmanager.releaseID(qd.d_assignedID); // not added to qids for cleanup
9912fa43 706 s_origdnserrors++;
60f851c0 707 }
1451ac8a 708
f751bd2d 709 return sent;
60f851c0
BH
710}
711
050e6877 712static void usage(po::options_description &desc) {
2a6faa4f
PL
713 cerr << "Usage: dnsreplay [OPTIONS] FILENAME [IP-ADDRESS] [PORT]"<<endl;
714 cerr << desc << "\n";
715}
716
60f851c0
BH
717int main(int argc, char** argv)
718try
719{
b9f65b4f
BH
720 po::options_description desc("Allowed options");
721 desc.add_options()
722 ("help,h", "produce help message")
2a6faa4f 723 ("version", "show version number")
f751bd2d 724 ("packet-limit", po::value<uint32_t>()->default_value(0), "stop after this many packets")
cb07837f 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)")
f751bd2d
BH
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)")
2a8fb096 728 ("speedup", po::value<float>()->default_value(1), "replay at this speedup")
976ec823 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")
658b9c44
RG
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")
29ef4233 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)")
658b9c44 734 ("source-port", po::value<uint16_t>()->default_value(0), "Port to send the replayed packet from");
b9f65b4f
BH
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);
f751bd2d 751
520d87b9
BH
752 reportAllTypes();
753
b9f65b4f 754 if (g_vm.count("help")) {
2a6faa4f 755 usage(desc);
b9f65b4f 756 return EXIT_SUCCESS;
60f851c0 757 }
2a6faa4f
PL
758
759 if (g_vm.count("version")) {
760 cerr<<"dnsreplay "<<VERSION<<endl;
761 return EXIT_SUCCESS;
762 }
763
b9f65b4f
BH
764 if(!g_vm.count("pcap-source")) {
765 cerr<<"Fatal, need to specify at least a PCAP source file"<<endl;
2a6faa4f 766 usage(desc);
b9f65b4f
BH
767 return EXIT_FAILURE;
768 }
769
f751bd2d
BH
770 uint32_t packetLimit = g_vm["packet-limit"].as<uint32_t>();
771
772 g_rdSelector = g_vm["recursive"].as<bool>();
cb07837f 773 g_pcapDnsPort = g_vm["pcap-dns-port"].as<uint16_t>();
27b014bc 774
f751bd2d
BH
775 g_quiet = g_vm["quiet"].as<bool>();
776
1451ac8a 777 signal(SIGINT, pleaseQuitHandler);
520d87b9 778 float speedup=g_vm["speedup"].as<float>();
2a8fb096 779 g_timeoutMsec=g_vm["timeout-msec"].as<uint32_t>();
b9f65b4f
BH
780
781 PcapPacketReader pr(g_vm["pcap-source"].as<string>());
29ef4233 782 s_socket = make_unique<Socket>(AF_INET, SOCK_DGRAM);
60f851c0
BH
783
784 s_socket->setNonBlocking();
658b9c44 785
29ef4233 786 if(g_vm.count("source-ip") && !g_vm["source-ip"].as<string>().empty()) {
658b9c44 787 s_socket->bind(ComboAddress(g_vm["source-ip"].as<string>(), g_vm["source-port"].as<uint16_t>()));
29ef4233 788 }
658b9c44 789
f402f388
RG
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 }
1451ac8a 802
9912fa43 803 ComboAddress remote(g_vm["target-ip"].as<string>(),
232f0877 804 g_vm["target-port"].as<uint16_t>());
60f851c0 805
976ec823 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
b9f65b4f 810 cerr<<"Replaying packets to: '"<<g_vm["target-ip"].as<string>()<<"', port "<<g_vm["target-port"].as<uint16_t>()<<endl;
60f851c0 811
29ef4233 812 bool usePCAPSourceIP = g_vm["source-from-pcap"].as<bool>();
3da5caaf 813 if (usePCAPSourceIP && !checkIPTransparentUsability()) {
47a57505 814 cerr << "--source-from-pcap requested but IP_TRANSPARENT support is unavailable or not working properly" << endl;
3da5caaf
RG
815 exit(EXIT_FAILURE);
816 }
817
60f851c0 818 unsigned int once=0;
192d405b
BH
819 struct timeval mental_time;
820 mental_time.tv_sec=0; mental_time.tv_usec=0;
821
cc4d4e8f 822 if(!pr.getUDPPacket()) // we do this here so we error out more cleanly on no packets
192d405b 823 return 0;
f751bd2d 824 unsigned int count=0;
bc9e03a0 825 bool first = true;
60f851c0 826 for(;;) {
1451ac8a 827 if(g_pleaseQuit) {
828 cerr<<"Interrupted from terminal"<<endl;
829 break;
830 }
9912fa43 831 if(!((once++)%100))
60f851c0 832 houseKeeping();
9912fa43 833
a1e7d16d 834 struct timeval packet_ts;
9912fa43
FM
835 packet_ts.tv_sec = 0;
836 packet_ts.tv_usec = 0;
bc9e03a0 837
a1e7d16d 838 while(packet_ts < mental_time) {
cc4d4e8f 839 if(!first && !pr.getUDPPacket()) // otherwise we miss the first packet
4957a608 840 goto out;
cc4d4e8f 841 first=false;
976ec823 842
a1e7d16d
BH
843 packet_ts.tv_sec = pr.d_pheader.ts.tv_sec;
844 packet_ts.tv_usec = pr.d_pheader.ts.tv_usec;
845
29ef4233 846 if (sendPacketFromPR(pr, remote, stamp, usePCAPSourceIP)) {
4957a608 847 count++;
29ef4233 848 }
9912fa43
FM
849 }
850 if(packetLimit && count >= packetLimit)
f751bd2d 851 break;
192d405b 852
a1e7d16d 853 mental_time=packet_ts;
192d405b
BH
854 struct timeval then, now;
855 gettimeofday(&then,0);
60f851c0 856
60f851c0 857 receiveFromReference();
192d405b
BH
858
859 gettimeofday(&now, 0);
860
b9f65b4f 861 mental_time= mental_time + speedup * (now-then);
60f851c0 862 }
192d405b 863 out:;
27b014bc
BH
864 sleep(1);
865 receiveFromReference();
866 printStats();
1451ac8a 867 emitFlightTimes();
60f851c0 868}
adc10f99 869catch(std::exception& e)
60f851c0
BH
870{
871 cerr<<"Fatal: "<<e.what()<<endl;
872}