add EDNS, proper label ordering including embedded zeros, update docs

This commit is contained in:
bert hubert
2018-04-12 00:04:59 +02:00
parent 294e72b261
commit cc9a13b29a
6 changed files with 67 additions and 16 deletions

View File

@ -26,14 +26,14 @@ Features are complete:
* Delegations * Delegations
* Glue records * Glue records
* Truncation * Truncation
* EDNS (buffer size, no options)
Missing: Missing:
* Compression (may not fit in the 1000 lines!) * Compression (may not fit in the 1200 lines!)
* EDNS (not 'basic' DNS by our definition, but ok)
Known broken: Known broken:
* Embedded 0s in DNS labels don't yet work * ~~Embedded 0s in DNS labels don't yet work~~
* Case-insensitive comparison isn't 100% correct * ~~Case-insensitive comparison isn't 100% correct~~
* RCode after one CNAME chase * RCode after one CNAME chase
* On output (to screen) we do not escape DNS names correctly * On output (to screen) we do not escape DNS names correctly
* TCP/IP does not follow recommended timeouts * TCP/IP does not follow recommended timeouts

View File

@ -28,7 +28,9 @@ void loadZones(DNSNode& zones)
newzone->add({"fra"})->addRRs(NSGen::make({"ns1","fra","powerdns","org"}), NSGen::make({"ns1","fra","powerdns","org"})); newzone->add({"fra"})->addRRs(NSGen::make({"ns1","fra","powerdns","org"}), NSGen::make({"ns1","fra","powerdns","org"}));
newzone->add({"ns1"})->addRRs(AGen::make("212.13.14.15")); newzone->add({"ns1"})->addRRs(AGen::make("212.13.14.15"));
newzone->add({"ns1", "fra"})->addRRs(AGen::make("12.13.14.15")); newzone->add({"ns1", "fra"})->addRRs(AGen::make("12.13.14.15"));
newzone->add({"NS2", "fra"})->addRRs(AGen::make("12.13.14.16"), AAAAGen::make("::1")); newzone->add({"NS2", "fra"})->addRRs(AGen::make("12.13.14.16"));
newzone->add({"ns2", "fra"})->addRRs(AAAAGen::make("::1"));
newzone->add({"something"})->addRRs(AAAAGen::make("::1"), AGen::make("12.13.14.15")); newzone->add({"something"})->addRRs(AAAAGen::make("::1"), AGen::make("12.13.14.15"));
newzone->add({"time"})->addRRs(ClockTXTGen::make("The time is %a, %d %b %Y %T %z")); newzone->add({"time"})->addRRs(ClockTXTGen::make("The time is %a, %d %b %Y %T %z"));
} }

View File

@ -14,18 +14,28 @@ class dnslabel
{ {
public: public:
dnslabel() {} dnslabel() {}
dnslabel(const char* s) : d_s(s) {} // XXX check length here! dnslabel(const char* s) : dnslabel(std::string(s)) {}
dnslabel(const std::string& s) : d_s(s) {} // XXX check length here! dnslabel(const std::string& s) : d_s(s) {} // XXX check length here!
bool operator<(const dnslabel& rhs) const bool operator<(const dnslabel& rhs) const
{ {
return strcasecmp(d_s.c_str(), rhs.d_s.c_str()) < 0; // XXX locale pain, plus embedded zeros return std::lexicographical_compare(d_s.begin(), d_s.end(), rhs.d_s.begin(), rhs.d_s.end(), charcomp);
} }
bool operator==(const dnslabel &rhs) const bool operator==(const dnslabel &rhs) const
{ {
return strcasecmp(d_s.c_str(), rhs.d_s.c_str()) == 0; // XXX locale pain, plus embedded zeros return !(*this < rhs) && !(rhs<*this);
} }
auto size() const { return d_s.size(); } auto size() const { return d_s.size(); }
std::string d_s; std::string d_s;
private:
static bool charcomp(char a, char b)
{
if(a >= 0x61 && a <= 0x7A)
a -= 0x20;
if(b >= 0x61 && b <= 0x7A)
b -= 0x20;
return a < b;
}
}; };
std::ostream & operator<<(std::ostream &os, const dnslabel& d); std::ostream & operator<<(std::ostream &os, const dnslabel& d);
@ -40,12 +50,12 @@ SMARTENUMEND(RCode)
enum class DNSType : uint16_t enum class DNSType : uint16_t
{ {
A = 1, NS = 2, CNAME = 5, SOA=6, PTR=12, MX=15, TXT=16, AAAA = 28, SRV=33, IXFR = 251, AXFR = 252, ANY = 255 A = 1, NS = 2, CNAME = 5, SOA=6, PTR=12, MX=15, TXT=16, AAAA = 28, SRV=33, OPT=41, IXFR = 251, AXFR = 252, ANY = 255
}; };
SMARTENUMSTART(DNSType) SMARTENUMSTART(DNSType)
SENUM13(DNSType, A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, IXFR, AAAA, SRV, IXFR, AXFR) SENUM13(DNSType, A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, IXFR, AAAA, SRV, OPT, IXFR)
SENUM(DNSType, ANY) SENUM2(DNSType, AXFR, ANY)
SMARTENUMEND(DNSType) SMARTENUMEND(DNSType)
COMBOENUM4(DNSSection, Question, 0, Answer, 1, Authority, 2, Additional, 3) COMBOENUM4(DNSSection, Question, 0, Answer, 1, Authority, 2, Additional, 3)

View File

