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
* Glue records
* Truncation
* EDNS (buffer size, no options)
Missing:
* Compression (may not fit in the 1000 lines!)
* EDNS (not 'basic' DNS by our definition, but ok)
* Compression (may not fit in the 1200 lines!)
Known broken:
* Embedded 0s in DNS labels don't yet work
* Case-insensitive comparison isn't 100% correct
* ~~Embedded 0s in DNS labels don't yet work~~
* ~~Case-insensitive comparison isn't 100% correct~~
* RCode after one CNAME chase
* On output (to screen) we do not escape DNS names correctly
* 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({"ns1"})->addRRs(AGen::make("212.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({"time"})->addRRs(ClockTXTGen::make("The time is %a, %d %b %Y %T %z"));
}

View File

@ -14,18 +14,28 @@ class dnslabel
{
public:
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!
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
{
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(); }
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);
@ -40,12 +50,12 @@ SMARTENUMEND(RCode)
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)
SENUM13(DNSType, A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, IXFR, AAAA, SRV, IXFR, AXFR)
SENUM(DNSType, ANY)
SENUM13(DNSType, A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, IXFR, AAAA, SRV, OPT, IXFR)
SENUM2(DNSType, AXFR, ANY)
SMARTENUMEND(DNSType)
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();
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)
{
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)
{
dh.ancount = dh.arcount = dh.nscount = 0;

View File

@ -25,6 +25,8 @@ struct DNSMessageWriter
std::vector<uint8_t> payload;
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 putEDNS(uint16_t bufsize, bool doBit);
std::string serialize() const;
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)
{
dnsname name, origname;
dnsname name;
DNSType 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;
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 {
response.dh = dm.dh;
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]);
}
addAdditional(bestzone, zone, additional, response);
}
}
else {
cout<<"No zone matched"<<endl;
response.dh.rcode = (uint8_t)RCode::Refused;
}
if(haveEDNS) {
response.putEDNS(newsize, doBit);
}
return true;
}
catch(std::out_of_range& e) { // exceeded packet size
cout<<"Query for '"<<origname<<"'|"<<type<<" got truncated"<<endl;
response.setQuestion(origname, type); // this resets the packet
response.dh.tc=1; response.dh.aa=0;
if(haveEDNS) {
response.putEDNS(newsize, doBit);
}
return true;
}
catch(std::exception& e) {