lots of work, starting to look useful

This commit is contained in:
bert hubert
2018-04-09 23:04:13 +02:00
parent ab91a50402
commit 2872f88c99
7 changed files with 106 additions and 76 deletions

View File

@ -45,10 +45,14 @@ const DNSNode* DNSNode::find(dnsname& name, dnsname& last, bool* passedZonecut)
return this;
}
else {
cout<<"Had wildcard match, following"<<endl;
cout<<" Had wildcard match, picking that, matching all labels"<<endl;
while(name.size() > 1) {
last.push_front(name.back());
name.pop_back();
}
}
}
cout<<"Had match, continuing to child '"<<iter->first<<"'"<<endl;
cout<<" Had match, continuing to child '"<<iter->first<<"'"<<endl;
last.push_front(name.back());
name.pop_back();
return iter->second.find(name, last, passedZonecut);
@ -56,9 +60,9 @@ const DNSNode* DNSNode::find(dnsname& name, dnsname& last, bool* passedZonecut)
DNSNode* DNSNode::add(dnsname name)
{
cout<<"Add for '"<<name<<"'"<<endl;
cout<<"Add called for '"<<name<<"'"<<endl;
if(name.size() == 1) {
cout<<"Last label, adding "<<name.front()<<endl;
cout<<" Last label, possibly addding, already present="<<children.count(name.front())<<endl;
return &children[name.front()];
}
@ -67,7 +71,7 @@ DNSNode* DNSNode::add(dnsname name)
auto iter = children.find(back);
if(iter == children.end()) {
cout<<"Inserting new child for "<<back<<endl;
cout<<"Inserting new child for "<<back<<", continuing add there"<<endl;
return children[back].add(name);
}
return iter->second.add(name);
@ -91,8 +95,7 @@ void DNSNode::visit(std::function<void(const dnsname& name, const DNSNode*)> vis
// this should perform escaping rules!
std::ostream & operator<<(std::ostream &os, const dnsname& d)
{
for(const auto& l : d.d_name) {
for(const auto& l : d.d_name)
os<<l<<".";
}
return os;
}

View File

@ -27,10 +27,11 @@ enum class DNSType : uint16_t
};
SMARTENUMSTART(DNSType)
SENUM13(DNSType, A, NS, CNAME, SOA, PTR, MX, AAAA, IXFR, AAAA, SRV, IXFR, AXFR, ANY)
SENUM13(DNSType, A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, IXFR, AAAA, SRV, IXFR, AXFR)
SENUM(DNSType, ANY)
SMARTENUMEND(DNSType)
COMBOENUM4(DNSSection, Question, 0, Answer, 1, Authority, 2, Additional,3 )
COMBOENUM4(DNSSection, Question, 0, Answer, 1, Authority, 2, Additional, 3)
struct dnsname
{
@ -45,7 +46,7 @@ struct dnsname
void pop_back() { d_name.pop_back(); }
auto push_front(const dnslabel& dn) { return d_name.push_front(dn); }
auto size() { return d_name.size(); }
void clear() { d_name.clear(); }
bool makeRelative(const dnsname& root);
std::deque<dnslabel> d_name;
@ -60,7 +61,6 @@ struct RRGenerator
virtual void toMessage(DNSMessageWriter& dpw) = 0;
};
struct RRSet
{
std::vector<std::unique_ptr<RRGenerator>> contents;

View File

@ -10,7 +10,6 @@ void AGenerator::toMessage(DNSMessageWriter& dmw)
dmw.payload.putUInt32(d_ip);
}
std::unique_ptr<RRGenerator> AAAAGenerator::make(const ComboAddress& ca)
{
if(ca.sin4.sin_family != AF_INET6)
@ -46,4 +45,9 @@ void MXGenerator::toMessage(DNSMessageWriter& dmw)
putName(dmw.payload, d_name);
}
void TXTGenerator::toMessage(DNSMessageWriter& dmw)
{
// XXX should autosplit
dmw.payload.putUInt8(d_txt.length());
dmw.payload.putBlob(d_txt);
}

View File

@ -69,3 +69,14 @@ struct MXGenerator : RRGenerator
uint16_t d_prio;
dnsname d_name;
};
struct TXTGenerator : RRGenerator
{
TXTGenerator(const std::string& txt) : d_txt(txt) {}
static std::unique_ptr<RRGenerator> make(const std::string& txt)
{
return std::move(std::make_unique<TXTGenerator>(txt));
}
void toMessage(DNSMessageWriter& dpw) override;
std::string d_txt;
};

