From 294e72b2617be977fe429baf800e2e2de0188449 Mon Sep 17 00:00:00 2001 From: bert hubert Date: Wed, 11 Apr 2018 22:12:57 +0200 Subject: [PATCH] make DNSMessageWriter variable length --- tdns/README.md | 4 +- tdns/contents.cc | 15 +-- tdns/dns-types.cc | 28 ++--- tdns/dnsmessages.cc | 26 ++--- tdns/dnsmessages.hh | 66 ++++++++++-- tdns/safearray.hh | 38 ------- tdns/tdns.cc | 242 ++++++++++++++++++++++---------------------- 7 files changed, 215 insertions(+), 204 deletions(-) diff --git a/tdns/README.md b/tdns/README.md index 0ec6fb5..4a19b9b 100644 --- a/tdns/README.md +++ b/tdns/README.md @@ -4,7 +4,7 @@ # teaching DNS Welcome to tdns, the teaching authoritative server, implementing all of -basic DNS in 1000 lines of code. +basic DNS in ~~1000~~ 1100 lines of code. The goals of tdns are: @@ -25,9 +25,9 @@ Features are complete: * Wildcards * Delegations * Glue records + * Truncation Missing: - * Truncation * Compression (may not fit in the 1000 lines!) * EDNS (not 'basic' DNS by our definition, but ok) diff --git a/tdns/contents.cc b/tdns/contents.cc index 6f6dc16..5c50740 100644 --- a/tdns/contents.cc +++ b/tdns/contents.cc @@ -7,14 +7,15 @@ void loadZones(DNSNode& zones) auto newzone = zone->zone = new DNSNode(); // XXX ICK newzone->addRRs(SOAGen::make({"ns1", "powerdns", "org"}, {"admin", "powerdns", "org"}, 1)); - newzone->rrsets[DNSType::MX].add(MXGen::make(25, {"server1", "powerdns", "org"})); + newzone->addRRs(MXGen::make(25, {"server1", "powerdns", "org"})); - newzone->rrsets[DNSType::A].add(AGen::make("1.2.3.4")); - newzone->rrsets[DNSType::AAAA].add(AAAAGen::make("::1")); + newzone->addRRs(AGen::make("1.2.3.4")); + newzone->addRRs(AAAAGen::make("::1")); newzone->rrsets[DNSType::AAAA].ttl= 900; - newzone->rrsets[DNSType::NS].add(NSGen::make({"ns1", "powerdns", "org"})); - newzone->addRRs(TXTGen::make("Proudly served by tdns " __DATE__ " " __TIME__)); - + newzone->addRRs(NSGen::make({"ns1", "powerdns", "org"}), NSGen::make({"ns2", "powerdns", "org"})); + newzone->addRRs(TXTGen::make("Proudly served by tdns compiled on " __DATE__ " " __TIME__), + TXTGen::make("This is some more filler to make this packet exceed 512 bytes")); + newzone->add({"www"})->rrsets[DNSType::CNAME].add(CNAMEGen::make({"server1","powerdns","org"})); newzone->add({"www2"})->rrsets[DNSType::CNAME].add(CNAMEGen::make({"nosuchserver1","powerdns","org"})); @@ -25,7 +26,7 @@ void loadZones(DNSNode& zones) newzone->add({"*", "fr"})->rrsets[DNSType::CNAME].add(CNAMEGen::make({"server2", "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", "fra"})->addRRs(AGen::make("12.13.14.15")); newzone->add({"NS2", "fra"})->addRRs(AGen::make("12.13.14.16"), AAAAGen::make("::1")); newzone->add({"something"})->addRRs(AAAAGen::make("::1"), AGen::make("12.13.14.15")); diff --git a/tdns/dns-types.cc b/tdns/dns-types.cc index 326e26b..ab269aa 100644 --- a/tdns/dns-types.cc +++ b/tdns/dns-types.cc @@ -7,7 +7,7 @@ std::unique_ptr AGen::make(const ComboAddress& ca) void AGen::toMessage(DNSMessageWriter& dmw) { - dmw.payload.putUInt32(d_ip); + dmw.putUInt32(d_ip); } std::unique_ptr AAAAGen::make(const ComboAddress& ca) @@ -23,39 +23,39 @@ std::unique_ptr AAAAGen::make(const ComboAddress& ca) void AAAAGen::toMessage(DNSMessageWriter& dmw) { - dmw.payload.putBlob(d_ip, 16); + dmw.putBlob(d_ip, 16); } void SOAGen::toMessage(DNSMessageWriter& dmw) { - putName(dmw.payload, d_mname); putName(dmw.payload, d_rname); - dmw.payload.putUInt32(d_serial); dmw.payload.putUInt32(d_refresh); - dmw.payload.putUInt32(d_retry); dmw.payload.putUInt32(d_expire); - dmw.payload.putUInt32(d_minimum); + dmw.putName(d_mname); dmw.putName(d_rname); + dmw.putUInt32(d_serial); dmw.putUInt32(d_refresh); + dmw.putUInt32(d_retry); dmw.putUInt32(d_expire); + dmw.putUInt32(d_minimum); } void CNAMEGen::toMessage(DNSMessageWriter& dmw) { - putName(dmw.payload, d_name); + dmw.putName(d_name); } void NSGen::toMessage(DNSMessageWriter& dmw) { - putName(dmw.payload, d_name); + dmw.putName(d_name); } void MXGen::toMessage(DNSMessageWriter& dmw) { - dmw.payload.putUInt16(d_prio); - putName(dmw.payload, d_name); + dmw.putUInt16(d_prio); + dmw.putName(d_name); } void TXTGen::toMessage(DNSMessageWriter& dmw) { // XXX should autosplit - dmw.payload.putUInt8(d_txt.length()); - dmw.payload.putBlob(d_txt); + dmw.putUInt8(d_txt.length()); + dmw.putBlob(d_txt); } void ClockTXTGen::toMessage(DNSMessageWriter& dmw) @@ -70,6 +70,6 @@ void ClockTXTGen::toMessage(DNSMessageWriter& dmw) else txt="Overflow"; // XXX should autosplit - dmw.payload.putUInt8(txt.length()); - dmw.payload.putBlob(txt); + dmw.putUInt8(txt.length()); + dmw.putBlob(txt); } diff --git a/tdns/dnsmessages.cc b/tdns/dnsmessages.cc index 1d503f0..f49eeec 100644 --- a/tdns/dnsmessages.cc +++ b/tdns/dnsmessages.cc @@ -27,17 +27,17 @@ void DNSMessageReader::getQuestion(dnsname& name, DNSType& type) void DNSMessageWriter::putRR(DNSSection section, const dnsname& name, DNSType type, uint32_t ttl, const std::unique_ptr& content) { - auto cursize = payload.payloadpos; + auto cursize = payloadpos; try { - putName(payload, name); - payload.putUInt16((int)type); payload.putUInt16(1); - payload.putUInt32(ttl); - auto pos = payload.putUInt16(0); // placeholder + putName(name); + putUInt16((int)type); putUInt16(1); + putUInt32(ttl); + auto pos = putUInt16(0); // placeholder content->toMessage(*this); - payload.putUInt16At(pos, payload.payloadpos-pos-2); + putUInt16At(pos, payloadpos-pos-2); } catch(...) { - payload.payloadpos = cursize; + payloadpos = cursize; throw; } switch(section) { @@ -58,10 +58,10 @@ void DNSMessageWriter::putRR(DNSSection section, const dnsname& name, DNSType ty void DNSMessageWriter::setQuestion(const dnsname& name, DNSType type) { dh.ancount = dh.arcount = dh.nscount = 0; - payload.rewind(); - putName(payload, name); - payload.putUInt16((uint16_t)type); - payload.putUInt16(1); // class + payloadpos=0; + putName(name); + putUInt16((uint16_t)type); + putUInt16(1); // class } string DNSMessageReader::serialize() const @@ -70,7 +70,9 @@ string DNSMessageReader::serialize() const } string DNSMessageWriter::serialize() const { - return string((const char*)this, (const char*)this + sizeof(dnsheader) + payload.payloadpos); + std::string ret((const char*)this, (const char*)this + sizeof(dnsheader)); + ret.append((const unsigned char*)&payload[0], (const unsigned char*)&payload[payloadpos]); + return ret; } static_assert(sizeof(DNSMessageReader) == 516, "dnsmessagereader size must be 516"); diff --git a/tdns/dnsmessages.hh b/tdns/dnsmessages.hh index b145ed8..aa1cc4e 100644 --- a/tdns/dnsmessages.hh +++ b/tdns/dnsmessages.hh @@ -2,6 +2,7 @@ #include "dns.hh" #include "safearray.hh" #include "dns-storage.hh" +#include struct DNSMessageReader { @@ -16,20 +17,63 @@ struct DNSMessageReader struct DNSMessageWriter { + explicit DNSMessageWriter(int maxsize=512) + { + payload.resize(maxsize); + } struct dnsheader dh=dnsheader{}; - SafeArray<1500> payload; + std::vector payload; void setQuestion(const dnsname& name, DNSType type); void putRR(DNSSection section, const dnsname& name, DNSType type, uint32_t ttl, const std::unique_ptr& rr); std::string serialize() const; + + uint16_t payloadpos=0; + void putUInt8(uint8_t val) + { + payload.at(payloadpos++)=val; + } + + uint16_t putUInt16(uint16_t val) + { + val = htons(val); + memcpy(&payload.at(payloadpos+2)-2, &val, 2); + payloadpos+=2; + return payloadpos - 2; + } + + void putUInt16At(uint16_t pos, uint16_t val) + { + val = htons(val); + memcpy(&payload.at(pos+2)-2, &val, 2); + } + + void putUInt32(uint32_t val) + { + val = htonl(val); + memcpy(&payload.at(payloadpos+sizeof(val)) - sizeof(val), &val, sizeof(val)); + payloadpos += sizeof(val); + } + + void putBlob(const std::string& blob) + { + memcpy(&payload.at(payloadpos+blob.size()) - blob.size(), blob.c_str(), blob.size()); + payloadpos += blob.size();; + } + + void putBlob(const unsigned char* blob, int size) + { + memcpy(&payload.at(payloadpos+size) - size, blob, size); + payloadpos += size; + } + void putName(const dnsname& name) + { + for(const auto& l : name) { + putUInt8(l.size()); + putBlob(l.d_s); + } + putUInt8(0); + } + + }; -inline void putName(SafeArray<1500>& payload, const dnsname& name) - { - for(const auto& l : name) { - if(l.size() > 63) - throw std::runtime_error("Can't emit a label larger than 63 characters"); - payload.putUInt8(l.size()); - payload.putBlob(l.d_s); - } - payload.putUInt8(0); -} diff --git a/tdns/safearray.hh b/tdns/safearray.hh index 713386c..f2dc3d6 100644 --- a/tdns/safearray.hh +++ b/tdns/safearray.hh @@ -27,46 +27,8 @@ struct SafeArray memcpy(&ret, &payload.at(payloadpos+2)-2, 2); payloadpos+=2; return htons(ret); - } - void putUInt8(uint8_t val) - { - payload.at(payloadpos++)=val; - } - - uint16_t putUInt16(uint16_t val) - { - val = htons(val); - memcpy(&payload.at(payloadpos+2)-2, &val, 2); - payloadpos+=2; - return payloadpos - 2; - } - - void putUInt16At(uint16_t pos, uint16_t val) - { - val = htons(val); - memcpy(&payload.at(pos+2)-2, &val, 2); - } - - void putUInt32(uint32_t val) - { - val = htonl(val); - memcpy(&payload.at(payloadpos+sizeof(val)) - sizeof(val), &val, sizeof(val)); - payloadpos += sizeof(val); - } - - void putBlob(const std::string& blob) - { - memcpy(&payload.at(payloadpos+blob.size()) - blob.size(), blob.c_str(), blob.size()); - payloadpos += blob.size();; - } - - void putBlob(const unsigned char* blob, int size) - { - memcpy(&payload.at(payloadpos+size) - size, blob, size); - payloadpos += size; - } std::string getBlob(int size) { diff --git a/tdns/tdns.cc b/tdns/tdns.cc index e1069ca..17bf789 100644 --- a/tdns/tdns.cc +++ b/tdns/tdns.cc @@ -19,13 +19,12 @@ using namespace std; void addAdditional(const DNSNode* bestzone, const dnsname& zone, const vector& toresolve, DNSMessageWriter& response) { for(auto addname : toresolve ) { - cout<<"Doing additional or glue lookup for "<find(addname, wuh); if(!addnode || !addname.empty()) { cout<<" Found nothing, continuing"<zone) { - cout<<"---\nBest zone: "<zone<zone; - dnsname searchname(name), lastnode, zonecutname; - const DNSNode* passedZonecut=0; - int CNAMELoopCount = 0; - - loopCNAME:; - lastnode.clear(); - zonecutname.clear(); - auto node = bestzone->find(searchname, lastnode, &passedZonecut, &zonecutname); - - if(!node) { - cout<<"Found nothing in zone '"<rrsets) { - cout<<" Have type "<zone) { + cout<<"---\nBest zone: "<zone<zone; + dnsname searchname(name), lastnode, zonecutname; + const DNSNode* passedZonecut=0; + int CNAMELoopCount = 0; + + loopCNAME:; + auto node = bestzone->find(searchname, lastnode, &passedZonecut, &zonecutname); + + if(!node) { + cout<<"Found nothing in zone '"<rrsets.find(DNSType::NS); - if(iter != passedZonecut->rrsets.end()) { - const auto& rrset = iter->second; - vector toresolve; - for(const auto& rr : rrset.contents) { - response.putRR(DNSSection::Authority, zonecutname+zone, DNSType::NS, rrset.ttl, rr); - toresolve.push_back(dynamic_cast(rr.get())->d_name); + else if(passedZonecut) { + response.dh.aa = false; + cout<<"This is a delegation, zonecutname: '"<rrsets) { + cout<<" Have type "<zone->rrsets[DNSType::SOA]; - response.dh.rcode = (int)RCode::Nxdomain; - response.putRR(DNSSection::Authority, zone, DNSType::SOA, rrset.ttl, rrset.contents[0]); - } - else { - cout<<"Found something in zone '"<second; - dnsname target; - for(const auto& rr : rrset.contents) { - response.putRR(DNSSection::Answer, lastnode+zone, DNSType::CNAME, rrset.ttl, rr); - target=dynamic_cast(rr.get())->d_name; - } - if(target.makeRelative(zone)) { - cout<<" Should follow CNAME to "<zone->rrsets[DNSType::SOA]; + response.dh.rcode = (int)RCode::Nxdomain; + response.putRR(DNSSection::Authority, zone, DNSType::SOA, rrset.ttl, rrset.contents[0]); } else { - cout<<"Node exists, qtype doesn't, NOERROR situation, inserting SOA"<zone->rrsets[DNSType::SOA]; - response.putRR(DNSSection::Answer, zone, DNSType::SOA, rrset.ttl, rrset.contents[0]); - } - addAdditional(bestzone, zone, additional, response); + cout<<"Found something in zone '"<second; + dnsname target; + for(const auto& rr : rrset.contents) { + response.putRR(DNSSection::Answer, lastnode+zone, DNSType::CNAME, rrset.ttl, rr); + target=dynamic_cast(rr.get())->d_name; + } + if(target.makeRelative(zone)) { + cout<<" Should follow CNAME to "<zone->rrsets[DNSType::SOA]; + response.putRR(DNSSection::Answer, zone, DNSType::SOA, rrset.ttl, rrset.contents[0]); + } + addAdditional(bestzone, zone, additional, response); + + } } + else { + cout<<"No zone matched"<::max()-sizeof(dnsheader)); if(type == DNSType::AXFR) { cout<<"Should do AXFR for "<