fix querying glue directly
This commit is contained in:
58
tdns/README.md
Normal file
58
tdns/README.md
Normal file
@ -0,0 +1,58 @@
|
||||
# teaching DNS
|
||||
Welcome to tdns, the teaching authoritative server, implementing all of
|
||||
basic DNS in 1000 lines of code.
|
||||
|
||||
The goals of tdns are:
|
||||
|
||||
* Protocol correctness
|
||||
* Suitable for educational purposes
|
||||
* Display best practices
|
||||
|
||||
Non-goals are:
|
||||
* Performance
|
||||
* Implementing more features
|
||||
|
||||
# Current status
|
||||
Features are complete:
|
||||
|
||||
* A, AAAA, NS, MX, CNAME, TXT, SOA
|
||||
* UDP & TCP
|
||||
* AXFR
|
||||
* Wildcards
|
||||
* Delegations
|
||||
* Glue records
|
||||
|
||||
Missing:
|
||||
* Truncation
|
||||
* Compression (may not fit in the 1000 lines!)
|
||||
* EDNS (not 'basic' DNS by our definition, but ok)
|
||||
|
||||
Known broken:
|
||||
* Embedded 0s in DNS labels don't yet work
|
||||
* Case-insensitive comparison isn't 100% correct
|
||||
* RCode after one CNAME chase
|
||||
* On output (to screen) we do not escape DNS names correctly
|
||||
|
||||
The code is not yet in a teachable state, and the layout is somewhat
|
||||
confusing: some stuff is in the wrong files.
|
||||
|
||||
# Layout
|
||||
Key to a good DNS implementation is having a faithful DNS storage model.
|
||||
Over the decades, many many nameservers have started out with an incorrect
|
||||
storage model, leading to pain later on with empty non-terminals, setting
|
||||
the 'AA' bit on glue (or not) and eventually DNSSEC ordering problems.
|
||||
|
||||
When storing DNS as a tree, as described in RFC 1034, a lot of things go
|
||||
right "automatically".
|
||||
|
||||
The core or `tdns` therefore is the tree of nodes as intended in 1034. This
|
||||
is implemented in `dns-storage.cc` and `dns-storage.hh`.
|
||||
|
||||
This lookup mechanism will tell you if a name is fully present in a zone, or
|
||||
if it was matched by an NS record. It will also perform wildcard matching,
|
||||
but not CNAME chasing.
|
||||
|
||||
# Best practices
|
||||
The code does not do any form of DNS escaping. Instead, DNS names are stored
|
||||
and manipulated as a sequence of DNS labels. So instead of messing with
|
||||
"www.powerdns.org", we use {"www", "powerdns", "org"}.
|
@ -18,11 +18,15 @@ bool dnsname::makeRelative(const dnsname& root)
|
||||
return true;
|
||||
}
|
||||
|
||||
const DNSNode* DNSNode::find(dnsname& name, dnsname& last, bool* passedZonecut) const
|
||||
const DNSNode* DNSNode::find(dnsname& name, dnsname& last, const DNSNode** passedZonecut, dnsname* zonecutname) const
|
||||
{
|
||||
cout<<"find for '"<<name<<"', last is now '"<<last<<"'"<<endl;
|
||||
if(!last.empty() && passedZonecut && rrsets.count(DNSType::NS)) {
|
||||
*passedZonecut=true;
|
||||
if(!last.empty() && rrsets.count(DNSType::NS)) {
|
||||
cout<<" passed a zonecut, making note of this"<<endl;
|
||||
if(passedZonecut)
|
||||
*passedZonecut=this;
|
||||
if(zonecutname)
|
||||
*zonecutname=last;
|
||||
}
|
||||
|
||||
if(name.empty()) {
|
||||
@ -55,7 +59,7 @@ const DNSNode* DNSNode::find(dnsname& name, dnsname& last, bool* passedZonecut)
|
||||
cout<<" Had match, continuing to child '"<<iter->first<<"'"<<endl;
|
||||
last.push_front(name.back());
|
||||
name.pop_back();
|
||||
return iter->second.find(name, last, passedZonecut);
|
||||
return iter->second.find(name, last, passedZonecut, zonecutname);
|
||||
}
|
||||
|
||||
DNSNode* DNSNode::add(dnsname name)
|
||||
|
@ -81,7 +81,7 @@ struct DNSLabelCompare: public std::binary_function<std::string, std::string, bo
|
||||
|
||||
struct DNSNode
|
||||
{
|
||||
const DNSNode* find(dnsname& name, dnsname& last, bool* passedZonecut=0) const;
|
||||
const DNSNode* find(dnsname& name, dnsname& last, const DNSNode** passedZonecut=0, dnsname* zonecutname=0) const;
|
||||
DNSNode* add(dnsname name);
|
||||
std::map<dnslabel, DNSNode, DNSLabelCompare> children;
|
||||
std::map<DNSType, RRSet > rrsets;
|
||||
|
42
tdns/tdns.cc
42
tdns/tdns.cc
@ -76,43 +76,42 @@ try
|
||||
response.dh.aa = 1;
|
||||
|
||||
auto bestzone = fnd->zone;
|
||||
dnsname searchname(name), lastnode;
|
||||
bool passedZonecut=false;
|
||||
dnsname searchname(name), lastnode, zonecutname;
|
||||
const DNSNode* passedZonecut=0;
|
||||
int CNAMELoopCount = 0;
|
||||
loopCNAME:;
|
||||
passedZonecut=false;
|
||||
lastnode.clear();
|
||||
auto node = bestzone->find(searchname, lastnode, &passedZonecut);
|
||||
if(passedZonecut)
|
||||
response.dh.aa = false;
|
||||
|
||||
loopCNAME:;
|
||||
lastnode.clear();
|
||||
zonecutname.clear();
|
||||
auto node = bestzone->find(searchname, lastnode, &passedZonecut, &zonecutname);
|
||||
|
||||
if(!node) {
|
||||
cout<<"Found nothing in zone '"<<zone<<"' for lhs '"<<name<<"'"<<endl;
|
||||
}
|
||||
else if(!searchname.empty()) {
|
||||
cout<<"This was a partial match, searchname now "<<searchname<<endl;
|
||||
else if(passedZonecut) {
|
||||
response.dh.aa = false;
|
||||
cout<<"This is a delegation, zonecutname: '"<<zonecutname<<"'"<<endl;
|
||||
|
||||
for(const auto& rr: node->rrsets) {
|
||||
for(const auto& rr: passedZonecut->rrsets) {
|
||||
cout<<" Have type "<<rr.first<<endl;
|
||||
}
|
||||
auto iter = node->rrsets.find(DNSType::NS);
|
||||
if(iter != node->rrsets.end() && passedZonecut) {
|
||||
cout<<"Have delegation"<<endl;
|
||||
auto iter = passedZonecut->rrsets.find(DNSType::NS);
|
||||
if(iter != passedZonecut->rrsets.end()) {
|
||||
const auto& rrset = iter->second;
|
||||
vector<dnsname> toresolve;
|
||||
for(const auto& rr : rrset.contents) {
|
||||
response.putRR(DNSSection::Authority, lastnode+zone, DNSType::NS, rrset.ttl, rr);
|
||||
response.putRR(DNSSection::Authority, zonecutname+zone, DNSType::NS, rrset.ttl, rr);
|
||||
toresolve.push_back(dynamic_cast<NameGenerator*>(rr.get())->d_name);
|
||||
}
|
||||
|
||||
addAdditional(bestzone, zone, toresolve, response);
|
||||
}
|
||||
else {
|
||||
cout<<"This is an NXDOMAIN situation"<<endl;
|
||||
const auto& rrset = fnd->zone->rrsets[DNSType::SOA];
|
||||
response.dh.rcode = (int)RCode::Nxdomain;
|
||||
response.putRR(DNSSection::Authority, zone, DNSType::SOA, rrset.ttl, rrset.contents[0]);
|
||||
}
|
||||
}
|
||||
else if(!searchname.empty()) {
|
||||
cout<<"This is an NXDOMAIN situation"<<endl;
|
||||
const auto& rrset = fnd->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 '"<<zone<<"' for lhs '"<<name<<"', searchname now '"<<searchname<<"', lastnode '"<<lastnode<<"', passedZonecut="<<passedZonecut<<endl;
|
||||
@ -338,6 +337,7 @@ void loadZones(DNSNode& zones)
|
||||
|
||||
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"));
|
||||
newzone->add({"NS2", "fra"})->rrsets[DNSType::AAAA].add(AAAAGenerator::make("::1"));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
|
Reference in New Issue
Block a user