]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
forward EDNS Client Subnet option during ALIAS processing
authorroot <root@pdns.dev.a2hosting.com>
Thu, 19 May 2022 20:21:02 +0000 (16:21 -0400)
committerPeter van Dijk <peter.van.dijk@powerdns.com>
Thu, 11 Jan 2024 10:57:41 +0000 (11:57 +0100)
typo

pdns/Makefile.am
pdns/dnspacket.cc
pdns/dnspacket.hh
pdns/dnsproxy.cc
pdns/ednssubnet.cc
pdns/iputils.hh
pdns/stubresolver.cc
pdns/stubresolver.hh

index c429ce6831996dcc574a64740c194a62689a09c2..7c8a7c5dc4612bf8b06cf91b919143d7a25c6de6 100644 (file)
@@ -660,6 +660,8 @@ stubquery_SOURCES = \
        dnsparser.cc \
        dnsrecords.cc \
        dnswriter.cc \
+       ednsoptions.cc ednsoptions.hh \
+       ednssubnet.cc ednssubnet.hh \
        iputils.cc \
        logger.cc \
        misc.cc \
@@ -792,6 +794,8 @@ ixplore_SOURCES = \
        dnsrecords.cc \
        dnssecinfra.cc \
        dnswriter.cc dnswriter.hh \
+       ednsoptions.cc ednsoptions.hh \
+       ednssubnet.cc ednssubnet.hh \
        gss_context.cc gss_context.hh  \
        iputils.cc \
        ixfr.cc ixfr.hh \
index a9ec83529bb659cb8a22e38b165deb628c371ce9..a69d9f1edb2f55390b0c3feec24c34c70e74af13 100644 (file)
@@ -184,7 +184,6 @@ void DNSPacket::addRecord(DNSZoneRecord&& rr)
     }
     d_dedup.insert(hash);
   }
-
   d_rrs.push_back(std::move(rr));
 }
 
