]> granicus.if.org Git - pdns/commitdiff
add edns-subnet parsing, plus some testing code in 'toysdig'. Off by default, turn...
authorBert Hubert <bert.hubert@netherlabs.nl>
Mon, 11 Jul 2011 07:25:27 +0000 (07:25 +0000)
committerBert Hubert <bert.hubert@netherlabs.nl>
Mon, 11 Jul 2011 07:25:27 +0000 (07:25 +0000)
git-svn-id: svn://svn.powerdns.com/pdns/trunk/pdns@2231 d19b8d6e-7fed-0310-83ef-9ca221ded41b

pdns/common_startup.cc
pdns/dns.hh
pdns/dnsbackend.cc
pdns/dnspacket.cc
pdns/dnspacket.hh
pdns/ednssubnet.cc
pdns/packethandler.cc
pdns/toysdig.cc

index f2c474afb1e9f7fbbd770e86154017cb7615abdf..8cb3c4de0d4b8bed40ef70d2e2248b09f942b3e7 100644 (file)
@@ -102,6 +102,7 @@ void declareArguments()
 
   ::arg().setSwitch("webserver","Start a webserver for monitoring")="no"; 
   ::arg().setSwitch("webserver-print-arguments","If the webserver should print arguments")="no"; 
+  ::arg().setSwitch("edns-subnet-processing","If we should act on EDNS Subnet options")="no"; 
   ::arg().set("webserver-address","IP Address of webserver to listen on")="127.0.0.1";
   ::arg().set("webserver-port","Port of webserver to listen on")="8081";
   ::arg().set("webserver-password","Password required for accessing the webserver")="";
@@ -311,6 +312,10 @@ void mainthread()
    int newuid=0;      
    if(!::arg()["setuid"].empty())        
      newuid=Utility::makeUidNumeric(::arg()["setuid"]); 
+     
+   
+   DNSPacket::s_doEDNSSubnetProcessing = ::arg().mustDo("edns-subnet-processing");
+     
 #ifndef WIN32
 
    if(!::arg()["chroot"].empty()) {  
index 9674e2facf0ca2410b7b5f1c07a825b1914ef857..4f883775bc50af4a19f00ae9d9c22fc67971c936 100644 (file)
@@ -48,6 +48,7 @@ struct SOAData
   uint32_t default_ttl;
   int domain_id;
   DNSBackend *db;
+  uint8_t scopeMask;
 };
 
 
@@ -67,7 +68,7 @@ public:
 class DNSResourceRecord
 {
 public:
-  DNSResourceRecord() : qclass(1), priority(0), last_modified(0), d_place(ANSWER), auth(1) {};
+  DNSResourceRecord() : qclass(1), priority(0), last_modified(0), d_place(ANSWER), auth(1), scopeMask(0) {};
   ~DNSResourceRecord(){};
 
   // data
@@ -85,6 +86,7 @@ public:
   Place d_place; //!< This specifies where a record goes within the packet
 
   bool auth;
+  uint8_t scopeMask;
 
   template<class Archive>
   void serialize(Archive & ar, const unsigned int version)
index 350b128a9a73a7d0d761fc1e3a91d7a075afcef6..a3f1509892b7d37901057753eb76dd67ffa42821 100644 (file)
@@ -219,6 +219,7 @@ bool DNSBackend::getSOA(const string &domain, SOAData &sd, DNSPacket *p)
     fillSOAData(rr.content, sd);
     sd.domain_id=rr.domain_id;
     sd.ttl=rr.ttl;
+    sd.scopeMask = rr.scopeMask;
   }
 
   if(!hits)
index f063c578f32752e71e43921e8c183a1780a3d021..7f12c7b307dd4f51d5ba584cb273e328f4fe0097 100644 (file)
@@ -42,6 +42,9 @@
 #include "dnsrecords.hh"
 #include "dnssecinfra.hh" 
 #include "base64.hh"
+#include "ednssubnet.hh"
+
+bool DNSPacket::s_doEDNSSubnetProcessing;
 
 DNSPacket::DNSPacket() 
 {
@@ -49,6 +52,7 @@ DNSPacket::DNSPacket()
   d_compress=true;
   d_tcp=false;
   d_wantsnsid=false;
+  d_haveednssubnet = false;
   d_dnssecOk=false;
 }
 
@@ -85,6 +89,10 @@ DNSPacket::DNSPacket(const DNSPacket &orig)
   d_maxreplylen = orig.d_maxreplylen;
   d_ednsping = orig.d_ednsping;
   d_wantsnsid = orig.d_wantsnsid;
+  
+  d_eso = orig.d_eso;
+  d_haveednssubnet = orig.d_haveednssubnet;
+  
   d_dnssecOk = orig.d_dnssecOk;
   d_rrs=orig.d_rrs;
   
