dnsdist can now retrieve zones over AXFR, including the root. This shook out some compression bugs, plus an off by one on serving the root zone. With this commit, tdns can also parse DNS Messages (which it needs for AXFR).
This also introduces some 'reflection' support that will eventually unify message generation/parting and zone file format input/output
This commit is contained in:
parent
632fc9d2cd
commit
d03d16cbdf
@ -4,7 +4,7 @@
|
||||
void loadZones(DNSNode& zones)
|
||||
{
|
||||
auto zone = zones.add({"tdns", "powerdns", "org"});
|
||||
auto newzone = zone->zone = new DNSNode(); // XXX ICK
|
||||
auto newzone = std::make_unique<DNSNode>();
|
||||
|
||||
newzone->addRRs(SOAGen::make({"ns1", "tdns", "powerdns", "org"}, {"admin", "powerdns", "org"}, 1),
|
||||
NSGen::make({"ns1", "tdns", "powerdns", "org"}),
|
||||
@ -53,4 +53,6 @@ void loadZones(DNSNode& zones)
|
||||
|
||||
newzone->add({"some host"})->addRRs(AGen::make("192.0.0.2"));
|
||||
newzone->add({"some.host"})->addRRs(AGen::make("192.0.0.3"));
|
||||
|
||||
zone->zone = std::move(newzone);
|
||||
}
|
||||
|
@ -27,11 +27,13 @@ DNSName operator+(const DNSName& a, const DNSName& b)
|
||||
return ret;
|
||||
}
|
||||
|
||||
DNSNode::~DNSNode() = default;
|
||||
|
||||
const DNSNode* DNSNode::find(DNSName& name, DNSName& last, bool wildcard, const DNSNode** passedZonecut, DNSName* zonecutname) const
|
||||
{
|
||||
cout<<"find called 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;
|
||||
// cout<<" passed a zonecut, making note of this"<<endl;
|
||||
if(passedZonecut)
|
||||
*passedZonecut=this;
|
||||
if(zonecutname)
|
||||
@ -39,35 +41,36 @@ const DNSNode* DNSNode::find(DNSName& name, DNSName& last, bool wildcard, const
|
||||
}
|
||||
|
||||
if(name.empty()) {
|
||||
cout<<"Empty lookup name. Returning node with following types: ";
|
||||
for(const auto& c : rrsets)
|
||||
cout<<c.first<<" ";
|
||||
cout<<endl;
|
||||
// cout<<"Empty lookup name. Returning node with following types: ";
|
||||
// for(const auto& c : rrsets)
|
||||
// cout<<c.first<<" ";
|
||||
// cout<<endl;
|
||||
return this;
|
||||
}
|
||||
cout<<"Children at this node: ";
|
||||
/* cout<<"Children at this node: ";
|
||||
for(const auto& c: children) cout <<"'"<<c.first<<"' ";
|
||||
cout<<endl;
|
||||
*/
|
||||
auto iter = children.find(name.back());
|
||||
cout<<"Looked for child called '"<<name.back()<<"'"<<endl;
|
||||
// cout<<"Looked for child called '"<<name.back()<<"'"<<endl;
|
||||
if(iter == children.end()) {
|
||||
if(!wildcard)
|
||||
return this;
|
||||
cout<<"Found nothing, trying wildcard"<<endl;
|
||||
// cout<<"Found nothing, trying wildcard"<<endl;
|
||||
iter = children.find("*");
|
||||
if(iter == children.end()) {
|
||||
cout<<"Still nothing, returning this node"<<endl;
|
||||
// cout<<"Still nothing, returning this node"<<endl;
|
||||
return this;
|
||||
}
|
||||
else {
|
||||
cout<<" Had wildcard match, picking that, matching all labels"<<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 at this node , 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, wildcard, passedZonecut, zonecutname);
|
||||
@ -75,9 +78,7 @@ const DNSNode* DNSNode::find(DNSName& name, DNSName& last, bool wildcard, const
|
||||
|
||||
DNSNode* DNSNode::add(DNSName name)
|
||||
{
|
||||
if(name.size() == 1) { // this is our home node
|
||||
return &children[name.front()];
|
||||
}
|
||||
if(name.empty()) return this;
|
||||
auto back = name.back();
|
||||
name.pop_back();
|
||||
return children[back].add(name); // will make child node if needed
|
||||
|
@ -53,12 +53,13 @@ SMARTENUMEND(RCode)
|
||||
|
||||
enum class DNSType : uint16_t
|
||||
{
|
||||
A = 1, NS = 2, CNAME = 5, SOA=6, PTR=12, MX=15, TXT=16, AAAA = 28, SRV=33, OPT=41, IXFR = 251, AXFR = 252, ANY = 255
|
||||
A = 1, NS = 2, CNAME = 5, SOA=6, PTR=12, MX=15, TXT=16, AAAA = 28, SRV=33, DS=43, RRSIG=46,
|
||||
NSEC=47, OPT=41, IXFR = 251, AXFR = 252, ANY = 255
|
||||
};
|
||||
|
||||
SMARTENUMSTART(DNSType)
|
||||
SENUM13(DNSType, A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, IXFR, AAAA, SRV, OPT, IXFR)
|
||||
SENUM2(DNSType, AXFR, ANY)
|
||||
SENUM13(DNSType, A, NS, CNAME, SOA, PTR, MX, TXT, AAAA, SRV,DS, RRSIG, NSEC, OPT)
|
||||
SENUM3(DNSType, IXFR, AXFR, ANY)
|
||||
SMARTENUMEND(DNSType)
|
||||
|
||||
enum class DNSClass : uint16_t
|
||||
@ -114,6 +115,7 @@ struct RRSet
|
||||
|
||||
struct DNSNode
|
||||
{
|
||||
~DNSNode();
|
||||
const DNSNode* find(DNSName& name, DNSName& last, bool wildcards=false, const DNSNode** passedZonecut=0, DNSName* zonecutname=0) const;
|
||||
DNSNode* add(DNSName name);
|
||||
std::map<DNSLabel, DNSNode> children;
|
||||
@ -129,7 +131,7 @@ struct DNSNode
|
||||
void visit(std::function<void(const DNSName& name, const DNSNode*)> visitor, DNSName name) const;
|
||||
|
||||
std::map<DNSType, RRSet > rrsets;
|
||||
DNSNode* zone{0}; // if this is set, this node is a zone
|
||||
std::unique_ptr<DNSNode> zone; // if this is set, this node is a zone
|
||||
uint16_t namepos{0};
|
||||
};
|
||||
|
||||
|
@ -11,15 +11,17 @@ DNSMessageReader::DNSMessageReader(const char* in, uint16_t size)
|
||||
payload.reserve(size-12);
|
||||
payload.insert(payload.begin(), (const unsigned char*)in + 12, (const unsigned char*)in + size);
|
||||
|
||||
d_qname = getName();
|
||||
d_qtype = (DNSType) getUInt16();
|
||||
d_qclass = (DNSClass) getUInt16();
|
||||
if(dh.qdcount) { // AXFR can skip this
|
||||
xfrName(d_qname);
|
||||
d_qtype = (DNSType) getUInt16();
|
||||
d_qclass = (DNSClass) getUInt16();
|
||||
}
|
||||
if(dh.arcount) {
|
||||
if(getUInt8() == 0 && getUInt16() == (uint16_t)DNSType::OPT) {
|
||||
d_bufsize=getUInt16();
|
||||
xfrUInt16(d_bufsize);
|
||||
getUInt8(); // extended RCODE
|
||||
d_ednsVersion = getUInt8();
|
||||
auto flags = getUInt8();
|
||||
d_ednsVersion = getUInt8();
|
||||
auto flags=getUInt8();
|
||||
d_doBit = flags & 0x80;
|
||||
getUInt8(); getUInt16(); // ignore rest
|
||||
cout<<" There was an EDNS section, size supported: "<< d_bufsize<<endl;
|
||||
@ -28,19 +30,29 @@ DNSMessageReader::DNSMessageReader(const char* in, uint16_t size)
|
||||
}
|
||||
}
|
||||
|
||||
DNSName DNSMessageReader::getName()
|
||||
void DNSMessageReader::xfrName(DNSName& res, uint16_t* pos)
|
||||
{
|
||||
DNSName name;
|
||||
if(!pos) pos = &payloadpos;
|
||||
res.clear();
|
||||
for(;;) {
|
||||
uint8_t labellen=getUInt8();
|
||||
if(labellen > 63)
|
||||
throw std::runtime_error("Got a compressed label");
|
||||
uint8_t labellen= getUInt8(pos);
|
||||
if(labellen & 0xc0) {
|
||||
uint16_t labellen2 = getUInt8(pos);
|
||||
uint16_t newpos = ((labellen & ~0xc0) << 8) | labellen2;
|
||||
newpos -= sizeof(dnsheader); // includes struct dnsheader
|
||||
if(newpos < *pos) {
|
||||
res=res+getName(&newpos);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("forward compression: " + std::to_string(newpos) + " >= " + std::to_string(*pos));
|
||||
}
|
||||
}
|
||||
if(!labellen) // end of DNSName
|
||||
break;
|
||||
DNSLabel label = getBlob(labellen);
|
||||
name.push_back(label);
|
||||
DNSLabel label = getBlob(labellen, pos);
|
||||
res.push_back(label);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void DNSMessageReader::getQuestion(DNSName& name, DNSType& type) const
|
||||
@ -57,36 +69,67 @@ bool DNSMessageReader::getEDNS(uint16_t* bufsize, bool* doBit) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DNSMessageReader::getRR(DNSSection& section, DNSName& name, DNSType& type, uint32_t& ttl, std::unique_ptr<RRGen>& content)
|
||||
{
|
||||
if(payloadpos == payload.size())
|
||||
return false;
|
||||
name = getName();
|
||||
type=(DNSType)getUInt16();
|
||||
/* uint16_t lclass = */ getUInt16(); // class
|
||||
xfrUInt32(ttl);
|
||||
auto len = getUInt16();
|
||||
if(type == DNSType::NS) {
|
||||
content = std::make_unique<NSGen>(*this);
|
||||
}
|
||||
else if(type == DNSType::SOA) {
|
||||
content = std::make_unique<SOAGen>(*this);
|
||||
}
|
||||
else if(type == DNSType::MX) {
|
||||
content = std::make_unique<MXGen>(*this);
|
||||
}
|
||||
else if(type == DNSType::CNAME) {
|
||||
content = std::make_unique<CNAMEGen>(*this);
|
||||
}
|
||||
else if(type == DNSType::PTR) {
|
||||
content = std::make_unique<PTRGen>(*this);
|
||||
}
|
||||
else {
|
||||
content = UnknownGen::make(type, getBlob(len));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// this is required to make the std::unique_ptr to DNSZone work. Long story.
|
||||
DNSMessageWriter::~DNSMessageWriter() = default;
|
||||
|
||||
void DNSMessageWriter::putName(const DNSName& name, bool compress)
|
||||
void DNSMessageWriter::xfrName(const DNSName& name, bool compress)
|
||||
{
|
||||
DNSName oname(name);
|
||||
cout<<"Attempt to emit "<<oname<<" (compress = "<<compress<<", d_nocompress= "<<d_nocompress<<")"<<endl;
|
||||
// cout<<"Attempt to emit "<<oname<<" (compress = "<<compress<<", d_nocompress= "<<d_nocompress<<")"<<endl;
|
||||
DNSName fname(oname), flast;
|
||||
|
||||
if(compress && !d_nocompress) {
|
||||
auto node = d_comptree->find(fname, flast);
|
||||
|
||||
if(node) {
|
||||
cout<<" Did lookup for "<<oname<<", left: "<<fname<<", node: "<<flast<<", pos: "<<node->namepos<<endl;
|
||||
// cout<<" Did lookup for "<<oname<<", left: "<<fname<<", node: "<<flast<<", pos: "<<node->namepos<<endl;
|
||||
if(flast.size() > 1) {
|
||||
uint16_t pos = node->namepos;
|
||||
cout<<" Using the pointer we found to pos "<<pos<<", have to emit "<<fname.size()<<" labels first"<<endl;
|
||||
auto opayloadpos = payloadpos;
|
||||
// cout<<" Using the pointer we found to pos "<<pos<<", have to emit "<<fname.size()<<" labels first"<<endl;
|
||||
|
||||
DNSName sname(oname);
|
||||
for(const auto& lab : fname) {
|
||||
putUInt8(lab.size());
|
||||
putBlob(lab.d_s);
|
||||
}
|
||||
putUInt8((pos>>8) | 0xc0 );
|
||||
putUInt8(pos & 0xff);
|
||||
if(!fname.empty()) { // worth it to save the full name for future reference
|
||||
auto anode = d_comptree->add(oname);
|
||||
auto anode = d_comptree->add(sname);
|
||||
if(!anode->namepos) {
|
||||
cout<<"Storing that "<<oname<<" can be found at "<<opayloadpos + 12 <<endl;
|
||||
anode->namepos = opayloadpos + 12;
|
||||
// cout<<"Storing that "<<sname<<" can be found at " << payloadpos + 12 << endl;
|
||||
anode->namepos = payloadpos + 12;
|
||||
}
|
||||
sname.pop_front();
|
||||
xfrUInt8(lab.size());
|
||||
xfrBlob(lab.d_s);
|
||||
}
|
||||
xfrUInt8((pos>>8) | (uint8_t)0xc0 );
|
||||
xfrUInt8(pos & 0xff);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -96,15 +139,15 @@ void DNSMessageWriter::putName(const DNSName& name, bool compress)
|
||||
if(!d_nocompress) { // even with compress=false, we want to store this name, unless this is a nocompress message (AXFR)
|
||||
auto anode = d_comptree->add(oname);
|
||||
if(!anode->namepos) {
|
||||
// cout<<"Storing that "<<oname<<" can be found at "<<payloadpos + 12 <<endl;
|
||||
// cout<<"Storing that "<<oname<<" can be found at "<<payloadpos + 12 <<endl;
|
||||
anode->namepos = payloadpos + 12;
|
||||
}
|
||||
}
|
||||
oname.pop_front();
|
||||
putUInt8(l.size());
|
||||
putBlob(l.d_s);
|
||||
xfrUInt8(l.size());
|
||||
xfrBlob(l.d_s);
|
||||
}
|
||||
putUInt8(0);
|
||||
xfrUInt8(0);
|
||||
}
|
||||
|
||||
static void nboInc(uint16_t& counter) // network byte order inc
|
||||
@ -116,12 +159,12 @@ void DNSMessageWriter::putRR(DNSSection section, const DNSName& name, DNSType ty
|
||||
{
|
||||
auto cursize = payloadpos;
|
||||
try {
|
||||
putName(name);
|
||||
putUInt16((int)type); putUInt16(1);
|
||||
putUInt32(ttl);
|
||||
auto pos = putUInt16(0); // placeholder
|
||||
xfrName(name);
|
||||
xfrUInt16((int)type); xfrUInt16(1);
|
||||
xfrUInt32(ttl);
|
||||
auto pos = xfrUInt16(0); // placeholder
|
||||
content->toMessage(*this);
|
||||
putUInt16At(pos, payloadpos-pos-2);
|
||||
xfrUInt16At(pos, payloadpos-pos-2);
|
||||
}
|
||||
catch(...) {
|
||||
payloadpos = cursize;
|
||||
@ -148,9 +191,9 @@ void DNSMessageWriter::putEDNS(uint16_t bufsize, RCode ercode, bool doBit)
|
||||
{
|
||||
auto cursize = payloadpos;
|
||||
try {
|
||||
putUInt8(0); putUInt16((uint16_t)DNSType::OPT); // 'root' name, our type
|
||||
putUInt16(bufsize); putUInt8(((int)ercode)>>4); putUInt8(0); putUInt8(doBit ? 0x80 : 0); putUInt8(0);
|
||||
putUInt16(0);
|
||||
xfrUInt8(0); xfrUInt16((uint16_t)DNSType::OPT); // 'root' name, our type
|
||||
xfrUInt16(bufsize); xfrUInt8(((int)ercode)>>4); xfrUInt8(0); xfrUInt8(doBit ? 0x80 : 0); xfrUInt8(0);
|
||||
xfrUInt16(0);
|
||||
}
|
||||
catch(...) { // went beyond message size, roll it all back
|
||||
payloadpos = cursize;
|
||||
@ -162,7 +205,7 @@ void DNSMessageWriter::putEDNS(uint16_t bufsize, RCode ercode, bool doBit)
|
||||
DNSMessageWriter::DNSMessageWriter(const DNSName& name, DNSType type, int maxsize) : d_qname(name), d_qtype(type)
|
||||
{
|
||||
memset(&dh, 0, sizeof(dh));
|
||||
payload.resize(maxsize);
|
||||
payload.resize(maxsize - sizeof(dh));
|
||||
clearRRs();
|
||||
}
|
||||
|
||||
@ -171,9 +214,9 @@ void DNSMessageWriter::clearRRs()
|
||||
d_comptree = std::make_unique<DNSNode>();
|
||||
dh.qdcount = htons(1) ; dh.ancount = dh.arcount = dh.nscount = 0;
|
||||
payloadpos=0;
|
||||
putName(d_qname, false);
|
||||
putUInt16((uint16_t)d_qtype);
|
||||
putUInt16(1); // class
|
||||
xfrName(d_qname, false);
|
||||
xfrUInt16((uint16_t)d_qtype);
|
||||
xfrUInt16(1); // class
|
||||
}
|
||||
|
||||
string DNSMessageWriter::serialize()
|
||||
@ -184,11 +227,12 @@ string DNSMessageWriter::serialize()
|
||||
putEDNS(payload.size() + sizeof(dnsheader), d_ercode, d_doBit);
|
||||
}
|
||||
std::string ret((const char*)&dh, ((const char*)&dh) + sizeof(dnsheader));
|
||||
ret.append((const unsigned char*)&payload.at(0), (const unsigned char*)&payload.at(payloadpos));
|
||||
if(payloadpos)
|
||||
ret.append((const unsigned char*)&payload.at(0), (const unsigned char*)&payload.at(payloadpos-1)+1);
|
||||
return ret;
|
||||
}
|
||||
catch(std::out_of_range& e) {
|
||||
cout<<"Got truncated while adding EDNS! Truncating"<<endl;
|
||||
cout<<"Got truncated while adding EDNS! Truncating. haveEDNS="<<haveEDNS<<", payloadpos="<<payloadpos<<endl;
|
||||
DNSMessageWriter act(d_qname, d_qtype);
|
||||
act.dh = dh;
|
||||
act.putEDNS(payload.size() + sizeof(dnsheader), d_ercode, d_doBit);
|
||||
|
@ -16,32 +16,58 @@ public:
|
||||
|
||||
void getQuestion(DNSName& name, DNSType& type) const;
|
||||
bool getEDNS(uint16_t* newsize, bool* doBit) const;
|
||||
|
||||
bool getRR(DNSSection& section, DNSName& name, DNSType& type, uint32_t& ttl, std::unique_ptr<RRGen>& content);
|
||||
|
||||
uint8_t d_ednsVersion{0};
|
||||
private:
|
||||
DNSName getName();
|
||||
uint8_t getUInt8()
|
||||
|
||||
void xfrName(DNSName& ret, uint16_t* pos=0);
|
||||
DNSName getName(uint16_t* pos=0) { DNSName res; xfrName(res, pos); return res;}
|
||||
void xfrUInt8(uint8_t&res, uint16_t* pos = 0)
|
||||
{
|
||||
return payload.at(payloadpos++);
|
||||
if(!pos) pos = &payloadpos;
|
||||
res=payload.at((*pos)++);
|
||||
}
|
||||
|
||||
uint16_t getUInt16()
|
||||
uint8_t getUInt8(uint16_t* pos=0)
|
||||
{ uint8_t ret; xfrUInt8(ret, pos); return ret; }
|
||||
|
||||
void xfrUInt16(uint16_t& res)
|
||||
{
|
||||
uint16_t ret;
|
||||
memcpy(&ret, &payload.at(payloadpos+1)-1, 2);
|
||||
memcpy(&res, &payload.at(payloadpos+1)-1, 2);
|
||||
payloadpos+=2;
|
||||
return htons(ret);
|
||||
res=htons(res);
|
||||
}
|
||||
uint16_t getUInt16()
|
||||
{ uint16_t ret; xfrUInt16(ret); return ret; }
|
||||
|
||||
void xfrUInt32(uint32_t& res)
|
||||
{
|
||||
memcpy(&res, &payload.at(payloadpos+3)-3, 4);
|
||||
payloadpos+=4;
|
||||
res=ntohl(res);
|
||||
}
|
||||
|
||||
std::string getBlob(int size)
|
||||
void xfrBlob(std::string& blob, int size, uint16_t* pos = 0)
|
||||
{
|
||||
std::string ret(&payload.at(payloadpos), &payload.at(payloadpos+size));
|
||||
payloadpos += size;
|
||||
return ret;
|
||||
if(!pos) pos = &payloadpos;
|
||||
if(!size) {
|
||||
blob.clear();
|
||||
return;
|
||||
}
|
||||
blob.assign(&payload.at(*pos), &payload.at(*pos+size-1)+1);
|
||||
(*pos) += size;
|
||||
}
|
||||
|
||||
std::string getBlob(int size, uint16_t* pos = 0)
|
||||
{
|
||||
std::string res;
|
||||
xfrBlob(res, size, pos);
|
||||
return res;
|
||||
}
|
||||
|
||||
DNSName d_qname;
|
||||
DNSType d_qtype;
|
||||
DNSClass d_qclass;
|
||||
DNSType d_qtype{(DNSType)0};
|
||||
DNSClass d_qclass{(DNSClass)0};
|
||||
uint16_t d_bufsize;
|
||||
bool d_doBit{false};
|
||||
|
||||
@ -71,12 +97,12 @@ public:
|
||||
void setEDNS(uint16_t bufsize, bool doBit, RCode ercode = (RCode)0);
|
||||
std::string serialize();
|
||||
|
||||
void putUInt8(uint8_t val)
|
||||
void xfrUInt8(uint8_t val)
|
||||
{
|
||||
payload.at(payloadpos++)=val;
|
||||
}
|
||||
|
||||
uint16_t putUInt16(uint16_t val)
|
||||
uint16_t xfrUInt16(uint16_t val)
|
||||
{
|
||||
val = htons(val);
|
||||
memcpy(&payload.at(payloadpos+2)-2, &val, 2);
|
||||
@ -84,31 +110,34 @@ public:
|
||||
return payloadpos - 2;
|
||||
}
|
||||
|
||||
void putUInt16At(uint16_t pos, uint16_t val)
|
||||
void xfrUInt16At(uint16_t pos, uint16_t val)
|
||||
{
|
||||
val = htons(val);
|
||||
memcpy(&payload.at(pos+2)-2, &val, 2);
|
||||
}
|
||||
|
||||
void putUInt32(uint32_t val)
|
||||
void xfrUInt32(uint32_t val)
|
||||
{
|
||||
val = htonl(val);
|
||||
memcpy(&payload.at(payloadpos+sizeof(val)) - sizeof(val), &val, sizeof(val));
|
||||
payloadpos += sizeof(val);
|
||||
}
|
||||
|
||||
void putBlob(const std::string& blob)
|
||||
void xfrBlob(const std::string& blob)
|
||||
{
|
||||
memcpy(&payload.at(payloadpos+blob.size()) - blob.size(), blob.c_str(), blob.size());
|
||||
payloadpos += blob.size();;
|
||||
}
|
||||
|
||||
void putBlob(const unsigned char* blob, int size)
|
||||
void xfrBlob(const unsigned char* blob, int size)
|
||||
{
|
||||
memcpy(&payload.at(payloadpos+size) - size, blob, size);
|
||||
payloadpos += size;
|
||||
}
|
||||
void putName(const DNSName& name, bool compress=true);
|
||||
|
||||
void xfrName(const DNSName& name, bool compress=true);
|
||||
|
||||
|
||||
private:
|
||||
std::unique_ptr<DNSNode> d_comptree;
|
||||
void putEDNS(uint16_t bufsize, RCode ercode, bool doBit);
|
||||
|
@ -1,5 +1,10 @@
|
||||
#include "record-types.hh"
|
||||
|
||||
void UnknownGen::toMessage(DNSMessageWriter& dmw)
|
||||
{
|
||||
dmw.xfrBlob(d_rr);
|
||||
}
|
||||
|
||||
std::unique_ptr<RRGen> AGen::make(const ComboAddress& ca)
|
||||
{
|
||||
return std::make_unique<AGen>(ntohl(ca.sin4.sin_addr.s_addr));
|
||||
@ -7,7 +12,7 @@ std::unique_ptr<RRGen> AGen::make(const ComboAddress& ca)
|
||||
|
||||
void AGen::toMessage(DNSMessageWriter& dmw)
|
||||
{
|
||||
dmw.putUInt32(d_ip);
|
||||
dmw.xfrUInt32(d_ip);
|
||||
}
|
||||
|
||||
std::unique_ptr<RRGen> AAAAGen::make(const ComboAddress& ca)
|
||||
@ -23,39 +28,70 @@ std::unique_ptr<RRGen> AAAAGen::make(const ComboAddress& ca)
|
||||
|
||||
void AAAAGen::toMessage(DNSMessageWriter& dmw)
|
||||
{
|
||||
dmw.putBlob(d_ip, 16);
|
||||
dmw.xfrBlob(d_ip, 16);
|
||||
}
|
||||
|
||||
SOAGen::SOAGen(DNSMessageReader& dmr)
|
||||
{
|
||||
dmr.xfrName(d_mname); dmr.xfrName(d_rname);
|
||||
dmr.xfrUInt32(d_serial); dmr.xfrUInt32(d_refresh);
|
||||
dmr.xfrUInt32(d_retry); dmr.xfrUInt32(d_expire);
|
||||
dmr.xfrUInt32(d_minimum);
|
||||
}
|
||||
|
||||
void SOAGen::toMessage(DNSMessageWriter& dmw)
|
||||
{
|
||||
dmw.putName(d_mname); dmw.putName(d_rname);
|
||||
dmw.putUInt32(d_serial); dmw.putUInt32(d_refresh);
|
||||
dmw.putUInt32(d_retry); dmw.putUInt32(d_expire);
|
||||
dmw.putUInt32(d_minimum);
|
||||
dmw.xfrName(d_mname); dmw.xfrName(d_rname);
|
||||
dmw.xfrUInt32(d_serial); dmw.xfrUInt32(d_refresh);
|
||||
dmw.xfrUInt32(d_retry); dmw.xfrUInt32(d_expire);
|
||||
dmw.xfrUInt32(d_minimum);
|
||||
}
|
||||
|
||||
void CNAMEGen::toMessage(DNSMessageWriter& dmw)
|
||||
CNAMEGen::CNAMEGen(DNSMessageReader& x)
|
||||
{
|
||||
dmw.putName(d_name);
|
||||
x.xfrName(d_name);
|
||||
}
|
||||
void CNAMEGen::toMessage(DNSMessageWriter& x)
|
||||
{
|
||||
x.xfrName(d_name);
|
||||
}
|
||||
|
||||
void NSGen::toMessage(DNSMessageWriter& dmw)
|
||||
PTRGen::PTRGen(DNSMessageReader& x)
|
||||
{
|
||||
dmw.putName(d_name);
|
||||
x.xfrName(d_name);
|
||||
}
|
||||
void PTRGen::toMessage(DNSMessageWriter& x)
|
||||
{
|
||||
x.xfrName(d_name);
|
||||
}
|
||||
|
||||
|
||||
void MXGen::toMessage(DNSMessageWriter& dmw)
|
||||
NSGen::NSGen(DNSMessageReader& x)
|
||||
{
|
||||
dmw.putUInt16(d_prio);
|
||||
dmw.putName(d_name);
|
||||
x.xfrName(d_name);
|
||||
}
|
||||
|
||||
void NSGen::toMessage(DNSMessageWriter& x)
|
||||
{
|
||||
x.xfrName(d_name);
|
||||
}
|
||||
|
||||
MXGen::MXGen(DNSMessageReader& x)
|
||||
{
|
||||
x.xfrUInt16(d_prio);
|
||||
x.xfrName(d_name);
|
||||
}
|
||||
|
||||
void MXGen::toMessage(DNSMessageWriter& x)
|
||||
{
|
||||
x.xfrUInt16(d_prio);
|
||||
x.xfrName(d_name);
|
||||
}
|
||||
|
||||
void TXTGen::toMessage(DNSMessageWriter& dmw)
|
||||
{
|
||||
// XXX should autosplit
|
||||
dmw.putUInt8(d_txt.length());
|
||||
dmw.putBlob(d_txt);
|
||||
// XXX should autosplit or throw
|
||||
dmw.xfrUInt8(d_txt.length());
|
||||
dmw.xfrBlob(d_txt);
|
||||
}
|
||||
|
||||
void ClockTXTGen::toMessage(DNSMessageWriter& dmw)
|
||||
|
@ -4,6 +4,20 @@
|
||||
#include "dnsmessages.hh"
|
||||
#include "comboaddress.hh"
|
||||
|
||||
struct UnknownGen : RRGen
|
||||
{
|
||||
UnknownGen(DNSType type, const std::string& rr) : d_type(type), d_rr(rr) {}
|
||||
DNSType d_type;
|
||||
std::string d_rr;
|
||||
static std::unique_ptr<RRGen> make(DNSType type, const std::string& rr)
|
||||
{
|
||||
return std::make_unique<UnknownGen>(type, rr);
|
||||
}
|
||||
void toMessage(DNSMessageWriter& dpw) override;
|
||||
DNSType getType() const override { return d_type; }
|
||||
};
|
||||
|
||||
|
||||
struct AGen : RRGen
|
||||
{
|
||||
AGen(uint32_t ip) : d_ip(ip) {}
|
||||
@ -33,17 +47,22 @@ struct AAAAGen : RRGen
|
||||
unsigned char d_ip[16];
|
||||
};
|
||||
|
||||
class DNSMessageReader;
|
||||
|
||||
struct SOAGen : RRGen
|
||||
{
|
||||
SOAGen(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) :
|
||||
d_mname(mname), d_rname(rname), d_serial(serial), d_minimum(minimum), d_refresh(refresh), d_retry(retry), d_expire(expire)
|
||||
{}
|
||||
|
||||
SOAGen(DNSMessageReader& dmr);
|
||||
|
||||
template<typename ... Targs>
|
||||
static std::unique_ptr<RRGen> make(const DNSName& mname, const DNSName& rname, Targs&& ... fargs)
|
||||
{
|
||||
return std::make_unique<SOAGen>(mname, rname, std::forward<Targs>(fargs)...);
|
||||
}
|
||||
|
||||
void toMessage(DNSMessageWriter& dpw) override;
|
||||
DNSType getType() const override { return DNSType::SOA; }
|
||||
DNSName d_mname, d_rname;
|
||||
@ -53,6 +72,7 @@ struct SOAGen : RRGen
|
||||
struct CNAMEGen : RRGen
|
||||
{
|
||||
CNAMEGen(const DNSName& name) : d_name(name) {}
|
||||
CNAMEGen(DNSMessageReader& dmr);
|
||||
static std::unique_ptr<RRGen> make(const DNSName& mname)
|
||||
{
|
||||
return std::make_unique<CNAMEGen>(mname);
|
||||
@ -62,9 +82,23 @@ struct CNAMEGen : RRGen
|
||||
DNSName d_name;
|
||||
};
|
||||
|
||||
struct PTRGen : RRGen
|
||||
{
|
||||
PTRGen(const DNSName& name) : d_name(name) {}
|
||||
PTRGen(DNSMessageReader& dmr);
|
||||
static std::unique_ptr<RRGen> make(const DNSName& mname)
|
||||
{
|
||||
return std::make_unique<PTRGen>(mname);
|
||||
}
|
||||
void toMessage(DNSMessageWriter& dpw) override;
|
||||
DNSType getType() const override { return DNSType::PTR; }
|
||||
DNSName d_name;
|
||||
};
|
||||
|
||||
struct NSGen : RRGen
|
||||
{
|
||||
NSGen(const DNSName& name) : d_name(name) {}
|
||||
NSGen(DNSMessageReader& dmr);
|
||||
static std::unique_ptr<RRGen> make(const DNSName& mname)
|
||||
{
|
||||
return std::make_unique<NSGen>(mname);
|
||||
@ -78,6 +112,8 @@ struct NSGen : RRGen
|
||||
struct MXGen : RRGen
|
||||
{
|
||||
MXGen(uint16_t prio, const DNSName& name) : d_prio(prio), d_name(name) {}
|
||||
MXGen(DNSMessageReader& dmr);
|
||||
|
||||
static std::unique_ptr<RRGen> make(uint16_t prio, const DNSName& name)
|
||||
{
|
||||
return std::make_unique<MXGen>(prio, name);
|
||||
|
100
tdns/tdns.cc
100
tdns/tdns.cc
@ -94,7 +94,7 @@ bool processQuestion(const DNSNode& zones, DNSMessageReader& dm, const ComboAddr
|
||||
cout<<"---\nFound best zone: "<<zonename<<", qname now "<<qname<<endl;
|
||||
response.dh.aa = 1;
|
||||
|
||||
auto bestzone = fnd->zone;
|
||||
auto bestzone = fnd->zone.get();
|
||||
DNSName searchname(qname), lastnode, zonecutname;
|
||||
const DNSNode* passedZonecut=0;
|
||||
int CNAMELoopCount = 0;
|
||||
@ -207,29 +207,37 @@ void udpThread(ComboAddress local, Socket* sock, const DNSNode* zones)
|
||||
}
|
||||
}
|
||||
|
||||
static void writeTCPResponse(int sock, DNSMessageWriter& response)
|
||||
static void writeTCPMessage(int sock, DNSMessageWriter& response)
|
||||
{
|
||||
string ser="00"+response.serialize();
|
||||
cout<<"Sending a message of "<<ser.size()<<" bytes in response"<<endl;
|
||||
// cout<<"Sending a message of "<<ser.size()<<" bytes in response"<<endl;
|
||||
uint16_t len = htons(ser.length()-2);
|
||||
ser[0] = *((char*)&len);
|
||||
ser[1] = *(((char*)&len) + 1);
|
||||
SWriten(sock, ser);
|
||||
}
|
||||
|
||||
uint16_t tcpGetLen(int sock)
|
||||
{
|
||||
string message = SRead(sock, 2);
|
||||
if(message.empty())
|
||||
return 0;
|
||||
if(message.size() != 2) {
|
||||
throw std::runtime_error("Incomplete TCP/IP message");
|
||||
}
|
||||
uint16_t len;
|
||||
memcpy(&len, &message.at(1)-1, 2);
|
||||
return htons(len);
|
||||
}
|
||||
|
||||
void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNode* zones)
|
||||
{
|
||||
Socket sock(s);
|
||||
cout<<"TCP Connection from "<<remote.toStringWithPort()<<endl;
|
||||
for(;;) {
|
||||
uint16_t len;
|
||||
|
||||
string message = SRead(sock, 2);
|
||||
if(message.size() != 2)
|
||||
break;
|
||||
memcpy(&len, &message.at(1)-1, 2);
|
||||
len=htons(len);
|
||||
|
||||
uint16_t len=tcpGetLen(sock);
|
||||
if(!len)
|
||||
return;
|
||||
if(len > 512) {
|
||||
cerr<<"Remote "<<remote.toStringWithPort()<<" sent question that was too big"<<endl;
|
||||
return;
|
||||
@ -240,15 +248,15 @@ void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNo
|
||||
return;
|
||||
}
|
||||
|
||||
message = SRead(sock, len);
|
||||
std::string message = SRead(sock, len);
|
||||
DNSMessageReader dm(message);
|
||||
|
||||
DNSName name;
|
||||
DNSType type;
|
||||
dm.getQuestion(name, type);
|
||||
|
||||
DNSMessageWriter response(name, type, std::numeric_limits<uint16_t>::max());
|
||||
response.d_nocompress = true;
|
||||
DNSMessageWriter response(name, type, 16384);
|
||||
// response.d_nocompress = true;
|
||||
if(type == DNSType::AXFR || type == DNSType::IXFR) {
|
||||
if(dm.dh.opcode || dm.dh.qr) {
|
||||
cerr<<"Dropping non-query AXFR from "<<remote.toStringWithPort()<<endl; // too weird
|
||||
@ -266,17 +274,17 @@ void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNo
|
||||
if(!fnd || !fnd->zone || !name.empty() || !fnd->zone->rrsets.count(DNSType::SOA)) {
|
||||
cout<<" This was not a zone, or zone had no SOA"<<endl;
|
||||
response.dh.rcode = (int)RCode::Refused;
|
||||
writeTCPResponse(sock, response);
|
||||
writeTCPMessage(sock, response);
|
||||
continue;
|
||||
}
|
||||
cout<<"Have zone, walking it"<<endl;
|
||||
|
||||
auto node = fnd->zone;
|
||||
auto node = fnd->zone.get();
|
||||
|
||||
// send SOA
|
||||
response.putRR(DNSSection::Answer, zone, DNSType::SOA, node->rrsets[DNSType::SOA].ttl, node->rrsets[DNSType::SOA].contents[0]);
|
||||
|
||||
writeTCPResponse(sock, response);
|
||||
writeTCPMessage(sock, response);
|
||||
response.clearRRs();
|
||||
|
||||
// send all other records
|
||||
@ -290,7 +298,7 @@ void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNo
|
||||
response.putRR(DNSSection::Answer, nname, p.first, p.second.ttl, rr);
|
||||
}
|
||||
catch(std::out_of_range& e) { // exceeded packet size
|
||||
writeTCPResponse(sock, response);
|
||||
writeTCPMessage(sock, response);
|
||||
response.clearRRs();
|
||||
goto retry;
|
||||
}
|
||||
@ -298,18 +306,18 @@ void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNo
|
||||
}
|
||||
}, zone);
|
||||
|
||||
writeTCPResponse(sock, response);
|
||||
writeTCPMessage(sock, response);
|
||||
response.clearRRs();
|
||||
|
||||
// send SOA again
|
||||
response.putRR(DNSSection::Answer, zone, DNSType::SOA, node->rrsets[DNSType::SOA].ttl, node->rrsets[DNSType::SOA].contents[0]);
|
||||
|
||||
writeTCPResponse(sock, response);
|
||||
writeTCPMessage(sock, response);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if(processQuestion(*zones, dm, local, remote, response)) {
|
||||
writeTCPResponse(sock, response);
|
||||
writeTCPMessage(sock, response);
|
||||
}
|
||||
else
|
||||
return;
|
||||
@ -317,6 +325,49 @@ void tcpClientThread(ComboAddress local, ComboAddress remote, int s, const DNSNo
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<DNSNode> retrieveZone(const ComboAddress& remote, const DNSName& zone)
|
||||
{
|
||||
cout<<"Attempting to retrieve zone "<<zone<<" from "<<remote.toStringWithPort()<<endl;
|
||||
Socket tcp(remote.sin4.sin_family, SOCK_STREAM);
|
||||
SConnect(tcp, remote);
|
||||
|
||||
DNSMessageWriter dmw(zone, DNSType::AXFR);
|
||||
writeTCPMessage(tcp, dmw);
|
||||
|
||||
auto ret = std::make_unique<DNSNode>();
|
||||
|
||||
int soaCount=0;
|
||||
for(;;) {
|
||||
uint16_t len = tcpGetLen(tcp);
|
||||
string message = SRead(tcp, len);
|
||||
|
||||
cout<<"Got "<<message.length()<<" bytes out of "<<len<<endl;
|
||||
DNSMessageReader dmr(message);
|
||||
|
||||
if(dmr.dh.rcode != (int)RCode::Noerror) {
|
||||
cout<<"Got error "<<dmr.dh.rcode<<" from auth "<<remote.toStringWithPort()<< " when attempting to retrieve "<<zone<<endl;
|
||||
return std::unique_ptr<DNSNode>();
|
||||
}
|
||||
|
||||
DNSName rrname;
|
||||
DNSType rrtype;
|
||||
DNSSection rrsection;
|
||||
uint32_t ttl;
|
||||
std::unique_ptr<RRGen> rr;
|
||||
while(dmr.getRR(rrsection, rrname, rrtype, ttl, rr)) {
|
||||
if(!rrname.makeRelative(zone))
|
||||
continue;
|
||||
if(rrtype == DNSType::SOA && ++soaCount==2)
|
||||
goto done;
|
||||
ret->add(rrname)->addRRs(std::move(rr));
|
||||
ret->add(rrname)->rrsets[rrtype].ttl = ttl;
|
||||
}
|
||||
}
|
||||
done:
|
||||
cout<<"Done"<<endl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try
|
||||
{
|
||||
@ -325,6 +376,7 @@ try
|
||||
return(EXIT_FAILURE);
|
||||
}
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
ComboAddress local(argv[1], 53);
|
||||
|
||||
Socket udplistener(local.sin4.sin_family, SOCK_DGRAM);
|
||||
@ -337,6 +389,12 @@ try
|
||||
|
||||
DNSNode zones;
|
||||
loadZones(zones);
|
||||
|
||||
/*
|
||||
zones.add({})->zone=retrieveZone(ComboAddress("2001:500:2f::f", 53), {});
|
||||
zones.add({"hubertnet", "nl"})->zone=retrieveZone(ComboAddress("52.48.64.3", 53), {"hubertnet", "nl"});
|
||||
zones.add({"ds9a", "nl"})->zone=retrieveZone(ComboAddress("52.48.64.3", 53), {"ds9a", "nl"});
|
||||
*/
|
||||
|
||||
thread udpServer(udpThread, local, &udplistener, &zones);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user