@ -22,9 +22,9 @@ void DNSMessageReader::getQuestion(dnsname& name, DNSType& type)
{ {
name=getName(); name=getName();
type=(DNSType)payload.getUInt16(); type=(DNSType)payload.getUInt16();
payload.getUInt16(); // skip the class
} }
void DNSMessageWriter::putRR(DNSSection section, const dnsname& name, DNSType type, uint32_t ttl, const std::unique_ptr<RRGen>& content) void DNSMessageWriter::putRR(DNSSection section, const dnsname& name, DNSType type, uint32_t ttl, const std::unique_ptr<RRGen>& content)
{ {
auto cursize = payloadpos; auto cursize = payloadpos;
@ -55,6 +55,21 @@ void DNSMessageWriter::putRR(DNSSection section, const dnsname& name, DNSType ty
} }
} }
void DNSMessageWriter::putEDNS(uint16_t bufsize, bool doBit)
{
auto cursize = payloadpos;
try {
putUInt8(0); putUInt16((uint16_t)DNSType::OPT); // 'root' name, our type
putUInt16(bufsize); putUInt16(0); putUInt8(doBit ? 0x80 : 0); putUInt8(0);
putUInt16(0);
}
catch(...) {
payloadpos = cursize;
throw;
}
dh.nscount = htons(ntohs(dh.nscount)+1);
}
void DNSMessageWriter::setQuestion(const dnsname& name, DNSType type) void DNSMessageWriter::setQuestion(const dnsname& name, DNSType type)
{ {
dh.ancount = dh.arcount = dh.nscount = 0; dh.ancount = dh.arcount = dh.nscount = 0;

View File

@ -25,6 +25,8 @@ struct DNSMessageWriter
std::vector<uint8_t> payload; std::vector<uint8_t> payload;
void setQuestion(const dnsname& name, DNSType type); void setQuestion(const dnsname& name, DNSType type);
void putRR(DNSSection section, const dnsname& name, DNSType type, uint32_t ttl, const std::unique_ptr<RRGen>& rr); void putRR(DNSSection section, const dnsname& name, DNSType type, uint32_t ttl, const std::unique_ptr<RRGen>& rr);
void putEDNS(uint16_t bufsize, bool doBit);
std::string serialize() const; std::string serialize() const;
uint16_t payloadpos=0; uint16_t payloadpos=0;

View File

@ -44,12 +44,28 @@ void addAdditional(const DNSNode* bestzone, const dnsname& zone, const vector<dn
bool processQuestion(const DNSNode& zones, DNSMessageReader& dm, const ComboAddress& local, const ComboAddress& remote, DNSMessageWriter& response) bool processQuestion(const DNSNode& zones, DNSMessageReader& dm, const ComboAddress& local, const ComboAddress& remote, DNSMessageWriter& response)
{ {
dnsname name, origname; dnsname name;
DNSType type; DNSType type;
dm.getQuestion(name, type); dm.getQuestion(name, type);
origname=name; // we munch on this below dnsname origname=name; // we need this for error reporting, we munch the original name
bool haveEDNS=false;
cout<<"Received a query from "<<remote.toStringWithPort()<<" for "<<name<<" and type "<<type<<endl; cout<<"Received a query from "<<remote.toStringWithPort()<<" for "<<name<<" and type "<<type<<endl;
uint16_t newsize=0;
bool doBit=false;
if(dm.dh.arcount) {
if(dm.payload.getUInt8() == 0 && dm.payload.getUInt16() == (uint16_t)DNSType::OPT) {
haveEDNS=true;
newsize=dm.payload.getUInt16();
dm.payload.getUInt16(); // extended RCODE, EDNS version
auto flags = dm.payload.getUInt8();
doBit = flags & 0x80;
dm.payload.getUInt8(); dm.payload.getUInt16(); // ignore rest
cout<<" There was an EDNS section, size supported: "<<newsize<<endl;
if(newsize > sizeof(dnsheader))
response.payload.resize(newsize - sizeof(dnsheader));
}
}
try { try {
response.dh = dm.dh; response.dh = dm.dh;
response.dh.ad = response.dh.ra = response.dh.aa = 0; response.dh.ad = response.dh.ra = response.dh.aa = 0;
@ -162,19 +178,25 @@ bool processQuestion(const DNSNode& zones, DNSMessageReader& dm, const ComboAddr
response.putRR(DNSSection::Answer, zone, DNSType::SOA, rrset.ttl, rrset.contents[0]); response.putRR(DNSSection::Answer, zone, DNSType::SOA, rrset.ttl, rrset.contents[0]);
} }
addAdditional(bestzone, zone, additional, response); addAdditional(bestzone, zone, additional, response);
} }
} }
else { else {
cout<<"No zone matched"<<endl; cout<<"No zone matched"<<endl;
response.dh.rcode = (uint8_t)RCode::Refused; response.dh.rcode = (uint8_t)RCode::Refused;
} }
if(haveEDNS) {
response.putEDNS(newsize, doBit);
}
return true; return true;
} }
catch(std::out_of_range& e) { // exceeded packet size catch(std::out_of_range& e) { // exceeded packet size
cout<<"Query for '"<<origname<<"'|"<<type<<" got truncated"<<endl; cout<<"Query for '"<<origname<<"'|"<<type<<" got truncated"<<endl;
response.setQuestion(origname, type); // this resets the packet response.setQuestion(origname, type); // this resets the packet
response.dh.tc=1; response.dh.aa=0; response.dh.tc=1; response.dh.aa=0;
if(haveEDNS) {
response.putEDNS(newsize, doBit);
}
return true; return true;
} }
catch(std::exception& e) { catch(std::exception& e) {