Permalink
Browse files

implement DNAME. Probably some rough edges.

  • Loading branch information...
ahupowerdns authored and Habbie committed Apr 7, 2014
1 parent 40c3b29 commit 8dee07500bc2e924ac631a51f954cef46c23c148
Showing with 144 additions and 25 deletions.
  1. +2 −1 modules/tinydnsbackend/data
  2. BIN modules/tinydnsbackend/data.cdb
  3. +2 −0 pdns/common_startup.cc
  4. +2 −0 pdns/dnsrecords.cc
  5. +10 −0 pdns/dnsrecords.hh
  6. +55 −3 pdns/packethandler.cc
  7. +3 −0 pdns/packethandler.hh
  8. +5 −0 pdns/pdns.conf-dist
  9. +2 −1 pdns/qtype.hh
  10. +1 −0 pdns/zoneparser-tng.cc
  11. +2 −2 regression-tests.nobackend/tinydns-data-check/expected_result
  12. +2 −2 regression-tests/backends/bind-master
  13. +1 −1 regression-tests/backends/bind-slave
  14. +2 −2 regression-tests/backends/gmysql-master
  15. +1 −1 regression-tests/backends/gmysql-slave
  16. +2 −2 regression-tests/backends/gpgsql-master
  17. +2 −2 regression-tests/backends/gsqlite3-master
  18. +1 −1 regression-tests/backends/gsqlite3-slave
  19. +1 −1 regression-tests/backends/mydns-master
  20. +1 −1 regression-tests/backends/opendbx-master
  21. +1 −1 regression-tests/backends/remote-master
  22. +1 −1 regression-tests/backends/tinydns-master
  23. +2 −0 regression-tests/tests/dname-self/command
  24. +1 −0 regression-tests/tests/dname-self/description
  25. +3 −0 regression-tests/tests/dname-self/expected_result
  26. 0 regression-tests/tests/dname-self/skip-unboundhost
  27. 0 regression-tests/tests/dname-self/skip.mydns
  28. 0 regression-tests/tests/dname-self/skip.nodnssec
  29. +2 −0 regression-tests/tests/dname/command
  30. +1 −0 regression-tests/tests/dname/description
  31. +29 −0 regression-tests/tests/dname/expected_result
  32. 0 regression-tests/tests/dname/skip-unboundhost
  33. 0 regression-tests/tests/dname/skip.mydns
  34. 0 regression-tests/tests/dname/skip.nodnssec
  35. +2 −1 regression-tests/tests/ent-axfr/expected_result
  36. +2 −1 regression-tests/tests/ent-axfr/expected_result.nsec3
  37. +2 −1 regression-tests/tests/ent-axfr/expected_result.nsec3-optout
  38. +1 −0 regression-tests/zones/test.com