@@ -259,10 +267,13 @@ void DNSPacket::wrapup()
   if(!d_ednsping.empty()) {
     opts.push_back(make_pair(4, d_ednsping));
   }
-
-  if(!d_rrs.empty() || !opts.empty()) {
+  
+  
+  if(!d_rrs.empty() || !opts.empty() || d_haveednssubnet) {
     try {
+      uint8_t maxScopeMask=0;
       for(pos=d_rrs.begin(); pos < d_rrs.end(); ++pos) {
+        maxScopeMask = max(maxScopeMask, pos->scopeMask);
         // this needs to deal with the 'prio' mismatch:
         if(pos->qtype.getCode()==QType::MX || pos->qtype.getCode() == QType::SRV) {  
           pos->content = lexical_cast<string>(pos->priority) + " " + pos->content;
@@ -285,6 +296,16 @@ void DNSPacket::wrapup()
           goto noCommit;
         }
       }
+      
+      if(d_haveednssubnet) {
+        string makeEDNSSubnetOptsString(const EDNSSubnetOpts& eso);
+        EDNSSubnetOpts eso = d_eso;
+        eso.scope = Netmask(eso.source.getNetwork(), maxScopeMask);
+    
+        string opt = makeEDNSSubnetOptsString(eso);
+        opts.push_back(make_pair(6, opt));
+      }
+    
 
       if(!opts.empty() || d_dnssecOk)
         pw.addOpt(2800, 0, d_dnssecOk ? EDNSOpts::DNSSECOK : 0, opts);
@@ -342,6 +363,8 @@ DNSPacket *DNSPacket::replyPacket() const
   r->d_ednsping = d_ednsping;
   r->d_wantsnsid = d_wantsnsid;
   r->d_dnssecOk = d_dnssecOk;
+  r->d_eso = d_eso;
+  r->d_haveednssubnet = d_haveednssubnet;
   
   if(!d_tsigkeyname.empty()) {
     r->d_tsigkeyname = d_tsigkeyname;
@@ -442,6 +465,7 @@ try
   d_dnssecOk=false;
   d_ednsping.clear();
   d_havetsig = mdp.getTSIGPos();
+  d_haveednssubnet = false;
 
 
   if(getEDNSOpts(mdp, &edo)) {
@@ -459,8 +483,15 @@ try
       else if(iter->first == 5) {// 'EDNS PING'
         d_ednsping = iter->second;
       }
-      else
-        ; // cerr<<"Have an option #"<<iter->first<<endl;
+      else if(iter->first == 6) { // 'EDNS SUBNET'
+        if(s_doEDNSSubnetProcessing && getEDNSSubnetOptsFromString(iter->second, &d_eso)) {
+          //cerr<<"Parsed, source: "<<d_eso.source.toString()<<", scope: "<<d_eso.scope.toString()<<", family = "<<d_eso.scope.getNetwork().sin4.sin_family<<endl;
+          d_haveednssubnet=true;
+        } 
+      }
+      else {
+        // cerr<<"Have an option #"<<iter->first<<": "<<makeHexDump(iter->second)<<endl;
+      }
     }
   }
   else  {
@@ -503,6 +534,13 @@ void DNSPacket::setRemote(const ComboAddress *s)
   d_remote=*s;
 }
 
+Netmask DNSPacket::getRealRemote() const
+{
+  if(d_haveednssubnet)
+    return d_eso.source;
+  return Netmask(d_remote);
+}
+
 void DNSPacket::setSocket(Utility::sock_t sock)
 {
   d_socket=sock;
index bbfcb10633be543fb0f2f20f5f6811dcd1c55ec2..3c0f82c0235bc0bff9b93cc0196539bf0af5bbeb 100644 (file)
@@ -32,6 +32,7 @@
 #include <cstdlib>
 #include <sys/types.h>
 #include "iputils.hh"
+#include "ednssubnet.hh"
 
 #ifndef WIN32
 #include <sys/socket.h>
@@ -82,6 +83,7 @@ public:
   // address & socket manipulation
   void setRemote(const ComboAddress*);
   string getRemote() const;
+  Netmask getRealRemote() const;
   string getLocal() const
   {
     ComboAddress ca;
@@ -151,6 +153,7 @@ public:
   
   vector<DNSResourceRecord>& getRRS() { return d_rrs; }
   TSIGRecordContent d_trc;
+  static bool s_doEDNSSubnetProcessing;
 private:
   void pasteQ(const char *question, int length); //!< set the question of this packet, useful for crafting replies
 
@@ -164,7 +167,8 @@ private:
   int d_maxreplylen;
   string d_ednsping;
   bool d_wantsnsid;
-
+  bool d_haveednssubnet;
+  EDNSSubnetOpts d_eso;
   string d_tsigsecret;
   string d_tsigkeyname;
   string d_tsigprevious;
index abcfa40a989a3f078ec82eb1fb226064842fc517..08cd3d3029b40e321091df8681cd3c74f814e791 100644 (file)
@@ -37,19 +37,26 @@ bool getEDNSSubnetOptsFromString(const string& options, EDNSSubnetOpts* eso)
   EDNSSubnetOptsWire esow;
   memcpy(&esow, options.c_str(), sizeof(esow));
   esow.family = ntohs(esow.family);
-  cerr<<"Family: "<<esow.family<<endl;
+  //cerr<<"Family when parsing from string: "<<esow.family<<endl;
   ComboAddress address;
   if(esow.family == 1) {
     if(options.size() != 8)
       return false;
     address.sin4.sin_family = AF_INET;
     memcpy(&address.sin4.sin_addr.s_addr, options.c_str()+4, 4);
-    cerr<<"Source address: "<<address.toString()<<", mask: "<<(int)esow.sourceMask<<endl;
-    eso->source = Netmask(address, esow.sourceMask);
-    eso->scope = Netmask(address, esow.scopeMask);
-    return true;
+  } else if(esow.family == 2) {
+    if(options.size() != 4 + 16)
+      return false;
+    memset(&address, 0, sizeof(address));
+    address.sin4.sin_family = AF_INET6;
+    memcpy(&address.sin6.sin6_addr.s6_addr, options.c_str()+4, 16);
   }
-  return false;
+  else
+    return false;
+ // cerr<<"Source address: "<<address.toString()<<", mask: "<<(int)esow.sourceMask<<endl;
+  eso->source = Netmask(address, esow.sourceMask);
+  eso->scope = Netmask(address, esow.scopeMask);
+  return true;
 }
 
 string makeEDNSSubnetOptsString(const EDNSSubnetOpts& eso)
index b6ccbb56988ef82b8e5f3c44aa6a302a9daede2a..ce6e40e930f452df5363fb778078b40a980cbe14 100644 (file)
@@ -992,6 +992,7 @@ void PacketHandler::makeNXDomain(DNSPacket* p, DNSPacket* r, const std::string&
   rr.domain_id=sd.domain_id;
   rr.d_place=DNSResourceRecord::AUTHORITY;
   rr.auth = 1;
+  rr.scopeMask = sd.scopeMask;
   r->addRecord(rr);
   
   if(p->d_dnssecOk && d_dk.isSecuredZone(sd.qname))
index 7f48912a0516d4304390b90ead77be497716e6ba..8310fe433191b13a790c283b597c3889cdb0021a 100644 (file)
@@ -4,6 +4,7 @@
 #include "dnswriter.hh"
 #include "dnsrecords.hh"
 #include "statbag.hh"
+#include "ednssubnet.hh"
 StatBag S;
 
 int main(int argc, char** argv)
@@ -21,6 +22,18 @@ try
   DNSPacketWriter pw(packet, argv[3], DNSRecordContent::TypeToNumber(argv[4]));
 
   pw.getHeader()->rd=1;
+
+// void addOpt(int udpsize, int extRCode, int Z, const optvect_t& options=optvect_t());
+  DNSPacketWriter::optvect_t opts;
+  EDNSSubnetOpts eso;
+  eso.scope = eso.source = Netmask("2001:960:2:1e5::2");
+  
+  string subnet = makeEDNSSubnetOptsString(eso);
+  
+  opts.push_back(make_pair(6, subnet));
+  
+  pw.addOpt(1200, 0, 0, opts); // 1200 bytes answer size
+  pw.commit();
  
   Socket sock(InterNetwork, Datagram);
   ComboAddress dest(argv[1] + (*argv[1]=='@'), atoi(argv[2]));
@@ -38,6 +51,25 @@ try
     cout<<i->first.d_place-1<<"\t"<<i->first.d_label<<"\tIN\t"<<DNSRecordContent::NumberToType(i->first.d_type);
     cout<<"\t"<<i->first.d_ttl<<"\t"<< i->first.d_content->getZoneRepresentation()<<"\n";
   }
+  EDNSOpts edo;
+  if(getEDNSOpts(mdp, &edo)) {
+    cerr<<"Have "<<edo.d_options.size()<<" options!"<<endl;
+    for(vector<pair<uint16_t, string> >::const_iterator iter = edo.d_options.begin();
+        iter != edo.d_options.end(); 
+        ++iter) {
+        if(iter->first == 6) {
+          EDNSSubnetOpts eso;
+          if(getEDNSSubnetOptsFromString(iter->second, &eso)) {
+            cerr<<"Subnet options in reply: source "<<eso.source.toString()<<", scope "<<eso.scope.toString()<<endl;
+          }
+          else
+            cerr<<"EDNS Subnet failed to parse"<<endl;
+        }
+        else 
+          cerr<<"Have unknown option "<<(int)iter->first<<endl;
+      }
+  }
+