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