From 7afca8c957693d09fa41824b5fb9ac1944fcd232 Mon Sep 17 00:00:00 2001 From: bert hubert Date: Tue, 3 Apr 2018 13:18:37 +0200 Subject: [PATCH] tdns work --- tdns/Makefile | 4 +- tdns/safearray.hh | 6 +- tdns/tdns.cc | 252 +++++++++++++++++++++++++++++++++------------- 3 files changed, 190 insertions(+), 72 deletions(-) diff --git a/tdns/Makefile b/tdns/Makefile index b00c161..0591f56 100644 --- a/tdns/Makefile +++ b/tdns/Makefile @@ -1,4 +1,4 @@ -CXXFLAGS:=-std=gnu++14 -Wall -O2 -MMD -MP -ggdb -Iext/simplesocket +CXXFLAGS:=-std=gnu++14 -Wall -O2 -MMD -MP -ggdb -Iext/simplesocket -pthread PROGRAMS = tdns @@ -13,4 +13,4 @@ clean: -include *.d tdns: tdns.o ext/simplesocket/comboaddress.o ext/simplesocket/sclasses.o ext/simplesocket/swrappers.o - g++ -std=gnu++14 $^ -o $@ + g++ -std=gnu++14 $^ -o $@ -pthread diff --git a/tdns/safearray.hh b/tdns/safearray.hh index 0f74335..3907e61 100644 --- a/tdns/safearray.hh +++ b/tdns/safearray.hh @@ -53,5 +53,9 @@ struct SafeArray payloadpos += size; return ret; } - + + std::string serialize() const + { + return std::string((const char*)&payload.at(0), (const char*)&payload.at(payloadpos)); + } }; diff --git a/tdns/tdns.cc b/tdns/tdns.cc index ea61d4f..dfb9d94 100644 --- a/tdns/tdns.cc +++ b/tdns/tdns.cc @@ -11,24 +11,25 @@ #include "sclasses.hh" #include "dns.hh" #include "safearray.hh" +#include using namespace std; -typedef uint16_t dnstype; typedef std::string dnslabel; enum class RCode { - Refused=5 + Noerror = 0, Servfail =2, Nxdomain =3, Notimp = 4, Refused = 5 }; -enum class DNSType +enum class DNSType : uint16_t { - A = 1, - NS = 2, - CNAME = 5, - SOA=6, - AAAA = 28 + A = 1, NS = 2, CNAME = 5, SOA=6, AAAA = 28, IXFR = 251, AXFR = 252, ANY = 255 +}; + +enum class DNSSection +{ + Question, Answer, Authority, Additional }; typedef deque dnsname; @@ -41,20 +42,26 @@ static std::ostream & operator<<(std::ostream &os, const dnsname& d) return os; } +struct RRSet +{ + vector contents; + uint32_t ttl{3600}; +}; + struct DNSNode { - DNSNode* find(dnsname& name, dnsname& last, bool* passedZonecut=0); + const DNSNode* find(dnsname& name, dnsname& last, bool* passedZonecut=0) const; DNSNode* add(dnsname name); map children; - map > rrsets; + map rrsets; DNSNode* zone{0}; // if this is set, this node is a zone }; -DNSNode* DNSNode::find(dnsname& name, dnsname& last, bool* passedZonecut) +const DNSNode* DNSNode::find(dnsname& name, dnsname& last, bool* passedZonecut) const { cout<<"find for '"<second.add(name); } - - struct DNSMessage { struct dnsheader dh=dnsheader{}; SafeArray<500> payload; dnsname getName(); - void putName(const dnsname& name); - void getQuestion(dnsname& name, dnstype& type); - void setQuestion(const dnsname& name, dnstype type); - void putRR(const dnsname& name, uint16_t type, uint32_t ttl, const std::string& rr); - std::string serialize() const; -} __attribute__((packed)); + void getQuestion(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::string& rr); + + string serialize() const; +}; // __attribute__((packed)); dnsname DNSMessage::getName() { @@ -136,35 +141,50 @@ dnsname DNSMessage::getName() return name; } - -void DNSMessage::getQuestion(dnsname& name, dnstype& type) +void DNSMessage::getQuestion(dnsname& name, DNSType& type) { name=getName(); - type=payload.getUInt16(); + type=(DNSType)payload.getUInt16(); } -void DNSMessage::putName(const dnsname& name) +void putName(auto& 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); } payload.putUInt8(0); } -void DNSMessage::putRR(const dnsname& name, uint16_t type, uint32_t ttl, const std::string& content) +void DNSMessage::putRR(DNSSection section, const dnsname& name, DNSType type, uint32_t ttl, const std::string& content) { - putName(name); - payload.putUInt16(type); payload.putUInt16(1); + putName(payload, name); + payload.putUInt16((int)type); payload.putUInt16(1); payload.putUInt32(ttl); payload.putUInt16(content.size()); // check for overflow! payload.putBlob(content); + + switch(section) { + case DNSSection::Question: + throw runtime_error("Can't add questions to a DNS Message with putRR"); + case DNSSection::Answer: + dh.ancount = htons(ntohs(dh.ancount) + 1); + break; + case DNSSection::Authority: + dh.nscount = htons(ntohs(dh.nscount) + 1); + break; + case DNSSection::Additional: + dh.arcount = htons(ntohs(dh.arcount) + 1); + break; + } } -void DNSMessage::setQuestion(const dnsname& name, dnstype type) +void DNSMessage::setQuestion(const dnsname& name, DNSType type) { - putName(name); - payload.putUInt16(type); + putName(payload, name); + payload.putUInt16((uint16_t)type); payload.putUInt16(1); // class } @@ -172,7 +192,7 @@ string DNSMessage::serialize() const { return string((const char*)this, (const char*)this + sizeof(dnsheader) + payload.payloadpos); } - + static_assert(sizeof(DNSMessage) == 516, "dnsmessage size must be 516"); @@ -187,6 +207,39 @@ std::string serializeDNSName(const dnsname& dn) return ret; } +std::string serializeSOARecord(const dnsname& mname, const dnsname& rname, uint32_t serial, uint32_t minimum=3600, uint32_t refresh=10800, uint32_t retry=3600, uint32_t expire=604800) +{ + SafeArray<256> sa; + putName(sa, mname); + putName(sa, rname); + sa.putUInt32(serial); + sa.putUInt32(refresh); + sa.putUInt32(retry); + sa.putUInt32(expire); + sa.putUInt32(minimum); + + return sa.serialize(); +} + +std::string serializeARecord(const std::string& src) +{ + ComboAddress ca(src); + if(ca.sin4.sin_family != AF_INET) + throw std::runtime_error("Could not convert '"+src+"' to an IPv4 address"); + auto p = (const char*)&ca.sin4.sin_addr.s_addr; + return std::string(p, p+4); +} + +std::string serializeAAAARecord(const std::string& src) +{ + ComboAddress ca(src); + if(ca.sin4.sin_family != AF_INET6) + throw std::runtime_error("Could not convert '"+src+"' to an IPv6 address"); + auto p = (const char*)ca.sin6.sin6_addr.s6_addr; + return std::string(p, p+16); +} + + dnsname operator+(const dnsname& a, const dnsname& b) { dnsname ret=a; @@ -195,27 +248,11 @@ dnsname operator+(const dnsname& a, const dnsname& b) return ret; } -int main(int argc, char** argv) +void udpThread(ComboAddress local, const DNSNode* zones) { - ComboAddress local(argv[1], 53); Socket udplistener(local.sin4.sin_family, SOCK_DGRAM); SBind(udplistener, local); - DNSNode zones; - auto zone = zones.add({"powerdns", "org"}); - zone->zone = new DNSNode(); // XXX ICK - zone->zone->rrsets[(dnstype)DNSType::SOA]={"hello"}; - zone->zone->rrsets[(dnstype)DNSType::A]={"\x01\x02\x03\x04"}; - - zone->zone->add({"www"})->rrsets[(dnstype)DNSType::CNAME]={serializeDNSName({"server1","powerdns","com"})}; - - // zone->zone->add({"*"})->rrsets[(dnstype)DNSType::A]={"\x05\x06\x07\x08"}; - - zone->zone->add({"fra"})->rrsets[(dnstype)DNSType::NS]={serializeDNSName({"ns1","fra","powerdns","org"})}; - - zone->zone->add({"ns1", "fra"})->rrsets[(dnstype)DNSType::A]={"\x05\x06\x07\x08"}; - - for(;;) { ComboAddress remote(local); DNSMessage dm; @@ -226,15 +263,17 @@ int main(int argc, char** argv) } memcpy(&dm, message.c_str(), message.size()); - if(dm.dh.qr || dm.dh.opcode) { + if(dm.dh.qr) { cerr<<"Dropping non-query from "<find(name, zone); if(fnd && fnd->zone) { cout<<"---\nBest zone: "<zone<find(searchname, lastnode, &passedZonecut); + if(passedZonecut) + response.dh.aa = false; + if(!node) { cout<<"Found nothing in zone '"<rrsets) { - cout<<" Have type "<rrsets.count((int)DNSType::NS)) { - for(const auto& rr : node->rrsets[(int)DNSType::NS]) { - response.putRR(lastnode+zone, (int)DNSType::NS, 3600, rr); - response.dh.nscount = htons(ntohs(response.dh.ancount)+1); + auto iter = node->rrsets.find(DNSType::NS); + if(iter != node->rrsets.end() && passedZonecut) { + cout<<"Have delegation"<second; + for(const auto& rr : rrset.contents) { + response.putRR(DNSSection::Answer, lastnode+zone, DNSType::NS, rrset.ttl, rr); } // should do additional processing here } + else { + cout<<"This is an NXDOMAIN situation"<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 '"<rrsets[type]) { - response.putRR(lastnode+zone, type, 3600, rr); - response.dh.ancount = htons(ntohs(response.dh.ancount)+1); + auto iter = node->rrsets.cbegin(); + if(type == DNSType::ANY) { + for(const auto& t : node->rrsets) { + const auto& rrset = t.second; + for(const auto& rr : rrset.contents) { + response.putRR(DNSSection::Answer, lastnode+zone, t.first, rrset.ttl, rr); + } } } - else if(node->rrsets.count((int)DNSType::CNAME)) { + else if(iter = node->rrsets.find(type), iter != node->rrsets.end()) { + const auto& rrset = iter->second; + for(const auto& rr : rrset.contents) { + response.putRR(DNSSection::Answer, lastnode+zone, type, rrset.ttl, rr); + } + } + else if(iter = node->rrsets.find(DNSType::CNAME), iter != node->rrsets.end()) { cout<<"We do have a CNAME!"<rrsets[(int)DNSType::CNAME]) { - response.putRR(lastnode+zone, (int)DNSType::CNAME, 3600, rr); - response.dh.ancount = htons(ntohs(response.dh.ancount)+1); + const auto& rrset = iter->second; + for(const auto& rr : rrset.contents) { + response.putRR(DNSSection::Answer, lastnode+zone, DNSType::CNAME, rrset.ttl, rr); } + cout<<" We should actually follow this, at least within our zone"<zone->rrsets[DNSType::SOA]; + response.putRR(DNSSection::Answer, zone, DNSType::SOA, rrset.ttl, rrset.contents[0]); } - } } else { @@ -298,4 +375,41 @@ int main(int argc, char** argv) } SSendto(udplistener, response.serialize(), remote); } + +} + +void loadZones(DNSNode& zones) +{ + auto zone = zones.add({"powerdns", "org"}); + zone->zone = new DNSNode(); // XXX ICK + zone->zone->rrsets[DNSType::SOA]={{serializeSOARecord({"ns1", "powerdns", "org"}, {"admin", "powerdns", "org"}, 1)}}; + zone->zone->rrsets[DNSType::A]={{serializeARecord("1.2.3.4")}, 300}; + zone->zone->rrsets[DNSType::AAAA]={{serializeAAAARecord("::1"), serializeAAAARecord("2001::1")}, 900}; + zone->zone->rrsets[DNSType::NS]={{serializeDNSName({"ns1", "powerdns", "org"})}, 300}; + + zone->zone->add({"www"})->rrsets[DNSType::CNAME]={{serializeDNSName({"server1","powerdns","org"})}}; + + zone->zone->add({"server1"})->rrsets[DNSType::A]={{serializeARecord("213.244.168.210")}}; + + // zone->zone->add({"*"})->rrsets[(dnstype)DNSType::A]={"\x05\x06\x07\x08"}; + + zone->zone->add({"fra"})->rrsets[DNSType::NS]={{serializeDNSName({"ns1","fra","powerdns","org"})}}; + + zone->zone->add({"ns1", "fra"})->rrsets[DNSType::A]={{serializeARecord("12.13.14.15")}, 86400}; +} + +int main(int argc, char** argv) +{ + ComboAddress local(argv[1], 53); + + DNSNode zones; + + loadZones(zones); + + thread udpServer(udpThread, local, &zones); + // thread tcpServer(tcpThread, local, &zones); + + udpServer.join(); + // tcpServer.join(); + }