@@ -20017,7 +20017,7 @@ Cexternal.example.com:somewhere.else.net.:120
+host-9998.example.com:192.168.1.14:120
+host-9999.example.com:192.168.1.15:120
:hwinfo.example.com:13:\003abc\003def:120
-:ipv6.example.com:28:\040\001\006\250\000\000\000\001\002\020K\377\376KLa:120
+3ipv6.example.com:200106a80000000102104bfffe4b4c61:120
&italy.example.com::italy-ns1.example.com.:120
&italy.example.com::italy-ns2.example.com.:120
+italy-ns1.example.com:192.168.5.1:120
@@ -20154,6 +20154,7 @@ Ztest.com:ns1.test.com.:ahu.example.com.:2005092501:28800:7200:604800:86400:3600
+b.c.test.com:5.6.7.8:3600
+\052.a.b.c.test.com:8.7.6.5:3600
+counter.test.com:1.1.1.5:3600
+:d.test.com:39:\002d2\005test2\003com\000:3600
:_double._tcp.dc.test.com:33:\000\000\000d\001\205\007server1\004test\003com\000:3600
:_double._tcp.dc.test.com:33:\000\001\000d\001\205\007server1\004test\003com\000:3600
:_ldap._tcp.dc.test.com:33:\000\000\000d\001\205\007server2\007example\003net\000:3600
Binary file not shown.
View
@@ -65,6 +65,7 @@ void declareArguments()
::arg().set("retrieval-threads", "Number of AXFR-retrieval threads for slave operation")="2";
::arg().setSwitch("experimental-json-interface", "If the webserver should serve JSON data")="no";
::arg().setSwitch("experimental-api-readonly", "If the JSON API should disallow data modification")="no";
+ ::arg().setSwitch("experimental-dname-processing", "If we should support DNAME records")="no";
::arg().setCmd("help","Provide a helpful message");
::arg().setCmd("version","Output version and compilation date");
@@ -349,6 +350,7 @@ void mainthread()
g_anyToTcp = ::arg().mustDo("any-to-tcp");
g_addSuperfluousNSEC3 = ::arg().mustDo("add-superfluous-nsec3-for-old-bind");
+
DNSPacket::s_udpTruncationThreshold = std::max(512, ::arg().asNum("udp-truncation-threshold"));
DNSPacket::s_doEDNSSubnetProcessing = ::arg().mustDo("edns-subnet-processing");
{
View
@@ -119,6 +119,7 @@ boilerplate_conv(AAAA, ns_t_aaaa, conv.xfrIP6(d_ip6); );
boilerplate_conv(NS, ns_t_ns, conv.xfrLabel(d_content, true));
boilerplate_conv(PTR, ns_t_ptr, conv.xfrLabel(d_content, true));
boilerplate_conv(CNAME, ns_t_cname, conv.xfrLabel(d_content, true));
+boilerplate_conv(DNAME, ns_t_dname, conv.xfrLabel(d_content));
boilerplate_conv(MR, ns_t_mr, conv.xfrLabel(d_alias, true));
boilerplate_conv(MINFO, ns_t_minfo, conv.xfrLabel(d_rmailbx, true); conv.xfrLabel(d_emailbx, true));
boilerplate_conv(TXT, ns_t_txt, conv.xfrText(d_text, true));
@@ -493,6 +494,7 @@ void reportBasicTypes()
void reportOtherTypes()
{
AFSDBRecordContent::report();
+ DNAMERecordContent::report();
SPFRecordContent::report();
NAPTRRecordContent::report();
LOCRecordContent::report();
View
@@ -201,6 +201,16 @@ private:
string d_content;
};
+class DNAMERecordContent : public DNSRecordContent
+{
+public:
+ includeboilerplate(DNAME)
+
+private:
+ string d_content;
+};
+
+
class MRRecordContent : public DNSRecordContent
{
public:
View
@@ -1,6 +1,6 @@
/*
PowerDNS Versatile Database Driven Nameserver
- Copyright (C) 2002-2012 PowerDNS.COM BV
+ Copyright (C) 2002-2014 PowerDNS.COM BV
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
@@ -62,6 +62,7 @@ PacketHandler::PacketHandler():B(s_programname)
{
++s_count;
d_doFancyRecords = (::arg()["fancy-records"]!="no");
+ d_doDNAME=::arg().mustDo("experimental-dname-processing");
d_doRecursion= ::arg().mustDo("recursor");
d_logDNSDetails= ::arg().mustDo("log-dns-details");
d_doIPv6AdditionalProcessing = ::arg().mustDo("do-ipv6-additional-processing");
@@ -331,6 +332,37 @@ vector<DNSResourceRecord> PacketHandler::getBestReferralNS(DNSPacket *p, SOAData
return ret;
}
+vector<DNSResourceRecord> PacketHandler::getBestDNAMESynth(DNSPacket *p, SOAData& sd, string &target)
+{
+ vector<DNSResourceRecord> ret;
+ DNSResourceRecord rr;
+ string prefix;
+ string subdomain(target);
+ do {
+ DLOG(L<<"Attempting DNAME lookup for "<<subdomain<<", sd.qname="<<sd.qname<<endl);
+
+ B.lookup(QType(QType::DNAME), subdomain, p, sd.domain_id);
+ while(B.get(rr)) {
+ ret.push_back(rr); // put in the original
+ rr.qtype = QType::CNAME;
+ rr.qname = prefix + rr.qname;
+ rr.content = prefix + rr.content;
+ target= rr.content;
+ ret.push_back(rr);
+ }
+ if(!ret.empty())
+ return ret;
+ string::size_type pos = subdomain.find('.');
+ if(pos != string::npos)
+ prefix+= subdomain.substr(0, pos+1);
+ if(subdomain == sd.qname) // stop at SOA
+ break;
+
+ } while( chopOff( subdomain ) ); // 'www.powerdns.org' -> 'powerdns.org' -> 'org' -> ''
+ return ret;
+}
+
+
// Return best matching wildcard or next closer name
bool PacketHandler::getBestWildcard(DNSPacket *p, SOAData& sd, const string &target, string &wildcard, vector<DNSResourceRecord>* ret)
{
@@ -1070,6 +1102,21 @@ void PacketHandler::completeANYRecords(DNSPacket *p, DNSPacket*r, SOAData& sd, c
}
}
+bool PacketHandler::tryDNAME(DNSPacket *p, DNSPacket*r, SOAData& sd, string &target)
+{
+ if(!d_doDNAME)
+ return false;
+ DLOG(L<<Logger::Warning<<"Let's try DNAME.."<<endl);
+ vector<DNSResourceRecord> rrset = getBestDNAMESynth(p, sd, target);
+ if(!rrset.empty()) {
+ BOOST_FOREACH(DNSResourceRecord& rr, rrset) {
+ rr.d_place = DNSResourceRecord::ANSWER;
+ r->addRecord(rr);
+ }
+ return true;
+ }
+ return false;
+}
bool PacketHandler::tryWildcard(DNSPacket *p, DNSPacket*r, SOAData& sd, string &target, string &wildcard, bool& retargeted, bool& nodata)
{
retargeted = nodata = false;
@@ -1199,7 +1246,6 @@ DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
return r;
}
-
string target=p->qdomain;
// catch chaos qclass requests
@@ -1312,7 +1358,7 @@ DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
// see what we get..
B.lookup(QType(QType::ANY), target, p, sd.domain_id);
rrset.clear();
- weDone = weRedirected = weHaveUnauth = 0;
+ weDone = weRedirected = weHaveUnauth = false;
while(B.get(rr)) {
if (p->qtype.getCode() == QType::ANY) {
@@ -1350,6 +1396,7 @@ DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
rrset.push_back(rr);
}
+
DLOG(L<<"After first ANY query for '"<<target<<"', id="<<sd.domain_id<<": weDone="<<weDone<<", weHaveUnauth="<<weHaveUnauth<<", weRedirected="<<weRedirected<<endl);
if(p->qtype.getCode() == QType::DS && weHaveUnauth && !weDone && !weRedirected && d_dk.isSecuredZone(sd.qname)) {
DLOG(L<<"Q for DS of a name for which we do have NS, but for which we don't have on a zone with DNSSEC need to provide an AUTH answer that proves we don't"<<endl);
@@ -1369,6 +1416,7 @@ DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
}
}
+
DLOG(L<<Logger::Warning<<"Found nothing in the by-name ANY, but let's try wildcards.."<<endl);
bool wereRetargeted(false), nodata(false);
string wildcard;
@@ -1382,6 +1430,10 @@ DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
goto sendit;
}
+ else if(tryDNAME(p, r, sd, target)) {
+ retargetcount++;
+ goto retargeted;
+ }
else
{
if (!(((p->qtype.getCode() == QType::CNAME) || (p->qtype.getCode() == QType::ANY)) && retargetcount > 0))
View
@@ -95,6 +95,8 @@ private:
void makeNXDomain(DNSPacket* p, DNSPacket* r, const std::string& target, const std::string& wildcard, SOAData& sd);
void makeNOError(DNSPacket* p, DNSPacket* r, const std::string& target, const std::string& wildcard, SOAData& sd, int mode);
vector<DNSResourceRecord> getBestReferralNS(DNSPacket *p, SOAData& sd, const string &target);
+ vector<DNSResourceRecord> getBestDNAMESynth(DNSPacket *p, SOAData& sd, string &target);
+ bool tryDNAME(DNSPacket *p, DNSPacket*r, SOAData& sd, string &target);
bool tryReferral(DNSPacket *p, DNSPacket*r, SOAData& sd, const string &target);
bool getBestWildcard(DNSPacket *p, SOAData& sd, const string &target, string &wildcard, vector<DNSResourceRecord>* ret);
@@ -108,6 +110,7 @@ private:
bool d_doRecursion;
bool d_logDNSDetails;
bool d_doIPv6AdditionalProcessing;
+ bool d_doDNAME;
int d_sendRootReferral;
AuthLua* d_pdl;
View
@@ -154,6 +154,11 @@
#
# experimental-api-readonly=no
+#################################
+# experimental-dname-processing If we should support DNAME records
+#
+# experimental-dname-processing=no
+
#################################
# experimental-json-interface If the webserver should serve JSON data
#
View
@@ -81,7 +81,7 @@ public:
// more solaris fun
#undef DS
enum typeenum {A=1, NS=2, CNAME=5, SOA=6, MR=9, PTR=12, HINFO=13, MX=15, TXT=16, RP=17, AFSDB=18, SIG=24, KEY=25, AAAA=28, LOC=29, SRV=33, NAPTR=35, KX=36,
- CERT=37, A6=38, OPT=41, DS=43, SSHFP=44, IPSECKEY=45, RRSIG=46, NSEC=47, DNSKEY=48, DHCID=49, NSEC3=50, NSEC3PARAM=51,
+ CERT=37, A6=38, DNAME=39, OPT=41, DS=43, SSHFP=44, IPSECKEY=45, RRSIG=46, NSEC=47, DNSKEY=48, DHCID=49, NSEC3=50, NSEC3PARAM=51,
TLSA=52, SPF=99, EUI48=108, EUI64=109, TSIG=250, IXFR=251, AXFR=252, MAILB=253, MAILA=254, ANY=255, URL=256, MBOXFW=257, CURL=258, ADDR=259, DLV=32769} types;
typedef pair<string,uint16_t> namenum;
static vector<namenum> names;
@@ -141,6 +141,7 @@ private:
qtype_insert("KX", 36);
qtype_insert("CERT", 37);
qtype_insert("A6", 38);
+ qtype_insert("DNAME", 39);
qtype_insert("OPT", 41);
qtype_insert("DS", 43);
qtype_insert("SSHFP", 44);
View
@@ -401,6 +401,7 @@ bool ZoneParserTNG::get(DNSResourceRecord& rr)
case QType::NS:
case QType::CNAME:
+ case QType::DNAME:
case QType::PTR:
case QType::AFSDB:
rr.content=stripDot(toCanonic(d_zonename, rr.content));
@@ -1,5 +1,5 @@
4bc48a8d9b8d04b553be67639e5656e8 ../regression-tests/zones/example.com
-4e9eed426f0cd123c08dffb9e715824a ../regression-tests/zones/test.com
+38f421f6dd6c842f6f4f8a5a996cdb7d ../regression-tests/zones/test.com
005b3381db2a7dc70b690484f6ab7770 ../regression-tests/zones/test.dyndns
21213b4e8cd56e4184696a1bafd987d7 ../regression-tests/zones/wtest.com
42b442de632686e94bde75acf66cf524 ../regression-tests/zones/nztest.com
@@ -9,4 +9,4 @@ a63dc120391d9df0003f2ec4f461a6af ../regression-tests/zones/secure-delegated.dns
24514dc104b22206daeb973ff9303545 ../regression-tests/zones/minimal.com
0b20d7a0250576451135483b863750bf ../regression-tests/zones/tsig.com
b1f775045fa2cf0a3b91aa834af06e49 ../regression-tests/zones/stest.com
-a691ba21cde91f1b9f3cc92c7a078636 ../modules/tinydnsbackend/data.cdb
+11085cc884e2db8bbd5a4710aad23604 ../modules/tinydnsbackend/data.cdb
@@ -4,7 +4,7 @@ case $context in
--no-shuffle --launch=bind --bind-config=./named.conf \
--send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --no-config --experimental-dname-processing \
--bind-ignore-broken-records=yes &
skipreasons="nodnssec nodyndns nometa"
bindwait
@@ -51,7 +51,7 @@ case $context in
--bind-dnssec-db=./dnssec.sqlite3 \
--send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes --direct-dnskey=yes \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --no-config --experimental-dname-processing \
--bind-ignore-broken-records=yes $lua_prequery &
bindwait
;;
@@ -30,6 +30,6 @@
--no-shuffle --launch=bind --bind-config=./named-slave.conf --slave \
--send-root-referral --retrieval-threads=1 --config-name=bind-slave \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
- --cache-ttl=$cachettl --no-config --bind-dnssec-db=./dnssec-slave.sqlite3 &
+ --cache-ttl=$cachettl --no-config --experimental-dname-processing --bind-dnssec-db=./dnssec-slave.sqlite3 &
echo 'waiting for zones to be loaded'
bindwait bind-slave
@@ -35,7 +35,7 @@ __EOF__
--no-shuffle --launch=gmysql \
--master --send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes --direct-dnskey=yes \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --experimental-dname-processing --no-config \
--gmysql-dbname="$GMYSQLDB" \
--gmysql-user="$GMYSQLUSER" \
--gmysql-host="$GMYSQLHOST" \
@@ -87,7 +87,7 @@ __EOF__
--no-shuffle --launch --launch+=random --launch+=gmysql --launch+=random --gmysql-dnssec \
--send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes --direct-dnskey=yes \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --experimental-dname-processing --no-config \
--gmysql-dbname="$GMYSQLDB" \
--gmysql-user="$GMYSQLUSER" \
--gmysql-host="$GMYSQLHOST" \
@@ -36,7 +36,7 @@
--no-shuffle --launch=gmysql --gmysql-dnssec \
--send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
- --cache-ttl=$cachettl --query-cache-ttl=$cachettl --no-config --slave --retrieval-threads=4 \
+ --cache-ttl=$cachettl --query-cache-ttl=$cachettl --experimental-dname-processing --no-config --slave --retrieval-threads=4 \
--gmysql-dbname="$GMYSQL2DB" \
--gmysql-user="$GMYSQL2USER" \
--gmysql-host="$GMYSQL2HOST" \
@@ -27,7 +27,7 @@ __EOF__
--no-shuffle --launch=gpgsql \
--send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --experimental-dname-processing --no-config \
--gpgsql-dbname="$GPGSQLDB" \
--gpgsql-user="$GPGSQLUSER" &
skipreasons="nodnssec nodyndns"
@@ -67,7 +67,7 @@ __EOF__
--no-shuffle --launch=gpgsql --gpgsql-dnssec \
--send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --experimental-dname-processing --no-config \
--gpgsql-dbname="$GPGSQLDB" \
--gpgsql-user="$GPGSQLUSER" $lua_prequery &
if [ $context = gpgsql-nsec3 ]
@@ -21,7 +21,7 @@ __EOF__
--no-shuffle --launch=gsqlite3 \
--send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --experimental-dname-processing --no-config \
--gsqlite3-database=pdns.sqlite3 &
skipreasons="nodnssec nodyndns"
;;
@@ -54,7 +54,7 @@ __EOF__
--no-shuffle --launch=gsqlite3 --gsqlite3-dnssec \
--send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --experimental-dname-processing --no-config \
--gsqlite3-database=pdns.sqlite3 $lua_prequery &
if [ $context = gsqlite3-nsec3 ]
then
@@ -22,7 +22,7 @@
$RUNWRAPPER $PDNS2 --daemon=no --local-port=$port --socket-dir=./ \
--no-shuffle --launch=gsqlite3 --gsqlite3-dnssec \
--fancy-records --send-root-referral \
- --cache-ttl=0 --query-cache-ttl=0 --no-config --slave --retrieval-threads=4 \
+ --cache-ttl=0 --query-cache-ttl=0 --experimental-dname-processing --no-config --slave --retrieval-threads=4 \
--gsqlite3-database=pdns.sqlite31 --gsqlite3-pragma-synchronous=0 \
--config-name=gsqlite32 &
echo 'waiting for zones to be slaved'
@@ -24,7 +24,7 @@ __EOF__
$RUNWRAPPER $PDNS --daemon=no --local-port=$port --socket-dir=./ \
--no-shuffle --launch=mydns \
--master --send-root-referral \
- --cache-ttl=0 --no-config \
+ --cache-ttl=0 --experimental-dname-processing --no-config \
--mydns-rr-active=no --mydns-soa-active=no --mydns-use-minimal-ttl=no \
--mydns-dbname="$MYDNSDB" \
--mydns-user="$MYDNSUSER" \
@@ -10,7 +10,7 @@ case $context in
$RUNWRAPPER $PDNS --daemon=no --local-port=$port --socket-dir=./ \
--no-shuffle --launch=opendbx \
--send-root-referral \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --experimental-dname-processing --no-config \
--opendbx-backend=sqlite3 --opendbx-host-read=./ --opendbx-host-write=./ \
--opendbx-database=pdns-opendbx.sqlite3 &
skipreasons="nodnssec noent nodyndns nometa"
@@ -106,7 +106,7 @@ EOF
$RUNWRAPPER $PDNS --daemon=no --local-port=$port --socket-dir=./ \
--no-shuffle --launch=remote \
- --query-logging --loglevel=9 --cache-ttl=$cachettl --no-config \
+ --query-logging --loglevel=9 --cache-ttl=$cachettl --experimental-dname-processing --no-config \
--send-root-referral --distributor-threads=1 \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
--remote-connection-string="$connstr" $remote_add_param &
@@ -2,7 +2,7 @@ case $context in
tinydns)
$RUNWRAPPER $PDNS --daemon=no --local-port=$port --socket-dir=./ \
--no-shuffle --launch=tinydns \
- --cache-ttl=$cachettl --no-config \
+ --cache-ttl=$cachettl --experimental-dname-processing --no-config \
--send-root-referral \
--allow-2136-from=127.0.0.0/8 --experimental-rfc2136=yes \
--tinydns-dbfile=../modules/tinydnsbackend/data.cdb &
@@ -0,0 +1,2 @@
+#!/bin/sh
+cleandig d.test.com A
@@ -0,0 +1 @@
+Make sure a DNAME does not synthesise for its own owner name.
@@ -0,0 +1,3 @@
+1 test.com. IN SOA 3600 ns1.test.com. ahu.example.com. 2005092501 28800 7200 604800 86400
+Rcode: 0, RD: 0, QR: 1, TC: 0, AA: 1, opcode: 0
+Reply to question for qname='d.test.com.', qtype=A
@@ -0,0 +1,2 @@
+#!/bin/sh
+cleandig www.d.test.com A
Oops, something went wrong.

0 comments on commit 8dee075

Please sign in to comment.