if RECURSOR
sbin_PROGRAMS = pdns_server pdns_recursor
else
-sbin_PROGRAMS = pdns_server
+sbin_PROGRAMS = pdns_server
endif
-bin_PROGRAMS = pdns_control
+bin_PROGRAMS = pdns_control sdig
EXTRA_PROGRAMS=pdns_recursor pdns_control qgen
pdns_server_LDFLAGS= @moduleobjects@ @modulelibs@ @DYNLINKFLAGS@ @LIBDL@ @THREADFLAGS@
pdns_server_INCLUDES=
+sdig_SOURCES=sdig.cc sstuff.hh dnsparser.cc dnsparser.hh dnsrecords.cc
+
qgen_SOURCES=resolver.cc resolver.hh misc.cc unix_utility.cc qtype.cc \
logger.cc statbag.cc dnspacket.cc arguments.cc qgen.cc \
/*
PowerDNS Versatile Database Driven Nameserver
- Copyright (C) 2002 PowerDNS.COM BV
+ Copyright (C) 2005 PowerDNS.COM BV
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
arg().set("soa-minimum-ttl","Default SOA mininum ttl")="3600";
arg().set("default-ttl","Seconds a result is valid if not set otherwise")="3600";
arg().set("max-tcp-connections","Maximum number of TCP connections")="10";
+ arg().setSwitch("no-shuffle","Set this to prevent random shuffling of answers - for regression testing")="off";
arg().setSwitch( "use-logfile", "Use a log file" )= "no";
arg().set( "logfile", "Logfile to use" )= "pdns.log";
/*
PowerDNS Versatile Database Driven Nameserver
- Copyright (C) 2002 PowerDNS.COM BV
+ Copyright (C) 2005 PowerDNS.COM BV
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-// $Id: dnspacket.cc,v 1.32 2005/01/11 19:59:00 ahu Exp $
+
#include "utility.hh"
#include <cstdio>
stable_sort(rrs.begin(),rrs.end(),rrcomp);
- // now shuffle! start out with the ANSWER records
- vector<DNSResourceRecord>::iterator first, second;
- for(first=rrs.begin();first!=rrs.end();++first)
- if(first->d_place==DNSResourceRecord::ANSWER && first->qtype.getCode() != QType::CNAME) // CNAME must come first
- break;
- for(second=first;second!=rrs.end();++second)
- if(second->d_place!=DNSResourceRecord::ANSWER)
- break;
-
- if(second-first>1)
- random_shuffle(first,second);
-
- // now shuffle the additional records
- for(first=second;first!=rrs.end();++first)
- if(first->d_place==DNSResourceRecord::ADDITIONAL && first->qtype.getCode() != QType::CNAME) // CNAME must come first
- break;
- for(second=first;second!=rrs.end();++second)
- if(second->d_place!=DNSResourceRecord::ADDITIONAL)
- break;
-
- if(second-first>1)
- random_shuffle(first,second);
-
+ if(!arg().mustDo("no-shuffle")) {
+ // now shuffle! start out with the ANSWER records
+ vector<DNSResourceRecord>::iterator first, second;
+ for(first=rrs.begin();first!=rrs.end();++first)
+ if(first->d_place==DNSResourceRecord::ANSWER && first->qtype.getCode() != QType::CNAME) // CNAME must come first
+ break;
+ for(second=first;second!=rrs.end();++second)
+ if(second->d_place!=DNSResourceRecord::ANSWER)
+ break;
+
+ if(second-first>1)
+ random_shuffle(first,second);
+
+ // now shuffle the additional records
+ for(first=second;first!=rrs.end();++first)
+ if(first->d_place==DNSResourceRecord::ADDITIONAL && first->qtype.getCode() != QType::CNAME) // CNAME must come first
+ break;
+ for(second=first;second!=rrs.end();++second)
+ if(second->d_place!=DNSResourceRecord::ADDITIONAL)
+ break;
+
+ if(second-first>1)
+ random_shuffle(first,second);
+ }
d_wrapped=true;
--- /dev/null
+#include "dnsparser.hh"
+#include <boost/lexical_cast.hpp>
+
+using namespace boost;
+
+class UnknownRecordContent : public DNSRecordContent
+{
+public:
+ UnknownRecordContent(const struct dnsrecordheader& ah, PacketReader& pr)
+ : d_ah(ah)
+ {
+ pr.copyRecord(d_record, ah.d_clen);
+ }
+
+ string getType() const
+ {
+ return "#"+lexical_cast<string>(d_ah.d_type);
+ }
+
+
+
+ string getZoneRepresentation() const
+ {
+ ostringstream str;
+ if(d_ah.d_class==1)
+ str<<"IN";
+ else
+ str<<"CLASS"<<d_ah.d_class;
+
+ str<<"\t";
+
+ str<<"TYPE"<<d_ah.d_type<<"\t";
+
+ str<<"\\# "<<d_record.size()<<" ";
+ char hex[4];
+ for(size_t n=0; n<d_record.size(); ++n) {
+ snprintf(hex,sizeof(hex)-1, "%02x", d_record.at(n));
+ str << hex;
+ }
+ str<<"\n";
+ return str.str();
+ }
+
+
+private:
+ struct dnsrecordheader d_ah;
+ vector<u_int8_t> d_record;
+};
+
+
+
+DNSRecordContent* DNSRecordContent::mastermake(const struct dnsrecordheader& ah,
+ PacketReader& pr)
+{
+ typemap_t::const_iterator i=typemap.find(make_pair(ah.d_class, ah.d_type));
+ if(i==typemap.end()) {
+ return new UnknownRecordContent(ah, pr);
+ }
+ return i->second(ah, pr);
+}
+
+
+DNSRecordContent::typemap_t DNSRecordContent::typemap __attribute__((init_priority(1000)));
+
+void MOADNSParser::init(const char *packet, unsigned int len)
+{
+ if(len < sizeof(dnsheader))
+ throw MOADNSException("Packet shorter than minimal header");
+
+ memcpy(&d_header, packet, sizeof(dnsheader));
+
+ d_header.qdcount=ntohs(d_header.qdcount);
+ d_header.ancount=ntohs(d_header.ancount);
+ d_header.nscount=ntohs(d_header.nscount);
+ d_header.arcount=ntohs(d_header.arcount);
+
+ u_int16_t contentlen=len-sizeof(dnsheader);
+
+ d_content.resize(contentlen);
+ copy(packet+sizeof(dnsheader), packet+len, d_content.begin());
+
+ unsigned int n;
+
+ PacketReader pr(d_content);
+
+ for(n=0;n < d_header.qdcount; ++n) {
+ d_qname=pr.getLabel();
+ d_qtype=pr.get16BitInt();
+ d_qclass=pr.get16BitInt();
+ // cout<<"Question is for '"<<d_qname<<"', type "<<d_qtype<<endl;
+ }
+
+ struct dnsrecordheader ah;
+ vector<unsigned char> record;
+
+ for(n=0;n < d_header.ancount + d_header.nscount + d_header.arcount; ++n) {
+ DNSRecord dr;
+
+ if(n < d_header.ancount)
+ dr.d_place=DNSRecord::Answer;
+ else if(n < d_header.ancount + d_header.nscount)
+ dr.d_place=DNSRecord::Nameserver;
+ else
+ dr.d_place=DNSRecord::Additional;
+
+ string label=pr.getLabel();
+
+ pr.getDnsrecordheader(ah);
+ dr.d_ttl=ah.d_ttl;
+ dr.d_type=ah.d_type;
+ dr.d_class=ah.d_class;
+
+ dr.d_label=label;
+ dr.d_ah=ah;
+ d_answers.push_back(make_pair(dr, pr.d_pos));
+
+ dr.d_content=boost::shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(ah, pr));
+ if(dr.d_content) {
+ // cout<<dr.d_label<<"\t"<<dr.d_content->getZoneRepresentation();
+ }
+
+ }
+
+ if(pr.d_pos!=contentlen) {
+ // cout<<pr.d_pos<<" != " <<contentlen<<endl;
+ throw MOADNSException("Packet has trailing garbage");
+ }
+}
+
+void PacketReader::getDnsrecordheader(struct dnsrecordheader &ah)
+{
+ unsigned int n;
+ unsigned char *p=reinterpret_cast<unsigned char*>(&ah);
+
+ for(n=0; n < sizeof(dnsrecordheader); ++n)
+ p[n]=d_content.at(d_pos++);
+
+ ah.d_type=ntohs(ah.d_type);
+ ah.d_class=ntohs(ah.d_class);
+ ah.d_clen=ntohs(ah.d_clen);
+ ah.d_ttl=ntohl(ah.d_ttl);
+}
+
+
+void PacketReader::copyRecord(vector<unsigned char>& dest, u_int16_t len)
+{
+ dest.resize(len);
+ for(u_int16_t n=0;n<len;++n) {
+ dest.at(n)=d_content.at(d_pos++);
+ }
+}
+
+void PacketReader::copyRecord(unsigned char* dest, u_int16_t len)
+{
+ if(d_pos + len > d_content.size())
+ throw MOADNSException("Attempt to copy outside of packet");
+
+ memcpy(dest, &d_content[d_pos], len);
+ d_pos+=len;
+}
+
+
+u_int32_t PacketReader::get32BitInt()
+{
+ u_int32_t ret=0;
+ ret+=d_content.at(d_pos++);
+ ret<<=8;
+ ret+=d_content.at(d_pos++);
+ ret<<=8;
+ ret+=d_content.at(d_pos++);
+ ret<<=8;
+ ret+=d_content.at(d_pos++);
+
+ return ret;
+}
+
+
+u_int16_t PacketReader::get16BitInt()
+{
+ return get16BitInt(d_content, d_pos);
+}
+
+u_int16_t PacketReader::get16BitInt(const vector<unsigned char>&content, u_int16_t& pos)
+{
+ u_int16_t ret=0;
+ ret+=content.at(pos++);
+ ret<<=8;
+ ret+=content.at(pos++);
+
+ return ret;
+}
+
+u_int8_t PacketReader::get8BitInt()
+{
+ return d_content.at(d_pos++);
+}
+
+
+string PacketReader::getLabel(unsigned int recurs)
+{
+ return getLabelFromContent(d_content, d_pos, recurs++);
+}
+
+
+string PacketReader::getLabelFromContent(const vector<u_int8_t>& content, u_int16_t& frompos, int recurs)
+{
+ if(recurs > 10)
+ throw MOADNSException("Loop");
+
+ string ret;
+ for(;;) {
+ unsigned char labellen=content.at(frompos++);
+
+ // cout<<"Labellen: "<<(int)labellen<<endl;
+ if(!labellen) {
+ if(ret.empty())
+ ret+=".";
+ break;
+ }
+ if((labellen & 0xc0) == 0xc0) {
+ u_int16_t offset=(labellen & ~0xc0) + content.at(frompos++) - sizeof(dnsheader);
+ // cout<<"This is an offset, need to go to: "<<offset<<endl;
+ return ret+getLabelFromContent(content, offset, ++recurs);
+ }
+ else {
+ for(int n=0;n<labellen;++n) {
+ ret+=content.at(frompos++);
+ }
+ ret+=".";
+ }
+ }
+
+ return ret;
+}
--- /dev/null
+#ifndef DNSPARSER_HH
+#define DNSPARSER_HH
+
+#include <map>
+#include <sstream>
+#include <stdexcept>
+#include <pcap.h>
+#include <iostream>
+#include <vector>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <boost/shared_ptr.hpp>
+
+namespace {
+ typedef HEADER dnsheader;
+}
+
+using namespace std;
+using namespace boost;
+typedef runtime_error MOADNSException;
+
+struct dnsrecordheader
+{
+ u_int16_t d_type;
+ u_int16_t d_class;
+ u_int32_t d_ttl;
+ u_int16_t d_clen;
+} __attribute__((packed));
+
+
+class MOADNSParser;
+
+class PacketReader
+{
+public:
+ PacketReader(const vector<u_int8_t>& content)
+ : d_pos(0), d_content(content)
+ {}
+
+ u_int32_t get32BitInt();
+ u_int16_t get16BitInt();
+ static u_int16_t get16BitInt(const vector<unsigned char>&content, u_int16_t& pos);
+ static string getLabelFromContent(const vector<u_int8_t>& content, u_int16_t& frompos, int recurs);
+ u_int8_t get8BitInt();
+ void getDnsrecordheader(struct dnsrecordheader &ah);
+ void copyRecord(vector<unsigned char>& dest, u_int16_t len);
+ void copyRecord(unsigned char* dest, u_int16_t len);
+ string getLabel(unsigned int recurs=0);
+
+ u_int16_t d_pos;
+private:
+ const vector<u_int8_t>& d_content;
+
+};
+
+class DNSRecordContent
+{
+public:
+ static DNSRecordContent* mastermake(const struct dnsrecordheader& ah, PacketReader& pr);
+ virtual std::string getZoneRepresentation() const = 0;
+ virtual std::string getType() const=0;
+ virtual ~DNSRecordContent() {}
+
+ std::string label;
+ struct dnsrecordheader header;
+protected:
+ typedef DNSRecordContent* makerfunc_t(const struct dnsrecordheader& ah, PacketReader& pr);
+ typedef std::map<std::pair<u_int16_t, u_int16_t>, makerfunc_t* > typemap_t;
+ static typemap_t typemap;
+};
+
+struct DNSRecord
+{
+ std::string d_label;
+ u_int16_t d_type;
+ u_int16_t d_class;
+ u_int32_t d_ttl;
+ enum {Answer, Nameserver, Additional} d_place;
+ boost::shared_ptr<DNSRecordContent> d_content;
+ struct dnsrecordheader d_ah;
+};
+
+class MOADNSParser
+{
+public:
+ MOADNSParser(const string& buffer)
+ {
+ init(buffer.c_str(), buffer.size());
+ }
+
+ MOADNSParser(const char *packet, unsigned int len)
+ {
+ init(packet, len);
+ }
+ dnsheader d_header;
+ string d_qname;
+ u_int16_t d_qclass, d_qtype;
+
+ typedef vector<pair<DNSRecord, uint16_t > > answers_t;
+ answers_t d_answers;
+
+ shared_ptr<PacketReader> getPacketReader(uint16_t offset)
+ {
+ shared_ptr<PacketReader> pr(new PacketReader(d_content));
+ pr->d_pos=offset;
+ return pr;
+ }
+private:
+ void getDnsrecordheader(struct dnsrecordheader &ah);
+ void init(const char *packet, unsigned int len);
+ vector<uint8_t> d_content;
+};
+
+
+
+
+#endif
--- /dev/null
+#include "dnsparser.hh"
+#include <boost/lexical_cast.hpp>
+using namespace std;
+using namespace boost;
+
+
+class ARecordContent : public DNSRecordContent
+{
+public:
+ static void report(void)
+ {
+ typemap[make_pair(1,1)]=&make;
+ }
+
+ static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader& pr)
+ {
+ if(ah.d_clen!=4)
+ throw MOADNSException("Wrong size for A record");
+
+ ARecordContent* ret=new ARecordContent();
+ pr.copyRecord((unsigned char*) &ret->d_ip, 4);
+ return ret;
+ }
+
+ u_int32_t getIP() const
+ {
+ return d_ip;
+ }
+
+ string getType() const
+ {
+ return "A";
+ }
+
+ string getZoneRepresentation() const
+ {
+ ostringstream str;
+ u_int32_t ip=ntohl(d_ip);
+
+ str<< ((ip >> 24)&0xff) << ".";
+ str<< ((ip >> 16)&0xff) << ".";
+ str<< ((ip >> 8)&0xff) << ".";
+ str<< ((ip )&0xff);
+ return str.str();
+ }
+
+private:
+ u_int32_t d_ip;
+};
+
+class AAAARecordContent : public DNSRecordContent
+{
+public:
+ static void report(void)
+ {
+ typemap[make_pair(1,ns_t_aaaa)]=&make;
+ }
+
+ string getType() const
+ {
+ return "AAAA";
+ }
+
+
+ static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader& pr)
+ {
+ if(ah.d_clen!=16)
+ throw MOADNSException("Wrong size for AAAA record");
+
+ AAAARecordContent* ret=new AAAARecordContent();
+ pr.copyRecord((unsigned char*) &ret->d_ip6, 16);
+ return ret;
+ }
+
+ string getZoneRepresentation() const
+ {
+ ostringstream str;
+
+ char hex[4];
+ for(size_t n=0; n< 16 ; n+=2) {
+ snprintf(hex,sizeof(hex)-1, "%x", d_ip6[n]);
+ str << hex;
+ snprintf(hex,sizeof(hex)-1, "%02x", d_ip6[n+1]);
+ str << hex;
+ if(n!=14)
+ str<<":";
+ }
+
+ return str.str();
+ }
+
+private:
+ unsigned char d_ip6[16];
+};
+
+
+namespace {
+ struct soatimes
+ {
+ u_int32_t serial;
+ u_int32_t refresh;
+ u_int32_t retry;
+ u_int32_t expire;
+ u_int32_t minimum;
+ };
+}
+
+
+class OneLabelRecordContent : public DNSRecordContent
+{
+public:
+
+ OneLabelRecordContent(const struct dnsrecordheader& ah, const string& nsname) : d_type(ah.d_type), d_nsname(nsname) {}
+
+ static void report(void)
+ {
+ typemap[make_pair(1,ns_t_ns)]=&make;
+ typemap[make_pair(1,ns_t_cname)]=&make;
+ typemap[make_pair(1,ns_t_ptr)]=&make;
+ }
+
+ static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader &pr)
+ {
+ return new OneLabelRecordContent(ah, pr.getLabel());
+ }
+
+ string getType() const
+ {
+ if(d_type==ns_t_ns)
+ return "NS";
+ else if(d_type==ns_t_cname)
+ return "CNAME";
+ if(d_type==ns_t_ptr)
+ return "PTR";
+ }
+
+ string getZoneRepresentation() const
+ {
+ ostringstream str;
+
+ /*
+ str<<"IN\t";
+
+ str<<"\t";
+ */
+
+ return str.str();
+ }
+
+private:
+ u_int16_t d_type;
+ string d_nsname;
+
+};
+
+class SOARecordContent : public DNSRecordContent
+{
+public:
+
+ SOARecordContent(const string& mname, const string& rname, const struct soatimes& st)
+ : d_mname(mname), d_rname(rname)
+ {
+ d_st=st;
+ }
+
+ static void report(void)
+ {
+ typemap[make_pair(1,6)]=&make;
+ }
+
+ string getType() const
+ {
+ return "SOA";
+ }
+
+
+ static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader& pr)
+ {
+ u_int16_t nowpos(pr.d_pos);
+ string mname=pr.getLabel();
+ string rname=pr.getLabel();
+
+ u_int16_t left=ah.d_clen - (pr.d_pos-nowpos);
+
+ if(left!=sizeof(struct soatimes))
+ throw MOADNSException("SOA RDATA has wrong size: "+lexical_cast<string>(left)+ ", should be "+lexical_cast<string>(sizeof(struct soatimes)));
+
+ struct soatimes st;
+ pr.copyRecord((unsigned char*)&st, sizeof(struct soatimes));
+
+ st.serial=ntohl(st.serial);
+ st.refresh=ntohl(st.refresh);
+ st.retry=ntohl(st.retry);
+ st.expire=ntohl(st.expire);
+ st.minimum=ntohl(st.minimum);
+
+ return new SOARecordContent(mname, rname, st);
+ }
+
+ string getZoneRepresentation() const
+ {
+ ostringstream str;
+
+ str<<d_mname<<" "<<d_rname<<" ";
+ str<<d_st.serial<<" " << d_st.refresh <<" " <<d_st.retry << " " << d_st.expire<< " "<<d_st.minimum;
+ return str.str();
+ }
+
+
+
+private:
+ string d_mname;
+ string d_rname;
+ struct soatimes d_st;
+};
+
+class MXRecordContent : public DNSRecordContent
+{
+public:
+
+ MXRecordContent(u_int16_t preference, const string& mxname)
+ : d_preference(preference), d_mxname(mxname)
+ {
+ }
+
+ string getType() const
+ {
+ return "MX";
+ }
+
+
+ static void report(void)
+ {
+ typemap[make_pair(1,ns_t_mx)]=&make;
+ }
+
+ static DNSRecordContent* make(const struct dnsrecordheader& ah, PacketReader& pr)
+ {
+ u_int16_t preference=pr.get16BitInt();
+ string mxname=pr.getLabel();
+
+ return new MXRecordContent(preference, mxname);
+ }
+
+ string getZoneRepresentation() const
+ {
+ ostringstream str;
+ str<<d_preference<<" "<<d_mxname;
+ return str.str();
+ }
+
+private:
+ u_int16_t d_preference;
+ string d_mxname;
+};
+
+
+static struct Reporter
+{
+ Reporter()
+ {
+ ARecordContent::report();
+ AAAARecordContent::report();
+ OneLabelRecordContent::report();
+ SOARecordContent::report();
+ MXRecordContent::report();
+ }
+} reporter __attribute__((init_priority(65535)));
--- /dev/null
+#include "dnsparser.hh"
+#include "sstuff.hh"
+#include "misc.hh"
+
+/** Write only packet generator */
+class DNSPacketGenerator
+{
+public:
+ DNSPacketGenerator(const string& qname, uint16_t qtype); // there is always a question
+ const string getPacket();
+ dnsheader d_dnsheader;
+private:
+ string d_content;
+};
+
+const string EncodeDNSLabel(const string& input)
+{
+ typedef vector<string> parts_t;
+ parts_t parts;
+ stringtok(parts,input,".");
+
+ string ret;
+ for(parts_t::const_iterator i=parts.begin(); i!=parts.end(); ++i) {
+ ret.append(1,(char)i->length());
+ ret.append(*i);
+ }
+ ret.append(1,(char)0);
+ return ret;
+
+}
+
+DNSPacketGenerator::DNSPacketGenerator(const string& qname, uint16_t qtype)
+{
+ memset(&d_dnsheader, 0, sizeof(d_dnsheader));
+ d_dnsheader.id=random();
+ d_dnsheader.qdcount=htons(1);
+
+ d_content=EncodeDNSLabel(qname);
+ qtype=htons(qtype);
+ d_content.append((char*)&qtype, 2);
+ qtype=htons(1);
+ d_content.append((char*)&qtype,2);
+}
+
+const string DNSPacketGenerator::getPacket()
+{
+ return string((char*)&d_dnsheader, ((char*)&d_dnsheader)+sizeof(d_dnsheader))+d_content;
+}
+
+int main(int argc, char** argv)
+try
+{
+ DNSPacketGenerator dpg(argv[3], atoi(argv[4]));
+
+ Socket sock(InterNetwork, Datagram);
+ IPEndpoint dest(argv[1], atoi(argv[2]));
+ sock.sendTo(dpg.getPacket(), dest);
+
+ string reply;
+ sock.recvFrom(reply, dest);
+
+ MOADNSParser mdp(reply);
+ cout<<"Reply to question for qname='"<<mdp.d_qname<<"', qtype="<<mdp.d_qtype<<endl;
+
+ for(MOADNSParser::answers_t::const_iterator i=mdp.d_answers.begin(); i!=mdp.d_answers.end(); ++i) {
+ shared_ptr<PacketReader> pr=mdp.getPacketReader(i->second);
+ DNSRecordContent* drc=DNSRecordContent::mastermake(i->first.d_ah, *pr);
+ cout<<i->first.d_place<<"\t"<<i->first.d_label<<"\tIN\t"<<drc->getType()<<"\t"<<i->first.d_ttl<<"\t"<<drc->getZoneRepresentation()<<endl;
+ }
+
+
+}
+catch(exception &e)
+{
+ cerr<<"Fatal: "<<e.what()<<endl;
+}
+