@@ -350,9 +349,8 @@ void DNSPacket::wrapup(bool throwsOnTruncation)
     try {
       uint8_t maxScopeMask=0;
       for(pos=d_rrs.begin(); pos < d_rrs.end(); ++pos) {
-        // cerr<<"during wrapup, content=["<<pos->content<<"]"<<endl;
         maxScopeMask = max(maxScopeMask, pos->scopeMask);
-        
+
         pw.startRecord(pos->dr.d_name, pos->dr.d_type, pos->dr.d_ttl, pos->dr.d_class, pos->dr.d_place);
         pos->dr.getContent()->toPacket(pw);
         if(pw.size() + optsize > (d_tcp ? 65535 : getMaxReplyLen())) {
@@ -374,6 +372,8 @@ void DNSPacket::wrapup(bool throwsOnTruncation)
       
       if(d_haveednssubnet) {
         EDNSSubnetOpts eso = d_eso;
+        // use the scopeMask from the resolver, if it is greater - issue #5469
+        maxScopeMask = max(maxScopeMask, eso.scope.getBits());
         eso.scope = Netmask(eso.source.getNetwork(), maxScopeMask);
     
         string opt = makeEDNSSubnetOptsString(eso);
@@ -598,9 +598,9 @@ try
     */
     d_ednsRawPacketSizeLimit=edo.d_packetsize;
     d_maxreplylen=std::min(std::max(static_cast<uint16_t>(512), edo.d_packetsize), s_udpTruncationThreshold);
-//    cerr<<edo.d_extFlags<<endl;
-    if(edo.d_extFlags & EDNSOpts::DNSSECOK)
+    if((edo.d_extFlags & EDNSOpts::DNSSECOK) != 0) {
       d_dnssecOk=true;
+    }
 
     for(const auto & option : edo.d_options) {
       if(option.first == EDNSOptionCode::NSID) {
index 582f5902cb0bb04c669610f13701a6a497f79318..60e3268a9ebf39d315472ec39e34512b654eadf2 100644 (file)
@@ -172,6 +172,7 @@ public:
   static bool s_doEDNSSubnetProcessing;
   static bool s_doEDNSCookieProcessing;
   static string s_EDNSCookieKey;
+  EDNSSubnetOpts d_eso;
 
 #ifdef ENABLE_GSS_TSIG
   void cleanupGSS(int rcode);
@@ -187,7 +188,6 @@ private:
   vector<DNSZoneRecord> d_rrs; // 8
   std::unordered_set<size_t> d_dedup;
   string d_rawpacket; // this is where everything lives 8
-  EDNSSubnetOpts d_eso;
   EDNSCookiesOpt d_eco;
 
   int d_maxreplylen{0};
index a25691c916cd11bff5a30c79d6b6843d32fffea4..3c5c07636cbab6dfbacbd44c76d2cd70276aa9ea 100644 (file)
@@ -37,6 +37,8 @@
 #include "stubresolver.hh"
 #include "arguments.hh"
 #include "threadname.hh"
+#include "ednsoptions.hh"
+#include "ednssubnet.hh"
 
 extern StatBag S;
 
@@ -91,14 +93,23 @@ void DNSProxy::go()
 //! look up qname target with r->qtype, plonk it in the answer section of 'r' with name aname
 bool DNSProxy::completePacket(std::unique_ptr<DNSPacket>& r, const DNSName& target,const DNSName& aname, const uint8_t scopeMask)
 {
+  string ECSOptionStr;
+
+  if (r->hasEDNSSubnet())
+  {
+    DLOG(g_log<<"dnsproxy::completePacket: Parsed edns source: "<<r->d_eso.source.toString()<<", scope: "<<r->d_eso.scope.toString()<<", family = "<<r->d_eso.scope.getNetwork().sin4.sin_family<<endl);
+    ECSOptionStr = makeEDNSSubnetOptsString(r->d_eso);
+    DLOG(g_log<<"from dnsproxy::completePacket: Creating ECS option string "<<makeHexDump(ECSOptionStr)<<endl);
+  }
+
   if(r->d_tcp) {
     vector<DNSZoneRecord> ips;
     int ret1 = 0, ret2 = 0;
-
+    // rip out edns info here, pass it to the stubDoResolve
     if(r->qtype == QType::A || r->qtype == QType::ANY)
-      ret1 = stubDoResolve(target, QType::A, ips);
+      ret1 = stubDoResolve(target, QType::A, ips, r->hasEDNSSubnet() ? &r->d_eso : nullptr);
     if(r->qtype == QType::AAAA || r->qtype == QType::ANY)
-      ret2 = stubDoResolve(target, QType::AAAA, ips);
+      ret2 = stubDoResolve(target, QType::AAAA, ips, r->hasEDNSSubnet() ? &r->d_eso : nullptr);
 
     if(ret1 != RCode::NoError || ret2 != RCode::NoError) {
       g_log<<Logger::Error<<"Error resolving for "<<aname<<" ALIAS "<<target<<" over UDP, original query came in over TCP";
@@ -151,6 +162,14 @@ bool DNSProxy::completePacket(std::unique_ptr<DNSPacket>& r, const DNSName& targ
   DNSPacketWriter pw(packet, target, qtype);
   pw.getHeader()->rd=true;
   pw.getHeader()->id=id ^ d_xor;
+  // Add EDNS Subnet if the client sent one - issue #5469
+  if (!ECSOptionStr.empty()) {
+    DLOG(g_log<<"from dnsproxy::completePacket: adding ECS option string to packet options "<<makeHexDump(ECSOptionStr)<<endl);
+    DNSPacketWriter::optvect_t opts;
+    opts.emplace_back(EDNSOptionCode::ECS, ECSOptionStr);
+    pw.addOpt(512, 0, 0, opts);
+    pw.commit();
+  }
 
   if(send(d_sock,&packet[0], packet.size() , 0)<0) { // zoom
     g_log<<Logger::Error<<"Unable to send a packet to our recursing backend: "<<stringerror()<<endl;
@@ -236,9 +255,8 @@ void DNSProxy::mainloop()
         d.id=i->second.id;
         memcpy(buffer,&d,sizeof(d));  // commit spoofed id
 
-        DNSPacket p(false),q(false);
+        DNSPacket p(false);
         p.parse(buffer,(size_t)len);
-        q.parse(buffer,(size_t)len);
 
         if(p.qtype.getCode() != i->second.qtype || p.qdomain != i->second.qname) {
           g_log<<Logger::Error<<"Discarding packet from recursor backend with id "<<(d.id^d_xor)<<
@@ -250,10 +268,14 @@ void DNSProxy::mainloop()
         memset(&msgh, 0, sizeof(struct msghdr));
         string reply; // needs to be alive at time of sendmsg!
         MOADNSParser mdp(false, p.getString());
-        //       cerr<<"Got completion, "<<mdp.d_answers.size()<<" answers, rcode: "<<mdp.d_header.rcode<<endl;
+        if (p.d_eso.scope.isValid()){
+          // update the EDNS options with info from the resolver - issue #5469
+          i->second.complete->d_eso = p.d_eso;
+          DLOG(g_log<<"from dnsproxy::mainLoop: updated EDNS options from resolver EDNS source: "<<i->second.complete->d_eso.source.toString()<<" EDNS scope: "<<i->second.complete->d_eso.scope.toString()<<endl);
+        }
+
         if (mdp.d_header.rcode == RCode::NoError) {
-          for (auto& answer : mdp.d_answers) {
-            //     cerr<<"comp: "<<(int)j->first.d_place-1<<" "<<j->first.d_label<<" " << DNSRecordContent::NumberToType(j->first.d_type)<<" "<<j->first.d_content->getZoneRepresentation()<<endl;
+          for(const auto & answer : mdp.d_answers) {        
             if(answer.first.d_place == DNSResourceRecord::ANSWER || (answer.first.d_place == DNSResourceRecord::AUTHORITY && answer.first.d_type == QType::SOA)) {
 
               if(answer.first.d_type == i->second.qtype || (i->second.qtype == QType::ANY && (answer.first.d_type == QType::A || answer.first.d_type == QType::AAAA))) {
@@ -267,6 +289,7 @@ void DNSProxy::mainloop()
               }
             }
           }
+
           i->second.complete->setRcode(mdp.d_header.rcode);
         } else {
           g_log<<Logger::Error<<"Error resolving for "<<i->second.aname<<" ALIAS "<<i->second.qname<<" over UDP, "<<QType(i->second.qtype).toString()<<"-record query returned "<<RCode::to_s(mdp.d_header.rcode)<<", returning SERVFAIL"<<endl;
index cf78ecf8a7e618394b13cacfeca473b0cf2bea59..a85fe02690924f7e5bd8a40e391691f6b5ea0d34 100644 (file)
@@ -74,7 +74,6 @@ bool getEDNSSubnetOptsFromString(const char* options, unsigned int len, EDNSSubn
   }
   else
     return false;
-  //cerr<<"Source address: "<<address.toString()<<", mask: "<<(int)esow.sourceMask<<endl;
   eso->source = Netmask(address, esow.sourceMask);
   /* 'address' has more bits set (potentially) than scopeMask. This leads to odd looking netmasks that promise
      more precision than they have. For this reason we truncate the address to scopeMask bits */
index ade4c82a301888e0756960b13c63b20e4a73c65f..5810bf1f0ab527f954c7ec52ae357a8c5d2205b9 100644 (file)
@@ -261,6 +261,16 @@ union ComboAddress {
     return ret;
   }
 
+  bool isValid() const
+  {
+    char host[1024];
+    int retval = 0;
+    if(sin4.sin_family && !(retval = getnameinfo(reinterpret_cast<const struct sockaddr*>(this), getSocklen(), host, sizeof(host),0, 0, NI_NUMERICHOST)))
+      return true;
+    else
+      return false;
+  }
+
   string toString() const
   {
     char host[1024];
@@ -595,6 +605,11 @@ public:
     return (ip & d_mask) == (ntohl(d_network.sin4.sin_addr.s_addr));
   }
 
+  bool isValid()
+  {
+    return d_network.isValid();
+  }
+  
   string toString() const
   {
     return d_network.toStringNoInterface()+"/"+std::to_string((unsigned int)d_bits);
index af5f2ea096327286c580d3a6d123182b29bde4d0..fc45b599f2c6c81e4e1f7f87c0e915fce29fe933 100644 (file)
@@ -15,6 +15,8 @@
 #include "namespaces.hh"
 #include "statbag.hh"
 #include "stubresolver.hh"
+#include "ednsoptions.hh"
+#include "ednssubnet.hh"
 
 #define LOCAL_RESOLV_CONF_PATH "/etc/resolv.conf"
 // don't stat() for local resolv.conf more than once every INTERVAL secs.
@@ -103,7 +105,7 @@ void stubParseResolveConf()
 }
 
 // s_resolversForStub contains the ComboAddresses that are used to resolve the
-int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& ret)
+int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& ret, EDNSSubnetOpts* d_eso)
 {
   // ensure resolver gets always configured
   if (!s_stubResolvConfigured) {
@@ -122,6 +124,16 @@ int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& r
   DNSPacketWriter pw(packet, qname, qtype);
   pw.getHeader()->id=dns_random_uint16();
   pw.getHeader()->rd=1;
+  
+  if(d_eso != nullptr)
+  {
+    // pass along EDNS subnet from client if given - issue #5469
+    string origECSOptionStr = makeEDNSSubnetOptsString(*d_eso);
+    DNSPacketWriter::optvect_t opts;
+    opts.emplace_back(EDNSOptionCode::ECS, origECSOptionStr);
+    pw.addOpt(512, 0, 0, opts);
+    pw.commit();
+  }
 
   string queryNameType = qname.toString() + "|" + QType(qtype).toString();
   string msg ="Doing stub resolving for '" + queryNameType + "', using resolvers: ";
@@ -171,9 +183,9 @@ int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& r
   return RCode::ServFail;
 }
 
-int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSRecord>& ret) {
+int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSRecord>& ret, EDNSSubnetOpts* d_eso) {
   vector<DNSZoneRecord> ret2;
-  int res = stubDoResolve(qname, qtype, ret2);
+  int res = stubDoResolve(qname, qtype, ret2, d_eso);
   for (const auto &r : ret2) {
     ret.push_back(r.dr);
   }
index 1cb94c8d4246438cd3e786833605605800e1a775..88f79f4cdfa928c61507705eb7985c8d466dd78f 100644 (file)
@@ -22,8 +22,9 @@
 #pragma once
 #include "namespaces.hh"
 #include "dnsparser.hh"
+#include "ednssubnet.hh"
 
 void stubParseResolveConf();
 bool resolversDefined();
-int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& ret);
-int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSRecord>& ret);
+int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSZoneRecord>& ret, EDNSSubnetOpts* d_eso = nullptr);
+int stubDoResolve(const DNSName& qname, uint16_t qtype, vector<DNSRecord>& ret, EDNSSubnetOpts* d_eso = nullptr);