add EDNS, proper label ordering including embedded zeros, update docs
This commit is contained in:
@ -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
|
||||
|
@ -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"));
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
30
tdns/tdns.cc
30
tdns/tdns.cc
@ -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) {
|
||||
|
Reference in New Issue
Block a user