--- /dev/null
+#ifndef PDNS_ANADNS_HH
+#define PDNS_ANADNS_HH
+#include <boost/tuple/tuple.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
+#include <string>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include "dnsparser.hh"
+
+using namespace boost;
+using namespace std;
+
+struct QuestionIdentifier
+{
+ QuestionIdentifier()
+ {}
+
+ bool operator<(const QuestionIdentifier& rhs) const
+ {
+ return
+ tie(d_sourceip, d_destip, d_sourceport, d_destport, d_qname, d_qtype, d_id) <
+ tie(rhs.d_sourceip, rhs.d_destip, rhs.d_sourceport, rhs.d_destport, rhs.d_qname, rhs.d_qtype, rhs.d_id);
+ }
+
+ // the canonical direction is that of the question
+ static QuestionIdentifier create(const struct iphdr* d_ip, const struct udphdr* d_udp, const MOADNSParser& mdp)
+ {
+ QuestionIdentifier ret;
+ if(mdp.d_header.qr) {
+ ret.d_sourceip=htonl(d_ip->daddr);
+ ret.d_destip=htonl(d_ip->saddr);
+ ret.d_sourceport=htons(d_udp->dest);
+ ret.d_destport=htons(d_udp->source);
+ }
+ else {
+ ret.d_sourceip=htonl(d_ip->saddr);
+ ret.d_destip=htonl(d_ip->daddr);
+ ret.d_sourceport=htons(d_udp->source);
+ ret.d_destport=htons(d_udp->dest);
+ }
+ ret.d_qname=mdp.d_qname;
+ ret.d_qtype=mdp.d_qtype;
+ ret.d_id=mdp.d_header.id;
+ return ret;
+ }
+
+
+ uint32_t d_sourceip;
+ uint32_t d_destip;
+ uint16_t d_sourceport;
+ uint16_t d_destport;
+
+ string d_qname;
+ uint16_t d_qtype;
+ uint16_t d_id;
+
+
+};
+
+inline ostream& operator<<(ostream &s, const QuestionIdentifier& qi)
+{
+ s<< "'"<<qi.d_qname<<"|"<<qi.d_qtype<<"', with id " << qi.d_id <<" from ";
+ u_int32_t rint=qi.d_sourceip;
+
+ s<< (rint>>24 & 0xff)<<".";
+ s<< (rint>>16 & 0xff)<<".";
+ s<< (rint>>8 & 0xff)<<".";
+ s<< (rint & 0xff);
+ s<<":"<<qi.d_sourceport;
+
+ s<<" to ";
+ rint=qi.d_destip;
+ s<< (rint>>24 & 0xff)<<".";
+ s<< (rint>>16 & 0xff)<<".";
+ s<< (rint>>8 & 0xff)<<".";
+ s<< (rint & 0xff);
+ return s<<":"<<qi.d_destport;
+}
+
+
+#endif
int offset=0;
d_qlen=0;
- if(ntohs(d.qdcount)) {
- offset = getq(); // also sets this->qdomain!
- if(offset < 0) {
- // L << Logger::Warning << "Ignoring packet: invalid label in question from "
- // << inet_ntoa(remote.sin_addr) << endl;
- return -1;
- }
- d_qlen=offset+4; // this points to the start of any answers
+ if(!ntohs(d.qdcount)) {
+ L << Logger::Warning << "No question section in packet from " << getRemote() <<", rcode="<<(int)d.rcode<<endl;
+ return -1;
}
+ offset = getq(); // also sets this->qdomain!
+ if(offset < 0) {
+ // L << Logger::Warning << "Ignoring packet: invalid label in question from "
+ // << inet_ntoa(remote.sin_addr) << endl;
+ return -1;
+ }
+ if(qdomain.length() > 255) { // this prevents crap from the rootservers
+ return -1;
+ }
+
+ d_qlen=offset+4; // this points to the start of any answers
+
if((unsigned int)(15+offset)>=stringbuffer.length()) {
L << Logger::Warning << "Ignoring packet: question too short from "<< getRemote()<<", offset "<<
15+offset<<">="<<stringbuffer.length()<<endl;
#include <arpa/nameser.h>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
-
+#include <boost/tuple/tuple.hpp>
+#include <boost/tuple/tuple_comparison.hpp>
namespace {
typedef HEADER dnsheader;
}
u_int16_t d_clen;
enum {Answer, Nameserver, Additional} d_place;
boost::shared_ptr<DNSRecordContent> d_content;
+
+ bool operator<(const DNSRecord& rhs) const
+ {
+ string lzrp, rzrp;
+ if(d_content)
+ lzrp= d_content->getZoneRepresentation();
+ if(rhs.d_content)
+ rzrp=rhs.d_content->getZoneRepresentation();
+
+ return
+ tie(d_label, d_type, d_class, d_ttl, d_clen, lzrp) <
+ tie(rhs.d_label, rhs.d_type, rhs.d_class, rhs.d_ttl, rhs.d_clen, rzrp);
+ }
+
+ bool operator==(const DNSRecord& rhs) const
+ {
+ string lzrp, rzrp;
+ if(d_content)
+ lzrp= d_content->getZoneRepresentation();
+ if(rhs.d_content)
+ rzrp=rhs.d_content->getZoneRepresentation();
+
+ return
+ tie(d_label, d_type, d_class, d_ttl, d_clen, lzrp) ==
+ tie(rhs.d_label, rhs.d_type, rhs.d_class, rhs.d_ttl, rhs.d_clen, rzrp);
+ }
};
/** two modes:
-Replay all recursion-desired DNS questions to a specified IP address
+Replay all recursion-desired DNS questions to a specified IP address.
+Track all outgoing questions, remap id to one of ours.
+Also track all recorded answers, and map them to that same id, the 'expectation'.
+
+When we see a question, parse it, give it a QuestionIdentifyer, and and an id from the free-id list.
+
+When we see an answer in the tcpdump, parse it, make QI, and add it to the original QI
+ and check
+
+When we see an answer from the socket, use the id to match it up to the original QI
+ and check
+
+
+There is one central object, which has (when complete)
+
+ our assigned id
+ QI
+ Original answer
+ Socket answer
*/
#include <pcap.h>
-
+#include <bitset>
#include "statbag.hh"
#include "dnspcap.hh"
#include "sstuff.hh"
-
+#include "anadns.hh"
#include <arpa/nameser.h>
+#include <set>
using namespace boost;
using namespace std;
StatBag S;
+class DNSIDManager
+{
+public:
+
+ int getID()
+ {
+
+ for(unsigned int n=0; n < d_freeids.size() ; ++n)
+ if(!d_freeids[n]) {
+ d_freeids[n]=1;
+ return n;
+ }
+
+ throw runtime_error("Out of free IDs");
+ }
+
+ void releaseID(int id)
+ {
+ if(!d_freeids[id])
+ throw runtime_error("Trying to release unused id: "+lexical_cast<string>(id));
+ d_freeids[id]=0;
+ }
+
+private:
+ bitset<65536> d_freeids;
+} s_idmanager;
+
+struct QuestionData
+{
+ QuestionData() : d_assignedID(-1), d_origRcode(-1), d_newRcode(-1)
+ {}
+ int d_assignedID;
+ MOADNSParser::answers_t d_origAnswers, d_newAnswers;
+ int d_origRcode, d_newRcode;
+};
+
+typedef map<QuestionIdentifier, QuestionData> qids_t;
+qids_t qids;
+
+void compactAnswerSet(MOADNSParser::answers_t orig, set<DNSRecord>& compacted)
+{
+ for(MOADNSParser::answers_t::const_iterator i=orig.begin(); i != orig.end(); ++i)
+ if(i->first.d_place==DNSRecord::Answer)
+ compacted.insert(i->first);
+}
+
+void measureResultAndClean(const QuestionIdentifier& qi)
+{
+ QuestionData qd=qids[qi];
+ cerr<<"Orig rcode: "<<qd.d_origRcode<<", ours: "<<qd.d_newRcode;
+
+ set<DNSRecord> canonicOrig, canonicNew;
+ compactAnswerSet(qd.d_origAnswers, canonicOrig);
+ compactAnswerSet(qd.d_newAnswers, canonicNew);
+
+ cerr<<", "<<canonicOrig.size()<< " vs " << canonicNew.size()<<", perfect: ";
+
+ if(canonicOrig==canonicNew)
+ cerr<<"yes";
+ else
+ cerr<<"no";
+ cerr<<endl;
+
+ qids.erase(qi);
+}
+
+
+void processIncoming(Socket& s)
+{
+ string packet;
+ IPEndpoint remote;
+ while(s.recvFromAsync(packet, remote)) {
+ MOADNSParser mdp(packet.c_str(), packet.length());
+ if(!mdp.d_header.qr) {
+ cerr<<"Received a question from our reference nameserver!"<<endl;
+ continue;
+ }
+
+ qids_t::iterator i=qids.begin();
+ for(; i!=qids.end(); ++i)
+ if(i->second.d_assignedID == ntohs(mdp.d_header.id))
+ break;
+
+ if(i==qids.end()) {
+ cerr<<"Received an answer from reference nameserver with id "<<mdp.d_header.id<<" which we can't match to a question!"<<endl;
+ continue;
+ }
+
+ cerr<<"Matched answer from reference to a question we asked"<<endl;
+
+ QuestionData& qd=i->second;
+
+ qd.d_newAnswers=mdp.d_answers;
+ qd.d_newRcode=mdp.d_header.rcode;
+ if(qd.d_origRcode!=-1) {
+ cerr<<"Removing entry "<<i->first<<", is done [in socket]"<<endl;
+ measureResultAndClean(i->first);
+ }
+ }
+
+}
int main(int argc, char** argv)
try
PcapPacketReader pr(argv[1]);
Socket s(InterNetwork, Datagram);
-
+ s.setNonBlocking();
IPEndpoint remote("127.0.0.1", 5300);
+ /*
struct timespec tosleep;
-
struct timeval lastsent={0,0};
double seconds, useconds;
double factor=20;
+ */
+
while(pr.getUDPPacket()) {
- if(ntohs(pr.d_udp->dest)==53 || ntohs(pr.d_udp->source)==53 && pr.d_len > sizeof(HEADER)) {
- HEADER* dh=(HEADER*)pr.d_payload;
-
- if(dh->rd && !dh->qr) {
- if(lastsent.tv_sec) {
- seconds=pr.d_pheader.ts.tv_sec - lastsent.tv_sec;
- useconds=(pr.d_pheader.ts.tv_usec - lastsent.tv_usec);
-
- seconds/=factor;
- useconds/=factor;
-
- long long nanoseconds=1000000000ULL*seconds + useconds * 1000;
-
- tosleep.tv_sec=nanoseconds/1000000000UL;
- tosleep.tv_nsec=nanoseconds%1000000000UL;
-
- nanosleep(&tosleep, 0);
+ processIncoming(s);
+
+
+ HEADER* dh=(HEADER*)pr.d_payload;
+ if(ntohs(pr.d_udp->dest)!=53 && ntohs(pr.d_udp->source)!=53 || !dh->rd || pr.d_len <= sizeof(HEADER))
+ continue;
+
+ try {
+ MOADNSParser mdp((const char*)pr.d_payload, pr.d_len);
+ QuestionIdentifier qi=QuestionIdentifier::create(pr.d_ip, pr.d_udp, mdp);
+
+ if(!mdp.d_header.qr) {
+ if(qids.count(qi)) {
+ cerr<<"Saw an exact duplicate question, "<<qi<< endl;
+ continue;
}
- lastsent=pr.d_pheader.ts;
+ else
+ cerr<<"New question "<<qi<<endl;
+ QuestionData& qd=qids[qi];
+
+ qd.d_assignedID = s_idmanager.getID();
+ dh->id=htons(qd.d_assignedID);
s.sendTo(string(pr.d_payload, pr.d_payload + pr.d_len), remote);
}
+ else {
+ if(qids.count(qi)) {
+ QuestionData& qd=qids[qi];
+ cerr<<"Matched answer "<<qi<<endl;
+ qd.d_origAnswers=mdp.d_answers;
+ qd.d_origRcode=mdp.d_header.rcode;
+ if(qd.d_newRcode!=-1) {
+ cerr<<"Removing entry "<<qi<<", is done [in main loop]"<<endl;
+ measureResultAndClean(qi);
+ }
+ continue;
+ }
+ else
+ cerr<<"Unmatched answer "<<qi<<endl;
+ // QuestionData& qd=qids[qi];
+
+ }
+ }
+ catch(MOADNSException &e)
+ {
+ }
+ catch(out_of_range &e)
+ {
}
}
{
cerr<<"Fatal: "<<e.what()<<endl;
}
+
+
+#if 0
+ if(lastsent.tv_sec) {
+ seconds=pr.d_pheader.ts.tv_sec - lastsent.tv_sec;
+ useconds=(pr.d_pheader.ts.tv_usec - lastsent.tv_usec);
+
+ seconds/=factor;
+ useconds/=factor;
+
+ long long nanoseconds=1000000000ULL*seconds + useconds * 1000;
+
+ tosleep.tv_sec=nanoseconds/1000000000UL;
+ tosleep.tv_nsec=nanoseconds%1000000000UL;
+
+ nanosleep(&tosleep, 0);
+ }
+#endif
#include <map>
#include <set>
#include <fstream>
+#include "anadns.hh"
using namespace boost;
using namespace std;
StatBag S;
-struct QuestionIdentifyer
-{
- QuestionIdentifyer()
- {}
-
- bool operator<(const QuestionIdentifyer& rhs) const
- {
- return
- tie(d_sourceip, d_destip, d_sourceport, d_destport, d_qname, d_qtype, d_id) <
- tie(rhs.d_sourceip, rhs.d_destip, rhs.d_sourceport, rhs.d_destport, rhs.d_qname, rhs.d_qtype, rhs.d_id);
- }
-
- // the canonical direction is that of the question
- static QuestionIdentifyer create(const struct iphdr* d_ip, const struct udphdr* d_udp, const MOADNSParser& mdp)
- {
- QuestionIdentifyer ret;
- if(mdp.d_header.qr) {
- ret.d_sourceip=htonl(d_ip->daddr);
- ret.d_destip=htonl(d_ip->saddr);
- ret.d_sourceport=htons(d_udp->dest);
- ret.d_destport=htons(d_udp->source);
- }
- else {
- ret.d_sourceip=htonl(d_ip->saddr);
- ret.d_destip=htonl(d_ip->daddr);
- ret.d_sourceport=htons(d_udp->source);
- ret.d_destport=htons(d_udp->dest);
- }
- ret.d_qname=mdp.d_qname;
- ret.d_qtype=mdp.d_qtype;
- ret.d_id=mdp.d_header.id;
- return ret;
- }
-
- uint32_t d_sourceip;
- uint32_t d_destip;
- uint16_t d_sourceport;
- uint16_t d_destport;
-
- string d_qname;
- uint16_t d_qtype;
- uint16_t d_id;
-};
struct QuestionData
{
struct timeval d_firstquestiontime;
};
-typedef map<QuestionIdentifyer, QuestionData> statmap_t;
+typedef map<QuestionIdentifier, QuestionData> statmap_t;
statmap_t statmap;
int main(int argc, char** argv)
string name=mdp.d_qname+"|"+DNSRecordContent::NumberToType(mdp.d_qtype);
- QuestionIdentifyer qi=QuestionIdentifyer::create(pr.d_ip, pr.d_udp, mdp);
+ QuestionIdentifier qi=QuestionIdentifier::create(pr.d_ip, pr.d_udp, mdp);
if(!mdp.d_header.qr) {
// cout<<"Question for '"<< name <<"'\n";
ep.port=ntohs(remote.sin_port);
}
+ bool recvFromAsync(string &dgram, IPEndpoint &ep)
+ {
+ struct sockaddr_in remote;
+ socklen_t remlen=sizeof(remote);
+ int bytes;
+ if((bytes=recvfrom(d_socket, d_buffer, d_buflen, 0, (sockaddr *)&remote, &remlen))<0)
+ if(errno!=EAGAIN)
+ throw NetworkError(strerror(errno));
+ else
+ return false;
+
+ dgram.assign(d_buffer,bytes);
+ ep.address.byte=remote.sin_addr.s_addr;
+ ep.port=ntohs(remote.sin_port);
+ return true;
+ }
+
+
//! For datagram sockets, send a datagram to a destination
/** For datagram sockets, send a datagram to a destination
\param dgram The datagram