View File

@ -57,6 +57,7 @@ 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);

View File

@ -1,5 +1,4 @@
#pragma once
#include <cstdint>
#include <iostream>
#include <stdexcept>
@ -7,7 +6,6 @@
#include <array>
#include <string.h>
#define SMARTENUMSTART(x) static constexpr std::pair<x, const char*> enumtypemap##x[]= {
#define SENUM(x,a1) { x::a1, #a1},
#define SENUM2(x, a1, ...) SENUM(x,a1) SENUM(x, __VA_ARGS__)
@ -40,7 +38,6 @@ for(const auto& a : enumtypemap##x) \
inline std::ostream& operator<<(std::ostream &os, const x& s) { \
os << toString(s); return os; } \
#define COMBOENUM4(x, a1,b1,a2,b2,a3,b3,a4,b4) enum class x : uint16_t { \
a1=b1, a2=b2, a3=b3, a4=b4 }; SMARTENUMSTART(x) SENUM4(x, a1, a2, a3,a4) \
SMARTENUMEND(x)

View File

@ -1,11 +1,9 @@
/* Goal: a fully standards compliant basic authoritative server. In <500 lines.
/* Goal: a fully standards compliant basic authoritative server. In <1000 lines.
Non-goals: notifications, slaving zones, name compression, edns,
performance
*/
#include <cstdint>
#include <string>
#include <vector>
#include <deque>
#include <map>
#include <stdexcept>
#include "sclasses.hh"
@ -18,6 +16,33 @@
using namespace std;
void addAdditional(const DNSNode* bestzone, const dnsname& zone, const vector<dnsname>& toresolve, DNSMessageWriter& response)
{
for(auto addname : toresolve ) {
cout<<"Doing additional or glue lookup for "<<addname<<endl;
if(!addname.makeRelative(zone)) {
cout<<addname<<" is not within our zone, not doing glue"<<endl;
continue;
}
dnsname wuh;
cout<<"Looking up glue record "<<addname<<" in zone "<<zone<<endl;
auto addnode = bestzone->find(addname, wuh);
if(!addnode || !addname.empty()) {
cout<<" Found nothing, continuing"<<endl;
continue;
}
for(auto& type : {DNSType::A, DNSType::AAAA}) {
auto iter2 = addnode->rrsets.find(type);
if(iter2 != addnode->rrsets.end()) {
const auto& rrset = iter2->second;
for(const auto& rr : rrset.contents) {
response.putRR(DNSSection::Additional, wuh+zone, type, rrset.ttl, rr);
}
}
}
}
}
bool processQuestion(const DNSNode& zones, DNSMessageReader& dm, const ComboAddress& local, const ComboAddress& remote, DNSMessageWriter& response)
try
{
@ -27,14 +52,11 @@ try
cout<<"Received a query from "<<remote.toStringWithPort()<<" for "<<name<<" and type "<<type<<endl;
response.dh = dm.dh;
response.dh.ad = 0;
response.dh.ra = 0;
response.dh.aa = 0;
response.dh.ad = response.dh.ra = response.dh.aa = 0;
response.dh.qr = 1;
response.dh.ancount = response.dh.arcount = response.dh.nscount = 0;
response.setQuestion(name, type);
if(type == DNSType::AXFR) {
if(type == DNSType::AXFR || type == DNSType::IXFR) {
cout<<"Query was for AXFR or IXFR over UDP, can't do that"<<endl;
response.dh.rcode = (int)RCode::Servfail;
return true;
@ -58,6 +80,8 @@ try
bool passedZonecut=false;
int CNAMELoopCount = 0;
loopCNAME:;
passedZonecut=false;
lastnode.clear();
auto node = bestzone->find(searchname, lastnode, &passedZonecut);
if(passedZonecut)
response.dh.aa = false;
@ -81,26 +105,7 @@ try
toresolve.push_back(dynamic_cast<NameGenerator*>(rr.get())->d_name);
}
for(auto& addname : toresolve ) {
if(!addname.makeRelative(zone)) {
cout<<addname<<" is not within our zone, not doing glue"<<endl;
continue;
}
dnsname wuh;
cout<<"Looking up glue record "<<addname<<" in zone "<<zone<<endl;
auto addnode = bestzone->find(addname, wuh);
if(!addnode || !addname.empty()) {
cout<<"Found nothing, continuing"<<endl;
}
auto iter2 = addnode->rrsets.find(DNSType::A);
if(iter2 != addnode->rrsets.end()) {
cout<<"Lastnode for '"<<addname<<"' glue: "<<wuh<<endl;
const auto& rrset = iter2->second;
for(const auto& rr : rrset.contents) {
response.putRR(DNSSection::Additional, wuh+zone, DNSType::A, rrset.ttl, rr);
}
}
}
addAdditional(bestzone, zone, toresolve, response);
}
else {
cout<<"This is an NXDOMAIN situation"<<endl;
@ -123,9 +128,13 @@ try
}
else if(iter = node->rrsets.find(type), iter != node->rrsets.end()) {
const auto& rrset = iter->second;
vector<dnsname> additional;
for(const auto& rr : rrset.contents) {
response.putRR(DNSSection::Answer, lastnode+zone, type, rrset.ttl, rr);
if(type == DNSType::MX)
additional.push_back(dynamic_cast<MXGenerator*>(rr.get())->d_name);
}
addAdditional(bestzone, zone, additional, response);
}
else if(iter = node->rrsets.find(DNSType::CNAME), iter != node->rrsets.end()) {
cout<<"We do have a CNAME!"<<endl;
@ -137,6 +146,7 @@ try
}
if(target.makeRelative(zone)) {
cout<<" Should follow CNAME to "<<target<<" within our zone"<<endl;
// XXX we need to change our behaviour on NXDOMAIN I think depending on if you've followed a CNAME
searchname = target;
if(CNAMELoopCount++ < 10)
goto loopCNAME;
@ -239,17 +249,14 @@ void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNo
dnsname zone;
auto fnd = zones->find(name, zone);
if(!fnd || !fnd->zone || !name.empty()) {
cout<<" This was not a zone"<<endl;
if(!fnd || !fnd->zone || !name.empty() || !fnd->zone->rrsets.count(DNSType::SOA)) {
cout<<" This was not a zone, or zone had no SOA"<<endl;
return;
}
cout<<"Have zone, walking it"<<endl;
response.dh = dm.dh;
response.dh.ad = 0;
response.dh.ra = 0;
response.dh.aa = 0;
response.dh.ad = response.dh.ra = response.dh.aa = 0;
response.dh.qr = 1;
response.dh.ancount = response.dh.arcount = response.dh.nscount = 0;
response.setQuestion(zone, type);
auto node = fnd->zone;
@ -258,13 +265,10 @@ void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNo
response.putRR(DNSSection::Answer, zone, DNSType::SOA, node->rrsets[DNSType::SOA].ttl, node->rrsets[DNSType::SOA].contents[0]);
writeTCPResponse(sock, response);
response.dh.ancount = response.dh.arcount = response.dh.nscount = 0;
response.payload.rewind();
response.setQuestion(zone, type);
// send all other records
node->visit([&response,&sock,&name,&type,&zone](const dnsname& nname, const DNSNode* n) {
cout<<nname<<", types: ";
for(const auto& p : n->rrsets) {
if(p.first == DNSType::SOA)
continue;
@ -275,20 +279,14 @@ void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNo
}
catch(...) { // exceeded packet size
writeTCPResponse(sock, response);
response.dh.ancount = response.dh.arcount = response.dh.nscount = 0;
response.payload.rewind();
response.setQuestion(zone, type);
goto retry;
}
}
cout<<p.first<<" ";
}
cout<<endl;
}, zone);
writeTCPResponse(sock, response);
response.dh.ancount = response.dh.arcount = response.dh.nscount = 0;
response.payload.rewind();
response.setQuestion(zone, type);
// send SOA again
@ -312,32 +310,43 @@ void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNo
void loadZones(DNSNode& zones)
{
auto zone = zones.add({"powerdns", "org"});
zone->zone = new DNSNode(); // XXX ICK
auto newzone = zone->zone = new DNSNode(); // XXX ICK
zone->zone->rrsets[DNSType::SOA].add(SOAGenerator::make({"ns1", "powerdns", "org"}, {"admin", "powerdns", "org"}, 1));
zone->zone->rrsets[DNSType::MX].add(MXGenerator::make(25, {"server1", "powerdns", "org"}));
newzone->rrsets[DNSType::SOA].add(SOAGenerator::make({"ns1", "powerdns", "org"}, {"admin", "powerdns", "org"}, 1));
newzone->rrsets[DNSType::MX].add(MXGenerator::make(25, {"server1", "powerdns", "org"}));
zone->zone->rrsets[DNSType::A].add(AGenerator::make("1.2.3.4"));
zone->zone->rrsets[DNSType::AAAA].add(AAAAGenerator::make("::1"));
zone->zone->rrsets[DNSType::AAAA].ttl= 900;
zone->zone->rrsets[DNSType::NS].add(NameGenerator::make({"ns1", "powerdns", "org"}));
newzone->rrsets[DNSType::A].add(AGenerator::make("1.2.3.4"));
newzone->rrsets[DNSType::AAAA].add(AAAAGenerator::make("::1"));
newzone->rrsets[DNSType::AAAA].ttl= 900;
newzone->rrsets[DNSType::NS].add(NameGenerator::make({"ns1", "powerdns", "org"}));
newzone->rrsets[DNSType::TXT].add(TXTGenerator::make("Proudly served by tdns " __DATE__ " " __TIME__));
zone->zone->add({"www"})->rrsets[DNSType::CNAME].add(NameGenerator::make({"server1","powerdns","org"}));
newzone->add({"www"})->rrsets[DNSType::CNAME].add(NameGenerator::make({"server1","powerdns","org"}));
newzone->add({"www2"})->rrsets[DNSType::CNAME].add(NameGenerator::make({"nosuchserver1","powerdns","org"}));
zone->zone->add({"server1"})->rrsets[DNSType::A].add(AGenerator::make("213.244.168.210"));
zone->zone->add({"server1"})->rrsets[DNSType::AAAA].add(AAAAGenerator::make("::1"));
newzone->add({"server1"})->rrsets[DNSType::A].add(AGenerator::make("213.244.168.210"));
newzone->add({"server1"})->rrsets[DNSType::AAAA].add(AAAAGenerator::make("::1"));
// zone->zone->add({"*"})->rrsets[(dnstype)DNSType::A]={"\x05\x06\x07\x08"};
newzone->add({"server2"})->rrsets[DNSType::A].add(AGenerator::make("213.244.168.210"));
newzone->add({"server2"})->rrsets[DNSType::AAAA].add(AAAAGenerator::make("::1"));
zone->zone->add({"fra"})->rrsets[DNSType::NS].add(NameGenerator::make({"ns1","fra","powerdns","org"}));
zone->zone->add({"fra"})->rrsets[DNSType::NS].add(NameGenerator::make({"ns2","fra","powerdns","org"}));
newzone->add({"*", "nl"})->rrsets[DNSType::A].add(AGenerator::make("5.6.7.8"));
newzone->add({"*", "fr"})->rrsets[DNSType::CNAME].add(NameGenerator::make({"server2", "powerdns", "org"}));
zone->zone->add({"ns1", "fra"})->rrsets[DNSType::A].add(AGenerator::make("12.13.14.15"));
zone->zone->add({"NS2", "fra"})->rrsets[DNSType::A].add(AGenerator::make("12.13.14.16"));
newzone->add({"fra"})->rrsets[DNSType::NS].add(NameGenerator::make({"ns1","fra","powerdns","org"}));
newzone->add({"fra"})->rrsets[DNSType::NS].add(NameGenerator::make({"ns2","fra","powerdns","org"}));
newzone->add({"ns1", "fra"})->rrsets[DNSType::A].add(AGenerator::make("12.13.14.15"));
newzone->add({"NS2", "fra"})->rrsets[DNSType::A].add(AGenerator::make("12.13.14.16"));
}
int main(int argc, char** argv)
try
{
if(argc != 2) {
cerr<<"Syntax: tdns ipaddress:port"<<endl;
return(EXIT_FAILURE);
}
signal(SIGPIPE, SIG_IGN);
ComboAddress local(argv[1], 53);
@ -361,3 +370,8 @@ int main(int argc, char** argv)
t.detach();
}
}
catch(std::exception& e)
{
cerr<<"Fatal error: "<<e.what()<<endl;
return EXIT_FAILURE;
}