more docs, more code
This commit is contained in:
parent
c13f0345b5
commit
619f8ed981
@ -1,6 +1,6 @@
|
||||
CXXFLAGS:=-std=gnu++14 -Wall -O2 -MMD -MP -ggdb -Iext/simplesocket -pthread
|
||||
CXXFLAGS:=-std=gnu++14 -Wall -O2 -MMD -MP -ggdb -Iext/simplesocket -Iext/catch -pthread
|
||||
|
||||
PROGRAMS = tdns
|
||||
PROGRAMS = tdns testrunner
|
||||
|
||||
all: $(PROGRAMS)
|
||||
|
||||
@ -14,3 +14,6 @@ clean:
|
||||
|
||||
tdns: tdns.o record-types.o dns-storage.o dnsmessages.o contents.o ext/simplesocket/comboaddress.o ext/simplesocket/sclasses.o ext/simplesocket/swrappers.o
|
||||
g++ -std=gnu++14 $^ -o $@ -pthread
|
||||
|
||||
testrunner: tests.o record-types.o dns-storage.o dnsmessages.o
|
||||
g++ -std=gnu++14 $^ -o $@
|
225
tdns/README.md
225
tdns/README.md
@ -1,23 +1,26 @@
|
||||
<meta charset="utf-8" emacsmode="-*- markdown -*-">
|
||||
**A warm welcome to DNS**
|
||||
<link rel="stylesheet" href="https://casual-effects.com/markdeep/latest/apidoc.css?">
|
||||
<!--<link rel="stylesheet" href="https://casual-effects.com/markdeep/latest/apidoc.css?">-->
|
||||
Note: this page is part of the
|
||||
'[hello-dns](https://powerdns.org/hello-dns/)' documentation effort.
|
||||
|
||||
# teaching DNS
|
||||
Welcome to tdns, the teaching authoritative server, implementing all of
|
||||
basic DNS in ~~1000~~ 1100 lines of code.
|
||||
[basic DNS](../basic.md.html) in ~~1000~~ 1100 lines of code.
|
||||
|
||||
The goals of tdns are:
|
||||
|
||||
* Protocol correctness
|
||||
* Suitable for educational purposes
|
||||
* Display best practices
|
||||
* Display best practices, both in DNS and security
|
||||
|
||||
Non-goals are:
|
||||
|
||||
* Performance
|
||||
* Implementing more features
|
||||
* Implementing more features (unless very educational)
|
||||
|
||||
# Current status
|
||||
Features are complete:
|
||||
All 'basic DNS' items are implemented.
|
||||
|
||||
* A, AAAA, NS, MX, CNAME, TXT, SOA
|
||||
* UDP & TCP
|
||||
@ -26,16 +29,18 @@ Features are complete:
|
||||
* Delegations
|
||||
* Glue records
|
||||
* Truncation
|
||||
|
||||
As a bonus:
|
||||
* EDNS (buffer size, no options)
|
||||
|
||||
Missing:
|
||||
* Compression (may not fit in the 1200 lines!)
|
||||
* DNS Compression (may not fit in, say, 1200 lines!)
|
||||
|
||||
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
|
||||
* ~~RCode after one CNAME chase~~
|
||||
* ~~On output (to screen) we do not escape DNS names correctly~~
|
||||
* TCP/IP does not follow recommended timeouts
|
||||
|
||||
The code is not yet in a teachable state, and the layout is somewhat
|
||||
@ -78,7 +83,15 @@ of this right.
|
||||
When DNS labels contain spaces or other non-ascii characters, and a label
|
||||
needs to be converted for screen display or entry, escaping rules apply. The
|
||||
only place in a nameserver where these escaping rules should be enabled is
|
||||
in the parsing of DNS Labels.
|
||||
in the parsing or printing of DNS Labels.
|
||||
|
||||
The input to a `DNSLabel` is an unescaped binary string. The escaping
|
||||
example from RFC 4343 thus works like this:
|
||||
|
||||
```
|
||||
DNSLabel dl("Donald E. Eastlake 3rd");
|
||||
cout << dl << endl; // prints: Donald\032E\.\032Eastlake\0323rd
|
||||
```
|
||||
|
||||
## DNSName
|
||||
A sequence of DNS Labels makes a DNS name. We store such a sequence as a
|
||||
@ -96,6 +109,19 @@ the code. Instead, use this:
|
||||
|
||||
```
|
||||
|
||||
Since a `DNSName` consists of `DNSLabel`s, it gets the same escaping. To
|
||||
again emphasise how we interpret the input as binary, ponder:
|
||||
|
||||
```
|
||||
DNSName test({"powerdns", "com."});
|
||||
cout << test << endl; // prints: powerdns.com\..
|
||||
|
||||
const char zero[]="p\x0werdns";
|
||||
DNSName test2({std::string(zero, sizeof(zero)-1), "com"});
|
||||
|
||||
cout << test2 << endl; // prints: p\000werdns.com.
|
||||
```
|
||||
|
||||
## DNSType, RCode, DNSSection
|
||||
This is an enum that contains the names and numerical values of the DNS
|
||||
types. This means for example that `DNSType::A` corresponds to 1 and
|
||||
@ -107,6 +133,7 @@ the printing of `DNSTypes` as symbolic names. Sample:
|
||||
```
|
||||
DNSType a = DNSType::CNAME;
|
||||
cout << a << "\n"; // prints: CNAME
|
||||
|
||||
a = (DNSType) 6;
|
||||
cout << a <<" is "<< (int)a << "\n"; // prints: SOA is 6
|
||||
```
|
||||
@ -297,6 +324,186 @@ put the generator in the right RRSet place.
|
||||
packet: the 16 bit priority, followed by the name.
|
||||
|
||||
# The RFC 1034 algorithm
|
||||
As noted in the [basic DNS](../basic.md.html) and
|
||||
[authoritative](../auth.md.html) pages, the RFC 1034
|
||||
algorithm can be simplified for a pure authoritative server.
|
||||
|
||||
|
||||
## Finding the right zone and node
|
||||
In tdns.cc, processing starts like this:
|
||||
|
||||
```
|
||||
1 DNSName zonename;
|
||||
2 auto fnd = zones.find(qname, zonename);
|
||||
3 ...
|
||||
4 response.dh.aa = 1;
|
||||
5
|
||||
6 auto bestzone = fnd->zone;
|
||||
7 DNSName searchname(qname), lastnode, zonecutname;
|
||||
8 const DNSNode* passedZonecut=0;
|
||||
9 auto node = bestzone->find(searchname, lastnode, &passedZonecut, &zonecutname);
|
||||
```
|
||||
|
||||
In line 1 we declare the DNSName where we will store the name of the
|
||||
matching zone. On line 2 we look up the query name, and get the node
|
||||
containing the zone, plus its name.
|
||||
|
||||
Line 3 elides error response if no zone was found. In line 4 we declare we
|
||||
have authority. Line 6 saves some typing later on.
|
||||
|
||||
Lines 7 and 8 declare what we are looking for, and reserves names for where
|
||||
we store what we found.
|
||||
|
||||
Line 9 finally calls `find` to find the best node within our zone. As noted
|
||||
above, `find` not only finds the best node, but also lets us know if we
|
||||
passed any NS records along the way.
|
||||
|
||||
## If we passed a zone cut
|
||||
|
||||
```
|
||||
1 if(passedZonecut) {
|
||||
2 response.dh.aa = false;
|
||||
3 cout<<"This is a delegation, zonecutname: '"<<zonecutname<<"'"<<endl;
|
||||
4 auto iter = passedZonecut->rrsets.find(DNSType::NS);
|
||||
5 if(iter != passedZonecut->rrsets.end()) {
|
||||
6 const auto& rrset = iter->second;
|
||||
7 vector< DNSName > toresolve;
|
||||
8 for(const auto& rr : rrset.contents) {
|
||||
9 response.putRR(DNSSection::Authority, zonecutname+zonename, DNSType::NS, rrset.ttl, rr);
|
||||
10 toresolve.push_back(dynamic_cast< NSGen* >(rr.get())->d_name);
|
||||
11 }
|
||||
12 addAdditional(bestzone, zonename, toresolve, response);
|
||||
13 }
|
||||
14 }
|
||||
```
|
||||
|
||||
This is the first thing we check: did we pass a zone cut? If so, on line 2
|
||||
we drop the aa bit, since we clearly are not providing an authoritative
|
||||
answer.
|
||||
|
||||
Lines 4 and 5 lookup and verify if there is actually an NS record at the
|
||||
zone cut. This should always be true.
|
||||
|
||||
In line 7 we store room for the NS server names we will need to look up
|
||||
glue for. In line 8 we iterate over the NS records, which we put in the
|
||||
`DNSMessageWriter` on line 9. On line 10 we store glue record names.
|
||||
|
||||
Finally on line 12, we call `addAdditional` which will look up the glue
|
||||
names for us. This completes the response in case of a delegation.
|
||||
|
||||
Note that contrary to RFC 1034, `addAdditional` **only** looks for glue
|
||||
within the `bestzone` itself.
|
||||
|
||||
## NXDOMAIN
|
||||
|
||||
```
|
||||
1 else if(!searchname.empty()) {
|
||||
2 if(!CNAMELoopCount) // RFC 1034, 4.3.2, step 3.c
|
||||
3 response.dh.rcode = (int)RCode::Nxdomain;
|
||||
4 const auto& rrset = bestzone->rrsets[DNSType::SOA];
|
||||
5
|
||||
6 response.putRR(DNSSection::Authority, zonename, DNSType::SOA, rrset.ttl, rrset.contents[0]);
|
||||
7 }
|
||||
```
|
||||
|
||||
If `find` returned with a non-empty `searchname`, it meant there were parts
|
||||
of the query name that could not be matched to a node. We checked for a
|
||||
zonecut earlier (in the previous section), there was none. So this name
|
||||
really does not exist.
|
||||
|
||||
In line 3 we set the response status to NXDOMAIN, unless we've looped
|
||||
through a CNAME already.
|
||||
|
||||
In line 4 we look up the SOA record of our `bestzone` and in line 6 we put
|
||||
it in the message.
|
||||
|
||||
## Node exists
|
||||
At this stage we know a node exists for this name, although it may actually
|
||||
be a wildcard node. We do not actually care if it is. Here is what we have
|
||||
to do first though.
|
||||
|
||||
### Check for a CNAME
|
||||
|
||||
```
|
||||
1 auto iter = node->rrsets.cbegin();
|
||||
2 if(iter = node->rrsets.find(DNSType::CNAME), iter != node->rrsets.end()) {
|
||||
5 const auto& rrset = iter->second;
|
||||
6 response.putRR(DNSSection::Answer, lastnode+zonename, DNSType::CNAME, rrset.ttl, rrset.contents[0]);
|
||||
7 DNSName target=dynamic_cast<CNAMEGen*>(rrset.contents[0].get())->d_name;
|
||||
8 if(target.makeRelative(zonename)) {
|
||||
9 searchname = target;
|
||||
10 if(CNAMELoopCount++ < 10) {
|
||||
11 lastnode.clear();
|
||||
12 zonecutname.clear();
|
||||
13 goto loopCNAME;
|
||||
14 }
|
||||
15 }
|
||||
16 else
|
||||
17 cout<<" CNAME points to record " << target << " in other zone, good luck" << endl;
|
||||
18 }
|
||||
```
|
||||
|
||||
Line 1 defines an iterator for our subsequent lookup in line 2: is there a
|
||||
CNAME at this node? If so, in line 6 we put it in the DNSMessage. In line 7
|
||||
we extract the target of the CNAME.
|
||||
|
||||
In line 8 we again violate the RFC 1034 algorithm by checking if the CNAME
|
||||
points to somewhere within our own zone. If it points to another zone, we
|
||||
are not going to chase this CNAME.
|
||||
|
||||
On line 9 we redirect ourselves if within the same zone. We also check if we
|
||||
haven't looped 'too much' already. It appears everyone has picked the number
|
||||
10 for this. We do some cleanup on lines 11 and 12 and finally on line 13 we
|
||||
restart our algorithm. With a goto.
|
||||
|
||||
### Name exists, no CNAME, matching types
|
||||
```
|
||||
1 if(iter = node->rrsets.find(qtype), iter != node->rrsets.end() || (!node->rrsets.empty() && qtype==DNSType::ANY)) {
|
||||
2 auto range = make_pair(iter, iter);
|
||||
3 if(qtype == DNSType::ANY)
|
||||
4 range = make_pair(node->rrsets.begin(), node->rrsets.end());
|
||||
5 else
|
||||
6 ++range.second;
|
||||
7 for(auto i2 = range.first; i2 != range.second; ++i2) {
|
||||
8 const auto& rrset = i2->second;
|
||||
9 for(const auto& rr : rrset.contents) {
|
||||
10 response.putRR(DNSSection::Answer, lastnode+zonename, i2->first, rrset.ttl, rr);
|
||||
11 if(i2->first == DNSType::MX)
|
||||
12 additional.push_back(dynamic_cast< MXGen* >(rr.get())->d_name);
|
||||
13 }
|
||||
14 }
|
||||
15 }
|
||||
```
|
||||
|
||||
On line 1 is a somewhat tricky lookup that tries to find the query type in
|
||||
the RRSET, and if it could not be found, if the query maybe was for ANY and
|
||||
there are records that could be matched.
|
||||
|
||||
On lines 2 to 6 we either pick the matching RRSet to put in the DNSMessage,
|
||||
or we set it up so we iterate over all types, which we then do on lines 8 to
|
||||
14.
|
||||
|
||||
Note that again we gather up the server name of the MX record for additional
|
||||
processing. If we supported SRV records, we would do the same for them.
|
||||
|
||||
### The name exists, but no types or no types match
|
||||
Finally one of the most vexing parts of DNS: a name that exists, but there
|
||||
are no types or at least no matching types. This could be an 'empty
|
||||
non-terminal', created out of thin air by 'some.long.name.powerdns.org'.
|
||||
This DNS Name populates nodes all along its length, even if no RRSets are
|
||||
attached to 'long.name.powerdns.org' for example.
|
||||
|
||||
In many servers this is tricky, but since we followed a DNS tree based
|
||||
design with nodes, our code is trivial:
|
||||
|
||||
```
|
||||
1 else {
|
||||
2 const auto& rrset = bestzone->rrsets[DNSType::SOA];
|
||||
3 response.putRR(DNSSection::Answer, zonename, DNSType::SOA, rrset.ttl, rrset.contents[0]);
|
||||
4 }
|
||||
```
|
||||
|
||||
All we have to do is 'else' off the previous case, and add the SOA record.
|
||||
|
||||
# DNSMessageWriter
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "dns-storage.hh"
|
||||
#include <iomanip>
|
||||
using namespace std;
|
||||
|
||||
bool DNSName::makeRelative(const DNSName& root)
|
||||
@ -20,7 +21,7 @@ bool DNSName::makeRelative(const DNSName& root)
|
||||
|
||||
const DNSNode* DNSNode::find(DNSName& name, DNSName& last, const DNSNode** passedZonecut, DNSName* zonecutname) const
|
||||
{
|
||||
cout<<"find for '"<<name<<"', last is now '"<<last<<"'"<<endl;
|
||||
cout<<"find called for '"<<name<<"', last is now '"<<last<<"'"<<endl;
|
||||
if(!last.empty() && rrsets.count(DNSType::NS)) {
|
||||
cout<<" passed a zonecut, making note of this"<<endl;
|
||||
if(passedZonecut)
|
||||
@ -30,11 +31,18 @@ const DNSNode* DNSNode::find(DNSName& name, DNSName& last, const DNSNode** passe
|
||||
}
|
||||
|
||||
if(name.empty()) {
|
||||
cout<<"Empty lookup, returning this node or 0"<<endl;
|
||||
if(!zone && rrsets.empty()) // only root zone can have this
|
||||
cout<<"Empty lookup name. ";
|
||||
if(!zone && rrsets.empty()) { // only root zone can have this
|
||||
cout<<"Returning zero"<<endl;
|
||||
return 0;
|
||||
else
|
||||
}
|
||||
else {
|
||||
cout<<"Returning node with following types: ";
|
||||
for(const auto& c : rrsets)
|
||||
cout<<c.first<<" ";
|
||||
cout<<endl;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
cout<<"Children at this node: ";
|
||||
for(const auto& c: children) cout <<"'"<<c.first<<"' ";
|
||||
@ -45,7 +53,7 @@ const DNSNode* DNSNode::find(DNSName& name, DNSName& last, const DNSNode** passe
|
||||
cout<<"Found nothing, trying wildcard"<<endl;
|
||||
iter = children.find("*");
|
||||
if(iter == children.end()) {
|
||||
cout<<"Still nothing, returning leaf"<<endl;
|
||||
cout<<"Still nothing, returning this node"<<endl;
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
@ -56,7 +64,7 @@ const DNSNode* DNSNode::find(DNSName& name, DNSName& last, const DNSNode** passe
|
||||
}
|
||||
}
|
||||
}
|
||||
cout<<" Had match, continuing to child '"<<iter->first<<"'"<<endl;
|
||||
cout<<" Had match at this node , continuing to child '"<<iter->first<<"'"<<endl;
|
||||
last.push_front(name.back());
|
||||
name.pop_back();
|
||||
return iter->second.find(name, last, passedZonecut, zonecutname);
|
||||
@ -100,10 +108,18 @@ void DNSNode::visit(std::function<void(const DNSName& name, const DNSNode*)> vis
|
||||
c.second.visit(visitor, DNSName{c.first}+name);
|
||||
}
|
||||
|
||||
// this should perform escaping rules!
|
||||
std::ostream & operator<<(std::ostream &os, const DNSLabel& d)
|
||||
{
|
||||
os<<d.d_s;
|
||||
for(uint8_t a : d.d_s) {
|
||||
if(a <= 0x20 || a >= 0x7f) { // RFC 4343
|
||||
os<<'\\'<<setfill('0')<<setw(3)<<(int)a;
|
||||
setfill(' '); // setw resets itself
|
||||
}
|
||||
else if((char)a =='.' || (char)a=='\\')
|
||||
os<<"\\"<<(char)a;
|
||||
else
|
||||
os<<(char)a;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,6 @@ struct DNSNode
|
||||
DNSNode* add(DNSName name);
|
||||
std::map<DNSLabel, DNSNode> children;
|
||||
std::map<DNSType, RRSet > rrsets;
|
||||
|
||||
|
||||
void addRRs(std::unique_ptr<RRGen>&&a);
|
||||
|
||||
|
13050
tdns/ext/catch/catch.hpp
Normal file
13050
tdns/ext/catch/catch.hpp
Normal file
File diff suppressed because it is too large
Load Diff
192
tdns/tdns.cc
192
tdns/tdns.cc
@ -44,14 +44,17 @@ void addAdditional(const DNSNode* bestzone, const DNSName& zone, const vector<DN
|
||||
|
||||
bool processQuestion(const DNSNode& zones, DNSMessageReader& dm, const ComboAddress& local, const ComboAddress& remote, DNSMessageWriter& response)
|
||||
{
|
||||
DNSName name;
|
||||
DNSType type;
|
||||
dm.getQuestion(name, type);
|
||||
DNSName origname=name; // we need this for error reporting, we munch the original name
|
||||
DNSName qname;
|
||||
DNSType qtype;
|
||||
dm.getQuestion(qname, qtype);
|
||||
DNSName origname=qname; // we need this for error reporting, we munch the original name
|
||||
bool haveEDNS=false;
|
||||
cout<<"Received a query from "<<remote.toStringWithPort()<<" for "<<name<<" and type "<<type<<endl;
|
||||
|
||||
cout<<"Received a query from "<<remote.toStringWithPort()<<" for "<<qname<<" and type "<<qtype<<endl;
|
||||
|
||||
uint16_t newsize=0;
|
||||
bool doBit=false;
|
||||
|
||||
haveEDNS = dm.getEDNS(&newsize, &doBit);
|
||||
if(haveEDNS && newsize > sizeof(dnsheader))
|
||||
response.payload.resize(newsize - sizeof(dnsheader));
|
||||
@ -60,118 +63,117 @@ bool processQuestion(const DNSNode& zones, DNSMessageReader& dm, const ComboAddr
|
||||
response.dh = dm.dh;
|
||||
response.dh.ad = response.dh.ra = response.dh.aa = 0;
|
||||
response.dh.qr = 1;
|
||||
response.setQuestion(name, type);
|
||||
response.setQuestion(qname, qtype);
|
||||
|
||||
if(type == DNSType::AXFR || type == DNSType::IXFR) {
|
||||
if(qtype == DNSType::AXFR || qtype == DNSType::IXFR) {
|
||||
cout<<"Query was for AXFR or IXFR over UDP, can't do that"<<endl;
|
||||
response.dh.rcode = (int)RCode::Servfail;
|
||||
if(haveEDNS) {
|
||||
response.putEDNS(newsize, doBit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(dm.dh.opcode != 0) {
|
||||
cout<<"Query had non-zero opcode "<<dm.dh.opcode<<", sending NOTIMP"<<endl;
|
||||
response.dh.rcode = (int)RCode::Notimp;
|
||||
if(haveEDNS) {
|
||||
response.putEDNS(newsize, doBit);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DNSName zone;
|
||||
auto fnd = zones.find(name, zone);
|
||||
if(fnd && fnd->zone) {
|
||||
cout<<"---\nBest zone: "<<zone<<", name now "<<name<<", loaded: "<<(void*)fnd->zone<<endl;
|
||||
|
||||
response.dh.aa = 1;
|
||||
|
||||
auto bestzone = fnd->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 '"<<zone<<"' for lhs '"<<name<<"'"<<endl;
|
||||
DNSName zonename;
|
||||
auto fnd = zones.find(qname, zonename);
|
||||
if(!fnd && !fnd->zone) {
|
||||
cout<<"No zone matched"<<endl;
|
||||
response.dh.rcode = (uint8_t)RCode::Refused;
|
||||
if(haveEDNS) {
|
||||
response.putEDNS(newsize, doBit);
|
||||
}
|
||||
else if(passedZonecut) {
|
||||
response.dh.aa = false;
|
||||
cout<<"This is a delegation, zonecutname: '"<<zonecutname<<"'"<<endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
cout<<"---\nFound best zone: "<<zonename<<", qname now "<<qname<<endl;
|
||||
response.dh.aa = 1;
|
||||
|
||||
auto bestzone = fnd->zone;
|
||||
DNSName searchname(qname), lastnode, zonecutname;
|
||||
const DNSNode* passedZonecut=0;
|
||||
int CNAMELoopCount = 0;
|
||||
|
||||
loopCNAME:;
|
||||
auto node = bestzone->find(searchname, lastnode, &passedZonecut, &zonecutname);
|
||||
if(passedZonecut) {
|
||||
response.dh.aa = false;
|
||||
cout<<"This is a delegation, zonecutname: '"<<zonecutname<<"'"<<endl;
|
||||
|
||||
for(const auto& rr: passedZonecut->rrsets) {
|
||||
cout<<" Have type "<<rr.first<<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, zonecutname+zone, DNSType::NS, rrset.ttl, rr);
|
||||
toresolve.push_back(dynamic_cast<NSGen*>(rr.get())->d_name);
|
||||
}
|
||||
addAdditional(bestzone, zone, toresolve, response);
|
||||
}
|
||||
for(const auto& rr: passedZonecut->rrsets) {
|
||||
cout<<" Have type "<<rr.first<<endl;
|
||||
}
|
||||
else if(!searchname.empty()) {
|
||||
cout<<"This is an NXDOMAIN situation"<<endl;
|
||||
const auto& rrset = fnd->zone->rrsets[DNSType::SOA];
|
||||
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, zonecutname+zonename, DNSType::NS, rrset.ttl, rr);
|
||||
toresolve.push_back(dynamic_cast<NSGen*>(rr.get())->d_name);
|
||||
}
|
||||
addAdditional(bestzone, zonename, toresolve, response);
|
||||
}
|
||||
}
|
||||
else if(!searchname.empty()) {
|
||||
cout<<"This is an NXDOMAIN situation"<<endl;
|
||||
if(!CNAMELoopCount) // RFC 1034, 4.3.2, step 3.c
|
||||
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;
|
||||
const auto& rrset = bestzone->rrsets[DNSType::SOA];
|
||||
|
||||
auto iter = node->rrsets.cbegin();
|
||||
vector<DNSName> additional;
|
||||
if(iter = node->rrsets.find(DNSType::CNAME), iter != node->rrsets.end()) {
|
||||
cout<<"We have a CNAME!"<<endl;
|
||||
const auto& rrset = iter->second;
|
||||
DNSName target;
|
||||
for(const auto& rr : rrset.contents) {
|
||||
response.putRR(DNSSection::Answer, lastnode+zone, DNSType::CNAME, rrset.ttl, rr);
|
||||
target=dynamic_cast<CNAMEGen*>(rr.get())->d_name;
|
||||
}
|
||||
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) {
|
||||
lastnode.clear();
|
||||
zonecutname.clear();
|
||||
goto loopCNAME;
|
||||
}
|
||||
}
|
||||
else
|
||||
cout<<" CNAME points to record "<<target<<" in other zone, good luck"<<endl;
|
||||
}
|
||||
else 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);
|
||||
if(t.first == DNSType::MX)
|
||||
additional.push_back(dynamic_cast<MXGen*>(rr.get())->d_name);
|
||||
response.putRR(DNSSection::Authority, zonename, DNSType::SOA, rrset.ttl, rrset.contents[0]);
|
||||
}
|
||||
else {
|
||||
cout<<"Found something in zone '"<<zonename<<"' for lhs '"<<qname<<"', searchname now '"<<searchname<<"', lastnode '"<<lastnode<<"', passedZonecut="<<passedZonecut<<endl;
|
||||
|
||||
auto iter = node->rrsets.cbegin();
|
||||
vector<DNSName> additional;
|
||||
if(iter = node->rrsets.find(DNSType::CNAME), iter != node->rrsets.end()) {
|
||||
cout<<"We have a CNAME!"<<endl;
|
||||
const auto& rrset = iter->second;
|
||||
response.putRR(DNSSection::Answer, lastnode+zonename, DNSType::CNAME, rrset.ttl, rrset.contents[0]);
|
||||
DNSName target=dynamic_cast<CNAMEGen*>(rrset.contents[0].get())->d_name;
|
||||
|
||||
}
|
||||
if(target.makeRelative(zonename)) {
|
||||
cout<<" Should follow CNAME to "<<target<<" within our zone"<<endl;
|
||||
searchname = target;
|
||||
if(CNAMELoopCount++ < 10) {
|
||||
lastnode.clear();
|
||||
zonecutname.clear();
|
||||
goto loopCNAME;
|
||||
}
|
||||
}
|
||||
else if(iter = node->rrsets.find(type), iter != node->rrsets.end() || type==DNSType::ANY) {
|
||||
const auto& rrset = iter->second;
|
||||
else
|
||||
cout<<" CNAME points to record "<<target<<" in other zone, good luck"<<endl;
|
||||
}
|
||||
else if(iter = node->rrsets.find(qtype), iter != node->rrsets.end() || (!node->rrsets.empty() && qtype==DNSType::ANY)) {
|
||||
auto range = make_pair(iter, iter);
|
||||
if(qtype == DNSType::ANY)
|
||||
range = make_pair(node->rrsets.begin(), node->rrsets.end());
|
||||
else
|
||||
++range.second;
|
||||
for(auto i2 = range.first; i2 != range.second; ++i2) {
|
||||
const auto& rrset = i2->second;
|
||||
for(const auto& rr : rrset.contents) {
|
||||
response.putRR(DNSSection::Answer, lastnode+zone, type, rrset.ttl, rr);
|
||||
if(type == DNSType::MX)
|
||||
response.putRR(DNSSection::Answer, lastnode+zonename, i2->first, rrset.ttl, rr);
|
||||
if(i2->first == DNSType::MX)
|
||||
additional.push_back(dynamic_cast<MXGen*>(rr.get())->d_name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
cout<<"Node exists, qtype doesn't, NOERROR situation, inserting SOA"<<endl;
|
||||
const auto& rrset = fnd->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"<<endl;
|
||||
response.dh.rcode = (uint8_t)RCode::Refused;
|
||||
else {
|
||||
cout<<"Node exists, qtype doesn't, NOERROR situation, inserting SOA"<<endl;
|
||||
const auto& rrset = bestzone->rrsets[DNSType::SOA];
|
||||
response.putRR(DNSSection::Answer, zonename, DNSType::SOA, rrset.ttl, rrset.contents[0]);
|
||||
}
|
||||
addAdditional(bestzone, zonename, additional, response);
|
||||
}
|
||||
if(haveEDNS) {
|
||||
response.putEDNS(newsize, doBit);
|
||||
@ -179,13 +181,13 @@ bool processQuestion(const DNSNode& zones, DNSMessageReader& dm, const ComboAddr
|
||||
return true;
|
||||
}
|
||||
catch(std::out_of_range& e) { // exceeded packet size
|
||||
cout<<"Query for '"<<origname<<"'|"<<type<<" got truncated"<<endl;
|
||||
response.setQuestion(origname, type); // this resets the packet
|
||||
cout<<"Query for '"<<origname<<"'|"<<qtype<<" got truncated"<<endl;
|
||||
response.setQuestion(origname, qtype); // this resets the packet
|
||||
response.dh.tc=1; response.dh.aa=0;
|
||||
if(haveEDNS) {
|
||||
response.putEDNS(newsize, doBit);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
catch(std::exception& e) {
|
||||
|
Loading…
Reference in New Issue
Block a user