lots of work, starting to look useful
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
130
tdns/tdns.cc
130
tdns/tdns.cc
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user