]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Merge pull request #4164 from pieterlexis/fail-on-lua-dns-script-missing
authorPeter van Dijk <peter.van.dijk@powerdns.com>
Thu, 21 Jul 2016 15:06:57 +0000 (17:06 +0200)
committerGitHub <noreply@github.com>
Thu, 21 Jul 2016 15:06:57 +0000 (17:06 +0200)
Fail on startup when lua-dns-script doesn't exist

50 files changed:
build-scripts/build-auth-rpm
configure.ac
docs/markdown/appendix/compiling-powerdns.md
docs/markdown/authoritative/settings.md
docs/markdown/changelog.raw.md
docs/markdown/recursor/dnssec.md
docs/markdown/recursor/settings.md
docs/markdown/recursor/stats.md
m4/pdns_enable_remotebackend_zeromq.m4
modules/luabackend/lua_functions.cc
modules/opendbxbackend/odbxprivate.cc
modules/pipebackend/pipebackend.cc
modules/remotebackend/remotebackend.cc
pdns/README-dnsdist.md
pdns/common_startup.cc
pdns/dnsdist-console.cc
pdns/dnsdist-lua.cc
pdns/dnspacket.cc
pdns/dnspacket.hh
pdns/dnsreplay.cc
pdns/dnsrulactions.hh
pdns/ixfr.cc
pdns/ixfr.hh
pdns/logger.cc
pdns/logger.hh
pdns/lua-auth.cc
pdns/lwres.cc
pdns/packethandler.cc
pdns/pdns_recursor.cc
pdns/rec-lua-conf.cc
pdns/recursordist/Makefile.am
pdns/reczones.cc
pdns/resolver.cc
pdns/resolver.hh
pdns/rfc2136handler.cc
pdns/rpzloader.cc
pdns/rpzloader.hh
pdns/slavecommunicator.cc
pdns/syncres.cc
pdns/syncres.hh
pdns/tcpreceiver.cc
pdns/tcpreceiver.hh
pdns/validate-recursor.cc
pdns/validate.cc
pdns/ws-auth.cc
regression-tests.dnsdist/test_Advanced.py
regression-tests.recursor-dnssec/recursortests.py
regression-tests.recursor-dnssec/test_Flags.py
regression-tests.recursor-dnssec/test_Interop.py [moved from regression-tests.recursor-dnssec/test_EDNS_fallback.py with 76% similarity]
regression-tests.recursor-dnssec/test_Simple.py

index e3da467f49aa8a8646ef8396a8ff29bf8dcbaf9c..e6dc2785fa996171670b7e19c18f531c42c1aa48 100755 (executable)
@@ -639,6 +639,16 @@ BuildRequires: sqlite-devel
 %description backend-sqlite
 This package contains the SQLite backend for %{name}
 
+%package backend-tinydns
+Summary: TinyDNS backend for %{name}
+Group: System Environment/Daemons
+Requires: %{name}%{?_isa} = %{version}-%{release}
+BuildRequires: tinycdb-devel
+%global backends %{backends} tinydns
+
+%description backend-tinydns
+This package contains the TinyDNS backend for %{name}
+
 %prep
 %setup -q -n %{name}-${TARBALLVERSION}
 
@@ -791,6 +801,9 @@ exit 0
 %doc modules/gsqlite3backend/dnssec-3.x_to_3.4.0_schema.sqlite3.sql
 %doc modules/gsqlite3backend/nodnssec-3.x_to_3.4.0_schema.sqlite3.sql
 %{_libdir}/%{name}/libgsqlite3backend.so
+
+%files backend-tinydns
+%{_libdir}/%{name}/libtinydnsbackend.so
 EOF
       ;;
   SLES\ 12*)
index 9f7039e9ef36d0a61637d1450197d616910575fe..7cc72107f56e72f10417ea4edbe6872c68352adb 100644 (file)
@@ -349,7 +349,7 @@ AC_MSG_NOTICE([----------------])
 AC_MSG_NOTICE([Built-in modules: $modules])
 AC_MSG_NOTICE([Dynamic modules: $dynmodules])
 AC_MSG_NOTICE([])
-AS_IF([test "x$openssl_ecdsa" == "xyes"],
+AS_IF([test "x$openssl_ecdsa" = "xyes"],
   [AC_MSG_NOTICE([OpenSSL ecdsa: yes])],
   [AC_MSG_NOTICE([OpenSSL ecdsa: no])]
 )
@@ -363,7 +363,7 @@ AS_IF([test "x$LUAPC" != "x"],
     [AC_MSG_NOTICE([LuaJit: $LUAJITPC])],
     [AC_MSG_NOTICE([Lua/LuaJit: no])])
 ])
-AS_IF([test "x$enable_experimental_gss_tsig" == "xyes"],
+AS_IF([test "x$enable_experimental_gss_tsig" = "xyes"],
   [AC_MSG_NOTICE([GSS-TSIG: yes])]
 )
 AS_IF([test "x$systemd" != "xn"],
index 2bdb0f6a212a22fa00581a278b0140f1e4814cee..44c9d646c2a06a797da82263129c76fe5d691c3b 100644 (file)
@@ -29,9 +29,10 @@ You can also download snapshot tarballs generated by Jenkins and can be found
 
 You can also download releases on the [website](https://downloads.powerdns.com/releases/).
 These releases are PGP-signed with key-id [FBAE 0323 821C 7706 A5CA 151B DCF5
-13FA 7EED 19F3](https://pgp.mit.edu/pks/lookup?op=get&search=0xDCF513FA7EED19F3)
-or [1628 90D0 689D D12D D33E 4696 1C5E
-E990 D2E7 1575](https://pgp.mit.edu/pks/lookup?op=get&search=0x1C5EE990D2E71575).
+13FA 7EED 19F3](https://pgp.mit.edu/pks/lookup?op=get&search=0xDCF513FA7EED19F3),
+[1628 90D0 689D D12D D33E 4696 1C5E
+E990 D2E7 1575](https://pgp.mit.edu/pks/lookup?op=get&search=0x1C5EE990D2E71575)
+or [B76C D467 1C09 68BA A87D  E61C 5E50 715B F2FF E1A7](https://pgp.mit.edu/pks/lookup?op=get&search=0x5E50715BF2FFE1A7).
 
 ## OS specific gotcha's
 ### AIX
index 7654e444917604d63384a8b3b27838b72a2799ea..6d9ccbeec37ee14785091730ffc488d6a8b0b98b 100644 (file)
@@ -844,3 +844,10 @@ If the webserver should print arguments. See ["Performance Monitoring"](../commo
 * Default: yes
 
 If a PID file should be written. Available since 4.0.
+
+## `xfr-max-received-mbytes`
+* Integer
+* Default: 100
+
+Specifies the maximum number of received megabytes allowed on an incoming AXFR/IXFR update, to prevent
+resource exhaustion. A value of 0 means no restriction.
index 4678451793f94f1867f873fa9b880e192e22127c..53d7d8f3d1a898e3e1945c012acc89e4c6b4948b 100644 (file)
@@ -1,5 +1,22 @@
 **Note**: Beyond PowerDNS 2.9.20, the Authoritative Server and Recursor are released separately.
 
+# PowerDNS Recursor 4.0.1
+UNRELEASED
+
+This release improves interoperability with DNSSEC clients that expect an AD-bit on validated data when they query with only the DO-bit set.
+
+## Bug fixes
+
+ - [#4162](https://github.com/PowerDNS/pdns/pull/4162) Don't validate zones from the local auth store, go one level down while validating when there is a CNAME
+ - [#4187](https://github.com/PowerDNS/pdns/pull/4187):
+   * Don't go bogus on islands of security
+   * Check all possible chains for Insecures
+   * Don't go Bogus on a CNAME at the apex
+
+## Improvements
+
+ - [#4160](https://github.com/PowerDNS/pdns/pull/4160) Also validate on +DO
+
 # PowerDNS Recursor 4.0.0
 Released July 11th 2016
 
index 96664cfd165c38244cec85d5948089a745130431..9f604c405c6efc10a110a2041ab47ff4f6eed322 100644 (file)
@@ -22,12 +22,14 @@ requested by the client.
 
 ## `process`
 When `dnssec` is set to `process` the behaviour is similar to [`process-no-validate`](#process-no-validate).
-However, when the query has the AD-bit set, the recursor will try to validate the
-data and set the AD-bit in the response when the data is validated and send a
-SERVFAIL on a bogus answer.
+However, the recursor will try to validate the data if at least one of the DO or AD bits is set in the query; in that case, it will set the AD-bit in the response when the data is validated successfully, or send SERVFAIL when the validation comes up bogus.
+
+**Note:** in 4.0.0, only the AD-bit was considered when determining whether to validate.
+This lead to interoperability issues with older client software.
+From 4.0.1-onward, the DO-bit is also taken into account when determining whether to validate.
 
 ## `log-fail`
-In this mode , the recursor will attempt to validate all data it retrieves from
+In this mode, the recursor will attempt to validate all data it retrieves from
 authoritative servers, regardless of the client's DNSSEC desires, and will log the
 validation result. This mode can be used to determine the extra load and amount
 of possibly bogus answers before turning on full-blown validation. Responses to
@@ -44,9 +46,9 @@ with regards to the `dnssec` mode.
 
 |    | `off` | `process-no-validate` | `process` | `log-fail` | `validate` |
 |:------------|:-------|:-------------|:-------------|:-------------|:-------------|
-|Perform validation| No | No | Only on +AD from client | Always (logs result) | Always |
-|SERVFAIL on bogus| No | No | Only on +AD from client | Only on +AD from client | Always |
-|AD in response on authenticated data| Never | Never | Only on +AD from client | Only on +AD from client | Only on +AD from client |
+|Perform validation| No | No | Only on +AD or +DO from client | Always (logs result) | Always |
+|SERVFAIL on bogus| No | No | Only on +AD or +DO from client | Only on +AD or +DO from client | Always |
+|AD in response on authenticated data| Never | Never | Only on +AD or +DO from client | Only on +AD or +DO from client | Only on +AD or +DO from client |
 |RRSIGs/NSECs in answer on +DO from client| No | Yes | Yes | Yes | Yes |
 
 **Note**: the `dig` tool sets the AD-bit in the query. This might lead to unexpected
index 0671d0113ab66780db24c1a87924afc6a8d7fce1..8f3c40bf9b1e8cef587672f49466ce2fb256c2cb 100644 (file)
@@ -194,7 +194,7 @@ outgoing queries. Don't do any validation.
 ### `process`
 Respond with DNSSEC records to clients that ask for it, set the DO bit on all
 outgoing queries. Do validation for clients that request it (by means of the AD-
-bit in the query).
+bit or DO-bit in the query).
 
 ### `log-fail`
 Similar behaviour to `process`, but validate RRSIGs on responses and log bogus
@@ -479,6 +479,8 @@ In addition to those, `rpzMaster` accepts:
 * tsigalgo = the name of the TSIG algorithm (like 'hmac-md5') used
 * tsigsecret = base64 encoded TSIG secret
 * refresh = an integer describing the interval between checks for updates. By default, the RPZ zone's default is used
+* maxReceivedMBytes = the maximum size in megabytes of an AXFR/IXFR update, to prevent resource exhaustion.
+The default value of 0 means no restriction.
 
 If no settings are included, the RPZ is taken literally with no overrides applied.
 
index dae1a9dc3001eef32817d8c68261dcecd3e54a42..3413cac384865a794fdf691cf4064470ee0ea7b4 100644 (file)
@@ -92,7 +92,7 @@ answers-slow + packetcache-hits + over-capacity-drops + policy-drops = questions
 Also note that unauthorized-tcp and unauthorized-udp packets do not end up in
 the 'questions' count.
 
-Every half our or so, the recursor outputs a line with statistics. More
+Every half hour or so, the recursor outputs a line with statistics. More
 infrastructure is planned so as to allow for Cricket or MRTG graphs. To force
 the output of statistics, send the process a SIGUSR1. A line of statistics looks
 like this:
@@ -112,4 +112,5 @@ answer a question. Initially this ratio may be well over 100% as additional
 queries may be needed to actually recurse the DNS and figure out the addresses
 of nameservers.
 
-Finally, 12% of queries were not performed because identical queries had gone out previously, saving load servers worldwide.
+Finally, 12% of queries were not performed because identical queries had gone out
+previously, saving load on servers worldwide.
index ef9f19c59d52ac594a0aa5016f943aaaed3110d3..b20d1b26801b80c4c54b7aa8a6749f768bebcc0d 100644 (file)
@@ -15,7 +15,7 @@ AC_DEFUN([PDNS_ENABLE_REMOTEBACKEND_ZEROMQ],[
 
   AS_IF([test "x$enable_remotebackend_zeromq" != "xno"],
     [
-      AS_IF([test "x$have_remotebackend" == "xyes"],
+      AS_IF([test "x$have_remotebackend" = "xyes"],
         [
           PKG_CHECK_MODULES([LIBZMQ], [libzmq],
             [
index bb0b19a520da4391bd4666979caa25d98f699884..daea36937c2ad848e9dd25903fc6faf6ca1cd253 100644 (file)
@@ -124,9 +124,9 @@ int l_dnspacket (lua_State *lua) {
        return 1;
     }
 
-    lua_pushstring(lua, lb->dnspacket->getRemote().c_str());
+    lua_pushstring(lua, lb->dnspacket->getRemote().toString().c_str());
     lua_pushinteger(lua, lb->dnspacket->getRemotePort());
-    lua_pushstring(lua, lb->dnspacket->getLocal().c_str());
+    lua_pushstring(lua, lb->dnspacket->getLocal().toString().c_str());
     lua_pushstring(lua, lb->dnspacket->getRealRemote().toString().c_str());
 
     return 4;
index 40961400ab638c6290bc051e77ed29bd7988fcb7..2413d84af95dc5544c06c617c2cdb4a0e2d807eb 100644 (file)
@@ -25,7 +25,7 @@ bool OdbxBackend::connectTo( const vector<string>& hosts, QueryType type )
 
         if( type == WRITE && getArg( "backend" ) == "sqlite" )
         {
-               L.log( m_myname + " Using same SQLite connection for reading and writeing to '" + hosts[odbx_host_index[READ]] + "'", Logger::Notice );
+               L.log( m_myname + " Using same SQLite connection for reading and writing to '" + hosts[odbx_host_index[READ]] + "'", Logger::Notice );
                m_handle[WRITE] = m_handle[READ];
                return true;
         }
index fbbf5bccd6df9259e02d081e20996949202c4c2e..1154bba77a13e7e8f07b2bf0e3dc9e0714d3328d 100644 (file)
@@ -155,9 +155,9 @@ void PipeBackend::lookup(const QType& qtype,const DNSName& qname, DNSPacket *pkt
       string remoteIP="0.0.0.0";
       Netmask realRemote("0.0.0.0/0");
       if (pkt_p) {
-        localIP=pkt_p->getLocal();
+        localIP=pkt_p->getLocal().toString();
         realRemote = pkt_p->getRealRemote();
-        remoteIP = pkt_p->getRemote();
+        remoteIP = pkt_p->getRemote().toString();
       }
       // abi-version = 1
       // type    qname           qclass  qtype   id      remote-ip-address
index 5261120db12c5bf8ace3d1429486b1d3e30bab43..95c40df79ac274c2d596f26b0f832b1493d83324 100644 (file)
@@ -157,9 +157,9 @@ void RemoteBackend::lookup(const QType &qtype, const DNSName& qdomain, DNSPacket
    string realRemote="0.0.0.0/0";
 
    if (pkt_p) {
-     localIP=pkt_p->getLocal();
+     localIP=pkt_p->getLocal().toString();
      realRemote = pkt_p->getRealRemote().toString();
-     remoteIP = pkt_p->getRemote();
+     remoteIP = pkt_p->getRemote().toString();
    }
 
    Json query = Json::object{
index b4076558c4942f40e3731e2a25df2df97b7867b8..70173b9752f133012f87e459deac34d6a393a09a 100644 (file)
@@ -338,6 +338,8 @@ Rules have selectors and actions. Current selectors are:
  * Number of entries in a given section (RecordsCountRule)
  * Number of entries of a specific type in a given section (RecordsTypeCountRule)
  * Presence of trailing data (TrailingDataRule)
+ * Number of labels in the qname (QNameLabelsCountRule)
+ * Wire length of the qname (QNameWireLengthRule)
 
 Special rules are:
 
@@ -392,6 +394,8 @@ A DNS rule can be:
  * an OpcodeRule
  * an OrRule
  * a QClassRule
+ * a QNameLabelsCountRule
+ * a QNameWireLengthRule
  * a QTypeRule
  * a RegexRule
  * a RE2Rule
@@ -1220,6 +1224,8 @@ instantiate a server with additional parameters
     * `OrRule()`: matches if at least one of the sub-rules matches
     * `OpcodeRule()`: matches queries with the specified opcode
     * `QClassRule(qclass)`: matches queries with the specified qclass (numeric)
+    * `QNameLabelsCountRule(min, max)`: matches if the qname has less than `min` or more than `max` labels
+    * `QNameWireLengthRule(min, max)`: matches if the qname's length on the wire is less than `min` or more than `max` bytes
     * `QTypeRule(qtype)`: matches queries with the specified qtype
     * `RegexRule(regex)`: matches the query name against the supplied regex
     * `RecordsCountRule(section, minCount, maxCount)`: matches if there is at least `minCount` and at most `maxCount` records in the `section` section
@@ -1340,9 +1346,11 @@ instantiate a server with additional parameters
         * `toStringWithPort()`: alias for `tostringWithPort()`
     * DNSName related:
         * `newDNSName(name)`: make a DNSName based on this .-terminated name
+        * member `countLabels()`: return the number of labels
         * member `isPartOf(dnsname)`: is this dnsname part of that dnsname
         * member `tostring()`: return as a human friendly . terminated string
         * member `toString()`: alias for `tostring()`
+        * member `wirelength()`: return the length on the wire
     * DNSQuestion related:
         * member `dh`: DNSHeader
         * member `len`: the question length
index c783d9a7479f3b1c927608ed53303dd8cb1e7a7c..e6377a5d68cf0cca56c75067be943e70da54115a 100644 (file)
@@ -187,6 +187,8 @@ void declareArguments()
 
   ::arg().setSwitch("outgoing-axfr-expand-alias", "Expand ALIAS records during outgoing AXFR")="no";
   ::arg().setSwitch("8bit-dns", "Allow 8bit dns queries")="no";
+
+  ::arg().set("xfr-max-received-mbytes", "Maximum number of megabytes received from an incoming XFR")="100";
 }
 
 static time_t s_start=time(0);
@@ -389,9 +391,9 @@ void *qthread(void *number)
     if(logDNSQueries) {
       string remote;
       if(P->hasEDNSSubnet()) 
-        remote = P->getRemote() + "<-" + P->getRealRemote().toString();
+        remote = P->getRemote().toString() + "<-" + P->getRealRemote().toString();
       else
-        remote = P->getRemote();
+        remote = P->getRemote().toString();
       L << Logger::Notice<<"Remote "<< remote <<" wants '" << P->qdomain<<"|"<<P->qtype.getName() << 
             "', do = " <<P->d_dnssecOk <<", bufsize = "<< P->getMaxReplyLen()<<": ";
     }
index c909efc09af84f24c35846c5a0462e2693f195be..1b37daac165ea372ae706177f3aad6b4a1640f5d 100644 (file)
@@ -248,7 +248,7 @@ char* my_generator(const char* text, int state)
       "PoolAction(", "printDNSCryptProviderFingerprint(",
       "RegexRule(", "RemoteLogAction(", "RemoteLogResponseAction(", "rmResponseRule(",
       "rmRule(", "rmServer(", "roundrobin",
-      "QTypeRule(",
+      "QNameLabelsCountRule(", "QNameWireLengthRule(", "QTypeRule(",
       "setACL(", "setDNSSECPool(", "setECSOverride(",
       "setECSSourcePrefixV4(", "setECSSourcePrefixV6(", "setKey(", "setLocal(",
       "setMaxTCPClientThreads(", "setMaxTCPQueuedConnections(", "setMaxUDPOutstanding(", "setRules(",
index ebd6e41889d6afbadc2d94e9aa07e9041b82d65c..e55e73dcfe7f364f061918df6b5ab45349995efc 100644 (file)
@@ -879,6 +879,14 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
       return std::shared_ptr<DNSRule>(new TrailingDataRule());
     });
 
+  g_lua.writeFunction("QNameLabelsCountRule", [](unsigned int minLabelsCount, unsigned int maxLabelsCount) {
+      return std::shared_ptr<DNSRule>(new QNameLabelsCountRule(minLabelsCount, maxLabelsCount));
+    });
+
+  g_lua.writeFunction("QNameWireLengthRule", [](size_t min, size_t max) {
+      return std::shared_ptr<DNSRule>(new QNameWireLengthRule(min, max));
+    });
+
   g_lua.writeFunction("addAction", [](luadnsrule_t var, std::shared_ptr<DNSAction> ea)
                      {
                         setLuaSideEffect();
@@ -1066,6 +1074,8 @@ vector<std::function<void(void)>> setupLua(bool client, const std::string& confi
   g_lua.registerFunction("toStringWithPort", &ComboAddress::toStringWithPort);
   g_lua.registerFunction<uint16_t(ComboAddress::*)()>("getPort", [](const ComboAddress& ca) { return ntohs(ca.sin4.sin_port); } );
   g_lua.registerFunction("isPartOf", &DNSName::isPartOf);
+  g_lua.registerFunction("countLabels", &DNSName::countLabels);
+  g_lua.registerFunction("wirelength", &DNSName::wirelength);
   g_lua.registerFunction<string(DNSName::*)()>("tostring", [](const DNSName&dn ) { return dn.toString(); });
   g_lua.registerFunction<string(DNSName::*)()>("toString", [](const DNSName&dn ) { return dn.toString(); });
   g_lua.writeFunction("newDNSName", [](const std::string& name) { return DNSName(name); });
index df8d34070beb2e2faac62ee4e12d6e16d480fabb..b6f0b818af20b9417be66b69dfa2b4380133a4a4 100644 (file)
@@ -81,9 +81,9 @@ const string& DNSPacket::getString()
   return d_rawpacket;
 }
 
-string DNSPacket::getRemote() const
+ComboAddress DNSPacket::getRemote() const
 {
-  return d_remote.toString();
+  return d_remote;
 }
 
 uint16_t DNSPacket::getRemotePort() const
index 4f9a51a1a2c3ccb32ec68220432d06da8c7f8aec..bf5e785782f9db9e027c1004b3c335db5680347f 100644 (file)
@@ -73,14 +73,14 @@ public:
 
   // address & socket manipulation
   void setRemote(const ComboAddress*);
-  string getRemote() const;
+  ComboAddress getRemote() const;
   Netmask getRealRemote() const;
-  string getLocal() const
+  ComboAddress getLocal() const
   {
     ComboAddress ca;
     socklen_t len=sizeof(ca);
     getsockname(d_socket, (sockaddr*)&ca, &len);
-    return ca.toString();
+    return ca;
   }
   uint16_t getRemotePort() const;
 
index aead489a33c032013cff15e38b9feb6c33eee196..4e0d16f3776abda42e0d9345364513c06ce2b338 100644 (file)
@@ -601,7 +601,7 @@ bool sendPacketFromPR(PcapPacketReader& pr, const ComboAddress& remote, int stam
       //      dh->rd=1; // useful to replay traffic to auths to a recursor
       uint16_t dlen = pr.d_len;
 
-      addECSOption((char*)pr.d_payload, 1500, &dlen, pr.getSource(), stamp);
+      if (stamp >= 0) addECSOption((char*)pr.d_payload, 1500, &dlen, pr.getSource(), stamp);
       pr.d_len=dlen;
       s_socket->sendTo((const char*)pr.d_payload, dlen, remote);
       sent=true;
index 2d6f9d2abb32a090db425d58f1ec040536578196..9441bce9b17b003b02f5955ce69d42c45e0314b5 100644 (file)
@@ -488,6 +488,46 @@ public:
   }
 };
 
+class QNameLabelsCountRule : public DNSRule
+{
+public:
+  QNameLabelsCountRule(unsigned int minLabelsCount, unsigned int maxLabelsCount): d_min(minLabelsCount), d_max(maxLabelsCount)
+  {
+  }
+  bool matches(const DNSQuestion* dq) const override
+  {
+    unsigned int count = dq->qname->countLabels();
+    return count < d_min || count > d_max;
+  }
+  string toString() const override
+  {
+    return "labels count < " + std::to_string(d_min) + " || labels count > " + std::to_string(d_max);
+  }
+private:
+  unsigned int d_min;
+  unsigned int d_max;
+};
+
+class QNameWireLengthRule : public DNSRule
+{
+public:
+  QNameWireLengthRule(size_t min, size_t max): d_min(min), d_max(max)
+  {
+  }
+  bool matches(const DNSQuestion* dq) const override
+  {
+    size_t const wirelength = dq->qname->wirelength();
+    return wirelength < d_min || wirelength > d_max;
+  }
+  string toString() const override
+  {
+    return "wire length < " + std::to_string(d_min) + " || wire length > " + std::to_string(d_max);
+  }
+private:
+  size_t d_min;
+  size_t d_max;
+};
+
 class DropAction : public DNSAction
 {
 public:
index 06d9fe5a9f6129ccd177c57d9b333a3fb3739187..dc87f74fbee3ba0e9c97966dfd6637721b59b559 100644 (file)
@@ -7,7 +7,7 @@
 
 // Returns pairs of "remove & add" vectors. If you get an empty remove, it means you got an AXFR!
 vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAddress& master, const DNSName& zone, const DNSRecord& oursr, 
-                                                                   const TSIGTriplet& tt, const ComboAddress* laddr)
+                                                                   const TSIGTriplet& tt, const ComboAddress* laddr, size_t maxReceivedBytes)
 {
   vector<pair<vector<DNSRecord>, vector<DNSRecord> > >  ret;
   vector<uint8_t> packet;
@@ -55,6 +55,7 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
   // CURRENT MASTER SOA 
   shared_ptr<SOARecordContent> masterSOA;
   vector<DNSRecord> records;
+  size_t receivedBytes = 0;
   for(;;) {
     if(s.read((char*)&len, 2)!=2)
       break;
@@ -62,8 +63,13 @@ vector<pair<vector<DNSRecord>, vector<DNSRecord> > > getIXFRDeltas(const ComboAd
     //    cout<<"Got chunk of "<<len<<" bytes"<<endl;
     if(!len)
       break;
+
+    if (maxReceivedBytes > 0 && (maxReceivedBytes - receivedBytes) < (size_t) len)
+      throw std::runtime_error("Reached the maximum number of received bytes in an IXFR delta for zone '"+zone.toString()+"' from master '"+master.toStringWithPort());
+
     char reply[len]; 
     readn2(s.getHandle(), reply, len);
+    receivedBytes += len;
     MOADNSParser mdp(string(reply, len));
     if(mdp.d_header.rcode) 
       throw std::runtime_error("Got an error trying to IXFR zone '"+zone.toString()+"' from master '"+master.toStringWithPort()+"': "+RCode::to_s(mdp.d_header.rcode));
index 543f4454a2b91273c13f7904f46d1e2a81667dff..eef2252247f0ac5464171bef2bb5d5cc197aafa4 100644 (file)
@@ -4,4 +4,4 @@
 
 vector<pair<vector<DNSRecord>, vector<DNSRecord> > >   getIXFRDeltas(const ComboAddress& master, const DNSName& zone, 
                                                                      const DNSRecord& sr, const TSIGTriplet& tt=TSIGTriplet(),
-                                                                     const ComboAddress* laddr=0);
+                                                                     const ComboAddress* laddr=0, size_t maxReceivedBytes=0);
index ae4585f10c3ab0206f6ca5e014188d9d60cb3501..9c10eb5bac491d24b90d966dfee28b1bdde99e77 100644 (file)
@@ -235,3 +235,10 @@ Logger& Logger::operator<<(const DNSName &d)
 
   return *this;
 }
+
+Logger& Logger::operator<<(const ComboAddress &ca)
+{
+  *this<<ca.toString();
+  return *this;
+}
+
index 9d1d85d2cf6a823842fb9752c5427fde22e3a74b..0840a7bfe7e61a1c57817758a99cb344f70f48bf 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "namespaces.hh"
 #include "dnsname.hh"
+#include "iputils.hh"
 
 //! The Logger class can be used to log messages in various ways.
 class Logger
@@ -80,7 +81,7 @@ public:
   Logger& operator<<(unsigned long);   //!< log an unsigned int
   Logger& operator<<(unsigned long long);   //!< log an unsigned 64 bit int
   Logger& operator<<(const DNSName&); 
-
+  Logger& operator<<(const ComboAddress&); //!< log an address
   Logger& operator<<(Urgency);    //!< set the urgency, << style
 
   Logger& operator<<(std::ostream & (&)(std::ostream &)); //!< this is to recognise the endl, and to commit the log
index 58be56b1589a2c28ad8589503607b2f698df362f..193b742a007f048a259207b1a006d6d675679301 100644 (file)
@@ -193,13 +193,13 @@ static int ldp_addRecords(lua_State *L) {
 
 static int ldp_getRemote(lua_State *L) {
   DNSPacket *p=ldp_checkDNSPacket(L);
-  lua_pushstring(L, p->getRemote().c_str());
+  lua_pushstring(L, p->getRemote().toString().c_str());
   return 1;
 }
 
 static int ldp_getRemoteRaw(lua_State *L) {
   DNSPacket *p=ldp_checkDNSPacket(L);
-  const ComboAddress& ca=p->d_remote;
+  const ComboAddress& ca=p->getRemote();
   if(ca.sin4.sin_family == AF_INET) {
     lua_pushlstring(L, (const char*)&ca.sin4.sin_addr.s_addr, 4);
   }
index fa2ee83fdb337b083f8cbb8be8410bf90343a142..81172c4f6b5c7982fd2dd275909dd4cc6ce0fdef 100644 (file)
@@ -75,7 +75,7 @@ int asyncresolve(const ComboAddress& ip, const DNSName& domain, int type, bool d
    * an "upstream query". To stay true to "dnssec=off means 3.X behaviour", we
    * only set +CD on forwarded query in any mode other than dnssec=off.
    */
-  pw.getHeader()->cd=(sendRDQuery && ::arg()["dnssec"] != "off");
+  pw.getHeader()->cd=(sendRDQuery && g_dnssecmode != DNSSECMode::Off);
 
   string ping;
   bool weWantEDNSSubnet=false;
index fc0bd4ef43c1c9487f905cb30f73348883a386cb..e85421fb3b7ded1e7d6e17c64d2ea00d81e680e5 100644 (file)
@@ -763,8 +763,8 @@ int PacketHandler::trySuperMasterSynchronous(DNSPacket *p, const DNSName& tsigke
   try {
     Resolver resolver;
     uint32_t theirserial;
-    resolver.getSoaSerial(p->getRemote(),p->qdomain, &theirserial);    
-    resolver.resolve(p->getRemote(), p->qdomain, QType::NS, &nsset);
+    resolver.getSoaSerial(p->getRemote().toString(),p->qdomain, &theirserial);
+    resolver.resolve(p->getRemote().toString(), p->qdomain, QType::NS, &nsset);
   }
   catch(ResolverException &re) {
     L<<Logger::Error<<"Error resolving SOA or NS for "<<p->qdomain<<" at: "<< p->getRemote() <<": "<<re.reason<<endl;
@@ -791,7 +791,7 @@ int PacketHandler::trySuperMasterSynchronous(DNSPacket *p, const DNSName& tsigke
     return RCode::Refused;
   }
 
-  if(!B.superMasterBackend(p->getRemote(), p->qdomain, nsset, &nameserver, &account, &db)) {
+  if(!B.superMasterBackend(p->getRemote().toString(), p->qdomain, nsset, &nameserver, &account, &db)) {
     L<<Logger::Error<<"Unable to find backend willing to host "<<p->qdomain<<" for potential supermaster "<<p->getRemote()<<". Remote nameservers: "<<endl;
     for(const auto& rr: nsset) {
       if(rr.qtype.getCode()==QType::NS)
@@ -800,7 +800,7 @@ int PacketHandler::trySuperMasterSynchronous(DNSPacket *p, const DNSName& tsigke
     return RCode::Refused;
   }
   try {
-    db->createSlaveDomain(p->getRemote(), p->qdomain, nameserver, account);
+    db->createSlaveDomain(p->getRemote().toString(), p->qdomain, nameserver, account);
     if (tsigkeyname.empty() == false) {
       vector<string> meta;
       meta.push_back(tsigkeyname.toStringNoDot());
@@ -863,7 +863,7 @@ int PacketHandler::processNotify(DNSPacket *p)
     }
   }
 
-  if(::arg().contains("trusted-notification-proxy", p->getRemote())) {
+  if(::arg().contains("trusted-notification-proxy", p->getRemote().toString())) {
     L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from trusted-notification-proxy "<< p->getRemote()<<endl;
     if(di.masters.empty()) {
       L<<Logger::Error<<"However, "<<p->qdomain<<" does not have any masters defined"<<endl;
@@ -874,7 +874,7 @@ int PacketHandler::processNotify(DNSPacket *p)
     L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" but we are master, rejecting"<<endl;
     return RCode::Refused;
   }
-  else if(!db->isMaster(p->qdomain, p->getRemote())) {
+  else if(!db->isMaster(p->qdomain, p->getRemote().toString())) {
     L<<Logger::Error<<"Received NOTIFY for "<<p->qdomain<<" from "<<p->getRemote()<<" which is not a master"<<endl;
     return RCode::Refused;
   }
@@ -1387,7 +1387,7 @@ DNSPacket *PacketHandler::questionOrRecurse(DNSPacket *p, bool *shouldRecurse)
     }
 
 
-    DLOG(L<<"After first ANY query for '"<<target<<"', id="<<sd.domain_id<<": weDone="<<weDone<<", weHaveUnauth="<<weHaveUnauth<<", weRedirected="<<weRedirected<<", haveAlias='"<<(haveAlias.empty() ? "(none)" : haveAlias)<<"'"<<endl);
+    DLOG(L<<"After first ANY query for '"<<target<<"', id="<<sd.domain_id<<": weDone="<<weDone<<", weHaveUnauth="<<weHaveUnauth<<", weRedirected="<<weRedirected<<", haveAlias='"<<haveAlias<<"'"<<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);
       makeNOError(p, r, target, DNSName(), sd, 1);
index 2f2f4048292b492a526965392ee913f3d4f7551d..bdbee9abf71c36d4b424d8e7456dbd073924f10a 100644 (file)
@@ -709,6 +709,7 @@ void startDoResolve(void *p)
 
     bool tracedQuery=false; // we could consider letting Lua know about this too
     bool variableAnswer = false;
+    bool shouldNotValidate = false;
 
     int res;
     DNSFilterEngine::Policy dfepol;
@@ -787,10 +788,10 @@ void startDoResolve(void *p)
       break;
     }
 
-
     if(!t_pdl->get() || !(*t_pdl)->preresolve(dc->d_remote, dc->d_local, dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_tcp, ret, dc->d_ednsOpts.empty() ? 0 : &dc->d_ednsOpts, dc->d_tag, &appliedPolicy, &dc->d_policyTags, res, &variableAnswer)) {
       try {
         res = sr.beginResolve(dc->d_mdp.d_qname, QType(dc->d_mdp.d_qtype), dc->d_mdp.d_qclass, ret);
+        shouldNotValidate = sr.wasOutOfBand();
       }
       catch(ImmediateServFailException &e) {
         if(g_logCommonErrors)
@@ -888,52 +889,52 @@ void startDoResolve(void *p)
       pw.getHeader()->rcode=res;
 
       // Does the validation mode or query demand validation?
-      if(g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode==DNSSECMode::ValidateForLog || (dc->d_mdp.d_header.ad && g_dnssecmode==DNSSECMode::Process)) {
+      if(!shouldNotValidate && (g_dnssecmode == DNSSECMode::ValidateAll || g_dnssecmode==DNSSECMode::ValidateForLog || ((dc->d_mdp.d_header.ad || DNSSECOK) && g_dnssecmode==DNSSECMode::Process))) {
         try {
           if(sr.doLog()) {
-            L<<Logger::Warning<<"Starting validation of answer to "<<dc->d_mdp.d_qname<<" for "<<dc->d_remote.toStringWithPort()<<endl;
+            L<<Logger::Warning<<"Starting validation of answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<endl;
           }
           
           auto state=validateRecords(ret);
           if(state == Secure) {
             if(sr.doLog()) {
-              L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<" for "<<dc->d_remote.toStringWithPort()<<" validates correctly"<<endl;
+              L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<" validates correctly"<<endl;
             }
             
             // Is the query source interested in the value of the ad-bit?
-            if (dc->d_mdp.d_header.ad)
+            if (dc->d_mdp.d_header.ad || DNSSECOK)
               pw.getHeader()->ad=1;
           }
           else if(state == Insecure) {
             if(sr.doLog()) {
-              L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<" for "<<dc->d_remote.toStringWithPort()<<" validates as Insecure"<<endl;
+              L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<" validates as Insecure"<<endl;
             }
             
             pw.getHeader()->ad=0;
           }
           else if(state == Bogus) {
             if(g_dnssecLogBogus || sr.doLog() || g_dnssecmode == DNSSECMode::ValidateForLog) {
-              L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<" for "<<dc->d_remote.toStringWithPort()<<" validates as Bogus"<<endl;
+              L<<Logger::Warning<<"Answer to "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" for "<<dc->d_remote.toStringWithPort()<<" validates as Bogus"<<endl;
             }
             
             // Does the query or validation mode sending out a SERVFAIL on validation errors?
-            if(!pw.getHeader()->cd && (g_dnssecmode == DNSSECMode::ValidateAll || dc->d_mdp.d_header.ad)) {
+            if(!pw.getHeader()->cd && (g_dnssecmode == DNSSECMode::ValidateAll || dc->d_mdp.d_header.ad || DNSSECOK)) {
               if(sr.doLog()) {
-                L<<Logger::Warning<<"Sending out SERVFAIL for "<<dc->d_mdp.d_qname<<" because recursor or query demands it for Bogus results"<<endl;
+                L<<Logger::Warning<<"Sending out SERVFAIL for "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" because recursor or query demands it for Bogus results"<<endl;
               }
               
               pw.getHeader()->rcode=RCode::ServFail;
               goto sendit;
             } else {
               if(sr.doLog()) {
-                L<<Logger::Warning<<"Not sending out SERVFAIL for "<<dc->d_mdp.d_qname<<" Bogus validation since neither config nor query demands this"<<endl;
+                L<<Logger::Warning<<"Not sending out SERVFAIL for "<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<" Bogus validation since neither config nor query demands this"<<endl;
               }
             }
           }
         }
         catch(ImmediateServFailException &e) {
           if(g_logCommonErrors)
-            L<<Logger::Notice<<"Sending SERVFAIL to "<<dc->getRemote()<<" during validation of '"<<dc->d_mdp.d_qname<<"' because: "<<e.reason<<endl;
+            L<<Logger::Notice<<"Sending SERVFAIL to "<<dc->getRemote()<<" during validation of '"<<dc->d_mdp.d_qname<<"|"<<QType(dc->d_mdp.d_qtype).getName()<<"' because: "<<e.reason<<endl;
           pw.getHeader()->rcode=RCode::ServFail;
           goto sendit;
         }
@@ -952,8 +953,20 @@ void startDoResolve(void *p)
 
       bool needCommit = false;
       for(auto i=ret.cbegin(); i!=ret.cend(); ++i) {
-        if(!DNSSECOK && (i->d_type == QType::RRSIG || i->d_type==QType::NSEC || i->d_type==QType::NSEC3))
+        if( ! DNSSECOK &&
+            ( i->d_type == QType::NSEC3 ||
+              (
+                ( i->d_type == QType::RRSIG || i->d_type==QType::NSEC ) &&
+                (
+                  ( dc->d_mdp.d_qtype != i->d_type &&  dc->d_mdp.d_qtype != QType::ANY ) ||
+                  i->d_place != DNSResourceRecord::ANSWER
+                )
+              )
+            )
+          ) {
           continue;
+        }
+
        pw.startRecord(i->d_name, i->d_type, i->d_ttl, i->d_class, i->d_place);
        if(i->d_type != QType::OPT) // their TTL ain't real
          minTTL = min(minTTL, i->d_ttl);
index 0c1474ed588c81da2b82a3387f7a06604f551c33..e6c07b0e311919451b629c65cc408eae9d3eaf41 100644 (file)
@@ -130,6 +130,7 @@ void loadRecursorLuaConfig(const std::string& fname)
         TSIGTriplet tt;
         int refresh=0;
        std::string polName;
+       size_t maxReceivedXFRMBytes = 0;
        if(options) {
          auto& have = *options;
          if(have.count("policyName")) {
@@ -163,14 +164,17 @@ void loadRecursorLuaConfig(const std::string& fname)
           if(have.count("refresh")) {
             refresh = boost::get<int>(constGet(have,"refresh"));
           }
+          if(have.count("maxReceivedMBytes")) {
+            maxReceivedXFRMBytes = static_cast<size_t>(boost::get<int>(constGet(have,"maxReceivedMBytes")));
+          }
        }
        ComboAddress master(master_, 53);
        DNSName zone(zone_);
 
-       auto sr=loadRPZFromServer(master, zone, lci.dfe, polName, defpol, 0, tt);
+       auto sr=loadRPZFromServer(master, zone, lci.dfe, polName, defpol, 0, tt, maxReceivedXFRMBytes * 1024 * 1024);
         if(refresh)
           sr->d_st.refresh=refresh;
-       std::thread t(RPZIXFRTracker, master, zone, polName, tt, sr);
+       std::thread t(RPZIXFRTracker, master, zone, polName, tt, sr, maxReceivedXFRMBytes * 1024 * 1024);
        t.detach();
       }
       catch(std::exception& e) {
index 8ff56231465eb134177e0be1d03c4ec864d06ef7..ab5809c29c867e3d730993bd2d71296b8292eba0 100644 (file)
@@ -229,20 +229,21 @@ recursor.conf-dist: pdns_recursor
 MANPAGES=pdns_recursor.1 \
         rec_control.1
 
-dist_man_MANS=$(MANPAGES)
+if HAVE_PANDOC
+  dist_man_MANS=$(MANPAGES)
+endif
+if HAVE_MANPAGES
+  dist_man_MANS=$(MANPAGES)
+endif
 
 if HAVE_PANDOC
 $(MANPAGES): %: %.md
        $(AM_V_GEN)$(PANDOC) -s -t man $< -o $@
 else
-if HAVE_MANPAGES
-#nothing
-else
 $(MANPAGES):
        echo "You need pandoc to generate the manpages"
        exit 1
 endif
-endif
 
 if HAVE_SYSTEMD
 pdns-recursor.service: pdns-recursor.service.in
index 74df7ec8e161ceee296eb5597cd5cbafd7263599..3cd3c9e5b8250bd64c2a07894ad8ced126691123 100644 (file)
@@ -311,7 +311,7 @@ string reloadAuthAndForwards()
 }
 
 
-void RPZIXFRTracker(const ComboAddress& master, const DNSName& zone, const std::string& polName, const TSIGTriplet& tt, shared_ptr<SOARecordContent> oursr)
+void RPZIXFRTracker(const ComboAddress& master, const DNSName& zone, const std::string& polName, const TSIGTriplet& tt, shared_ptr<SOARecordContent> oursr, size_t maxReceivedBytes)
 {
   int refresh = oursr->d_st.refresh;
   for(;;) {
@@ -323,7 +323,7 @@ void RPZIXFRTracker(const ComboAddress& master, const DNSName& zone, const std::
     L<<Logger::Info<<"Getting IXFR deltas for "<<zone<<" from "<<master.toStringWithPort()<<", our serial: "<<getRR<SOARecordContent>(dr)->d_st.serial<<endl;
     vector<pair<vector<DNSRecord>, vector<DNSRecord> > > deltas;
     try {
-      deltas = getIXFRDeltas(master, zone, dr, tt);
+      deltas = getIXFRDeltas(master, zone, dr, tt, nullptr, maxReceivedBytes);
     } catch(std::runtime_error& e ){
       L<<Logger::Warning<<e.what()<<endl;
       continue;
index 0220e157366f2976ed3ba36276027fdaa53e3351..b50dc91a6fa03a544f6602fadd34033f56fdd468 100644 (file)
@@ -360,8 +360,9 @@ void Resolver::getSoaSerial(const string &ipport, const DNSName &domain, uint32_
 AXFRRetriever::AXFRRetriever(const ComboAddress& remote,
                              const DNSName& domain,
                              const TSIGTriplet& tt, 
-                             const ComboAddress* laddr)
-  : d_tt(tt), d_tsigPos(0), d_nonSignedMessages(0)
+                             const ComboAddress* laddr,
+                             size_t maxReceivedBytes)
+  : d_tt(tt), d_receivedBytes(0), d_maxReceivedBytes(maxReceivedBytes), d_tsigPos(0), d_nonSignedMessages(0)
 {
   ComboAddress local;
   if (laddr != NULL) {
@@ -445,8 +446,14 @@ int AXFRRetriever::getChunk(Resolver::res_t &res, vector<DNSRecord>* records) //
   int len=getLength();
   if(len<0)
     throw ResolverException("EOF trying to read axfr chunk from remote TCP client");
-  
-  timeoutReadn(len); 
+
+  if (d_maxReceivedBytes > 0 && (d_maxReceivedBytes - d_receivedBytes) < (size_t) len)
+    throw ResolverException("Reached the maximum number of received bytes during AXFR");
+
+  timeoutReadn(len);
+
+  d_receivedBytes += (uint16_t) len;
+
   MOADNSParser mdp(d_buf.get(), len);
 
   int err;
index 24e45454db998870ee1d79b21d9c49166f7e1155..03fb4fdcb8a41ffbbc2523a004a7ba7c5520437a 100644 (file)
@@ -86,8 +86,9 @@ class AXFRRetriever : public boost::noncopyable
     AXFRRetriever(const ComboAddress& remote,
                   const DNSName& zone,
                   const TSIGTriplet& tt = TSIGTriplet(),
-                  const ComboAddress* laddr = NULL);
-       ~AXFRRetriever();
+                  const ComboAddress* laddr = NULL,
+                  size_t maxReceivedBytes=0);
+    ~AXFRRetriever();
     int getChunk(Resolver::res_t &res, vector<DNSRecord>* records=0);  
   
   private:
@@ -104,6 +105,8 @@ class AXFRRetriever : public boost::noncopyable
     TSIGTriplet d_tt;
     string d_prevMac; // RFC2845 4.4
     string d_signData;
+    size_t d_receivedBytes;
+    size_t d_maxReceivedBytes;
     uint32_t d_tsigPos;
     uint d_nonSignedMessages; // RFC2845 4.4
     TSIGRecordContent d_trc;
index aca057da71d031fcd3fad5e4e86fece75a0511ea..e37357d353faaceaa0124c3680dd0d43c45424de 100644 (file)
@@ -670,7 +670,7 @@ int PacketHandler::processUpdate(DNSPacket *p) {
   if (! ::arg().mustDo("dnsupdate"))
     return RCode::Refused;
 
-  string msgPrefix="UPDATE (" + itoa(p->d.id) + ") from " + p->getRemote() + " for " + p->qdomain.toLogString() + ": ";
+  string msgPrefix="UPDATE (" + itoa(p->d.id) + ") from " + p->getRemote().toString() + " for " + p->qdomain.toLogString() + ": ";
   L<<Logger::Info<<msgPrefix<<"Processing started."<<endl;
 
   // Check permissions - IP based
index 90a2dace320e6bd7118ffe071425dd9df89f8a44..31c5e6cd3a3122aa3f56596f9125abd42d52e8df 100644 (file)
@@ -105,14 +105,14 @@ void RPZRecordToPolicy(const DNSRecord& dr, DNSFilterEngine& target, const std::
   }
 }
 
-shared_ptr<SOARecordContent> loadRPZFromServer(const ComboAddress& master, const DNSName& zone, DNSFilterEngine& target, const std::string& polName, boost::optional<DNSFilterEngine::Policy> defpol, int place,  const TSIGTriplet& tt)
+shared_ptr<SOARecordContent> loadRPZFromServer(const ComboAddress& master, const DNSName& zone, DNSFilterEngine& target, const std::string& polName, boost::optional<DNSFilterEngine::Policy> defpol, int place,  const TSIGTriplet& tt, size_t maxReceivedBytes)
 {
   L<<Logger::Warning<<"Loading RPZ zone '"<<zone<<"' from "<<master.toStringWithPort()<<endl;
   if(!tt.name.empty())
     L<<Logger::Warning<<"With TSIG key '"<<tt.name<<"' of algorithm '"<<tt.algo<<"'"<<endl;
 
   ComboAddress local= master.sin4.sin_family == AF_INET ? ComboAddress("0.0.0.0") : ComboAddress("::"); // should be configurable
-  AXFRRetriever axfr(master, zone, tt, &local);
+  AXFRRetriever axfr(master, zone, tt, &local, maxReceivedBytes);
   unsigned int nrecords=0;
   Resolver::res_t nop;
   vector<DNSRecord> chunk;
index c61993ae4f842d79aab2e6a4fca8e5fa0daeb028..031e82795f8bbdbe1020c750137c5e6f7ff7dbed 100644 (file)
@@ -4,6 +4,6 @@
 #include "dnsrecords.hh"
 
 int loadRPZFromFile(const std::string& fname, DNSFilterEngine& target, const std::string& policyName, boost::optional<DNSFilterEngine::Policy> defpol, int place);
-std::shared_ptr<SOARecordContent> loadRPZFromServer(const ComboAddress& master, const DNSName& zone, DNSFilterEngine& target, const std::string& policyName, boost::optional<DNSFilterEngine::Policy> defpol, int place, const TSIGTriplet& tt);
+std::shared_ptr<SOARecordContent> loadRPZFromServer(const ComboAddress& master, const DNSName& zone, DNSFilterEngine& target, const std::string& policyName, boost::optional<DNSFilterEngine::Policy> defpol, int place, const TSIGTriplet& tt, size_t maxReceivedBytes);
 void RPZRecordToPolicy(const DNSRecord& dr, DNSFilterEngine& target, const std::string& policyName, bool addOrRemove, boost::optional<DNSFilterEngine::Policy> defpol, int place);
-void RPZIXFRTracker(const ComboAddress& master, const DNSName& zone, const std::string& policyName, const TSIGTriplet &tt, shared_ptr<SOARecordContent> oursr);
+void RPZIXFRTracker(const ComboAddress& master, const DNSName& zone, const std::string& policyName, const TSIGTriplet &tt, shared_ptr<SOARecordContent> oursr, size_t maxReceivedBytes);
index 9562c21d48cb4c46e57163b2be80c245fddb5f8b..2828a7289ac2d397368499db3879e0682bafb169 100644 (file)
@@ -103,7 +103,7 @@ void CommunicatorClass::ixfrSuck(const DNSName &domain, const TSIGTriplet& tt, c
 
     DNSRecord dr;
     dr.d_content = std::make_shared<SOARecordContent>(DNSName("."), DNSName("."), st);
-    auto deltas = getIXFRDeltas(remote, domain, dr, tt, laddr.sin4.sin_family ? &laddr : 0);
+    auto deltas = getIXFRDeltas(remote, domain, dr, tt, laddr.sin4.sin_family ? &laddr : 0, ((size_t) ::arg().asNum("xfr-max-received-mbytes")) * 1024 * 1024);
     zs.numDeltas=deltas.size();
     //    cout<<"Got "<<deltas.size()<<" deltas from serial "<<di.serial<<", applying.."<<endl;
     
@@ -235,7 +235,7 @@ static bool processRecordForZS(const DNSName& domain, bool& firstNSEC3, DNSResou
 vector<DNSResourceRecord> doAxfr(const ComboAddress& raddr, const DNSName& domain, const TSIGTriplet& tt, const ComboAddress& laddr,  scoped_ptr<AuthLua>& pdl, ZoneStatus& zs)
 {
   vector<DNSResourceRecord> rrs;
-  AXFRRetriever retriever(raddr, domain, tt, (laddr.sin4.sin_family == 0) ? NULL : &laddr);
+  AXFRRetriever retriever(raddr, domain, tt, (laddr.sin4.sin_family == 0) ? NULL : &laddr, ((size_t) ::arg().asNum("xfr-max-received-mbytes")) * 1024 * 1024);
   Resolver::res_t recs;
   bool first=true;
   bool firstNSEC3{true};
index 224945b6d3c411dba654ed439b10944ff71a7a7d..aa6e64c8ab965d7ab1bd51962a3fe6be33c124c1 100644 (file)
@@ -125,6 +125,7 @@ int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qcl
 {
   s_queries++;
   d_wasVariable=false;
+  d_wasOutOfBand=false;
 
   if( (qtype.getCode() == QType::AXFR))
     return -1;
@@ -146,6 +147,7 @@ int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qcl
     else
       dr.d_content=shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(QType::A, 1, "127.0.0.1"));
     ret.push_back(dr);
+    d_wasOutOfBand=true;
     return 0;
   }
 
@@ -165,6 +167,7 @@ int SyncRes::beginResolve(const DNSName &qname, const QType &qtype, uint16_t qcl
       dr.d_content=shared_ptr<DNSRecordContent>(DNSRecordContent::mastermake(QType::TXT, 3, "\""+s_serverID+"\""));
 
     ret.push_back(dr);
+    d_wasOutOfBand=true;
     return 0;
   }
 
@@ -420,7 +423,7 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
         const vector<ComboAddress>& servers = iter->second.d_servers;
         if(servers.empty()) {
           ret.clear();
-          doOOBResolve(qname, qtype, ret, depth, res);
+          d_wasOutOfBand = doOOBResolve(qname, qtype, ret, depth, res);
           return res;
         }
         else {
@@ -440,7 +443,7 @@ int SyncRes::doResolve(const DNSName &qname, const QType &qtype, vector<DNSRecor
       }
     }
 
-    if(doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed
+    if(qtype != QType::DS && doCNAMECacheCheck(qname,qtype,ret,depth,res)) // will reroute us if needed
       return res;
 
     if(doCacheCheck(qname,qtype,ret,depth,res)) // we done
@@ -982,7 +985,7 @@ int SyncRes::doResolveAt(NsSet &nameservers, DNSName auth, bool flawedNSSet, con
       LWResult lwr;
       if(tns->empty() && nameservers[*tns].first.empty() ) {
         LOG(prefix<<qname<<": Domain is out-of-band"<<endl);
-        doOOBResolve(qname, qtype, lwr.d_records, depth, lwr.d_rcode);
+        d_wasOutOfBand = doOOBResolve(qname, qtype, lwr.d_records, depth, lwr.d_rcode);
         lwr.d_tcbit=false;
         lwr.d_aabit=true;
       }
index 3287bb7acf881fb2c09698b62cb6228bc5292294..182666a1859ef59b941d17f5e450a5754cf02b07 100644 (file)
@@ -312,6 +312,11 @@ public:
     return d_wasVariable;
   }
 
+  bool wasOutOfBand() const
+  {
+    return d_wasOutOfBand;
+  }
+
   int asyncresolveWrapper(const ComboAddress& ip, bool ednsMANDATORY, const DNSName& domain, int type, bool doTCP, bool sendRDQuery, struct timeval* now, boost::optional<Netmask>& srcmask, LWResult* res);
 
   static void doEDNSDumpAndClose(int fd);
@@ -340,6 +345,7 @@ public:
   bool d_doDNSSEC;
   
   bool d_wasVariable{false};
+  bool d_wasOutOfBand{false};
   
   typedef multi_index_container <
     NegCacheEntry,
index 416db2bc1c4f9b7385075c1d8e9f4f58747eedb6..eb9913fd84b94ebbdfd18327a315c1b200477026 100644 (file)
@@ -66,7 +66,6 @@ extern StatBag S;
 pthread_mutex_t TCPNameserver::s_plock = PTHREAD_MUTEX_INITIALIZER;
 Semaphore *TCPNameserver::d_connectionroom_sem;
 PacketHandler *TCPNameserver::s_P; 
-int TCPNameserver::s_timeout;
 NetmaskGroup TCPNameserver::d_ng;
 
 void TCPNameserver::go()
@@ -322,9 +321,9 @@ void *TCPNameserver::doConnection(void *data)
       if(logDNSQueries)  {
         string remote;
         if(packet->hasEDNSSubnet()) 
-          remote = packet->getRemote() + "<-" + packet->getRealRemote().toString();
+          remote = packet->getRemote().toString() + "<-" + packet->getRealRemote().toString();
         else
-          remote = packet->getRemote();
+          remote = packet->getRemote().toString();
         L << Logger::Notice<<"TCP Remote "<< remote <<" wants '" << packet->qdomain<<"|"<<packet->qtype.getName() << 
         "', do = " <<packet->d_dnssecOk <<", bufsize = "<< packet->getMaxReplyLen()<<": ";
       }
@@ -470,7 +469,7 @@ bool TCPNameserver::canDoAXFR(shared_ptr<DNSPacket> q)
           vector<string> nsips=fns.lookup(j, B);
           for(vector<string>::const_iterator k=nsips.begin();k!=nsips.end();++k) {
             // cerr<<"got "<<*k<<" from AUTO-NS"<<endl;
-            if(*k == q->getRemote())
+            if(*k == q->getRemote().toString())
             {
               // cerr<<"got AUTO-NS hit"<<endl;
               L<<Logger::Warning<<"AXFR of domain '"<<q->qdomain<<"' allowed: client IP "<<q->getRemote()<<" is in NSset"<<endl;
@@ -494,7 +493,7 @@ bool TCPNameserver::canDoAXFR(shared_ptr<DNSPacket> q)
 
   extern CommunicatorClass Communicator;
 
-  if(Communicator.justNotified(q->qdomain, q->getRemote())) { // we just notified this ip 
+  if(Communicator.justNotified(q->qdomain, q->getRemote().toString())) { // we just notified this ip
     L<<Logger::Warning<<"Approved AXFR of '"<<q->qdomain<<"' from recently notified slave "<<q->getRemote()<<endl;
     return true;
   }
@@ -1160,7 +1159,6 @@ TCPNameserver::TCPNameserver()
 //  sem_init(&d_connectionroom_sem,0,::arg().asNum("max-tcp-connections"));
   d_connectionroom_sem = new Semaphore( ::arg().asNum( "max-tcp-connections" ));
   d_tid=0;
-  s_timeout=10;
   vector<string>locals;
   stringtok(locals,::arg()["local-address"]," ,");
 
index a8d0303ed6be392e366b2fb029ee6cc5792e5419..bf407af5c6c25b37ed123292b060fff56ac98cf1 100644 (file)
@@ -66,7 +66,6 @@ private:
 
   vector<int>d_sockets;
   vector<struct pollfd> d_prfds;
-  static int s_timeout;
 };
 
 #endif /* PDNS_TCPRECEIVER_HH */
index add348c288c886a408c32d07f59dde8a0a365061..5819cfe7a91b16aa25795b721312d0b11d1ae386 100644 (file)
@@ -32,6 +32,25 @@ inline vState increaseDNSSECStateCounter(const vState& state)
   return state;
 }
 
+/*
+ * This inline possibly sets currentState based on the new state. It will only
+ * set it to Secure iff the newState is Secure and mayUpgradeToSecure == true.
+ * This should be set by the calling function when checking more than one record
+ * and this is not the first record, this way, we can never go *back* to Secure
+ * from an Insecure vState
+ */
+inline void processNewState(vState& currentState, const vState& newState, bool& hadNTA, const bool& mayUpgradeToSecure)
+{
+  if (mayUpgradeToSecure && newState == Secure)
+    currentState = Secure;
+
+  if (newState == Insecure || newState == NTA) // We can never go back to Secure
+    currentState = Insecure;
+
+  if (newState == NTA)
+    hadNTA = true;
+}
+
 vState validateRecords(const vector<DNSRecord>& recs)
 {
   if(recs.empty())
@@ -53,41 +72,57 @@ vState validateRecords(const vector<DNSRecord>& recs)
   SRRecordOracle sro;
 
   vState state=Insecure;
+  bool hadNTA = false;
   if(numsigs) {
+    bool first = true;
     for(const auto& csp : cspmap) {
       for(const auto& sig : csp.second.signatures) {
-        state = getKeysFor(sro, sig->d_signer, keys); // XXX check validity here
-        if(state == NTA) {
-          increaseDNSSECStateCounter(state);
-          return Insecure;
-        }
+        vState newState = getKeysFor(sro, sig->d_signer, keys); // XXX check validity here
+
+        if (newState == Bogus) // No hope
+          return increaseDNSSECStateCounter(Bogus);
+
+        processNewState(state, newState, hadNTA, first);
+
+        first = false;
+
         LOG("! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys"<<endl);
         for(const auto& k : keys) {
           LOG("Key: "<<k.getZoneRepresentation()<< " {tag="<<k.getTag()<<"}"<<endl);
         }
-        // this sort of charges on and 'state' ends up as the last thing to have been checked
-        // maybe not the right idea
       }
     }
-    if(state == Bogus)
-      return increaseDNSSECStateCounter(state);
     validateWithKeySet(cspmap, validrrsets, keys);
   }
   else {
     LOG("! no sigs, hoping for Insecure status of "<<recs.begin()->d_name<<endl);
-    state = getKeysFor(sro, recs.begin()->d_name, keys); // um WHAT DOES THIS MEAN - try first qname??
-   
-    LOG("! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys "<<endl);
-    
+
+    bool first = true;
+    for(const auto& rec : recs) {
+      vState newState = getKeysFor(sro, rec.d_name, keys);
+
+      if (newState == Bogus) // We're done
+        return increaseDNSSECStateCounter(Bogus);
+
+      processNewState(state, newState, hadNTA, first);
+      first = false;
+
+      LOG("! state = "<<vStates[state]<<", now have "<<keys.size()<<" keys "<<endl);
+    }
     return increaseDNSSECStateCounter(state);
   }
-  
+
   LOG("Took "<<sro.d_queries<<" queries"<<endl);
   if(validrrsets.size() == cspmap.size())// shortcut - everything was ok
     return increaseDNSSECStateCounter(Secure);
 
-  if(keys.empty())
+  if(state == Insecure || keys.empty()) {
+    if (hadNTA) {
+      increaseDNSSECStateCounter(NTA);
+      return Insecure;
+    }
     return increaseDNSSECStateCounter(Insecure);
+  }
 
 #if 0
   cerr<<"! validated "<<validrrsets.size()<<" RRsets out of "<<cspmap.size()<<endl;
@@ -108,6 +143,5 @@ vState validateRecords(const vector<DNSRecord>& recs)
       return increaseDNSSECStateCounter(Bogus);
     }
   }
-  
   return increaseDNSSECStateCounter(Insecure);
 }
index 8d9571ee977bb7606b25559e30384ce13be11459..f3a94dc78781919d83815a51808fbb16e8f50e6f 100644 (file)
@@ -203,16 +203,13 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, keyset_t &keyset)
   }
 
   vector<string> labels = zone.getRawLabels();
-  vState state;
-
-  state = Indeterminate;
 
   typedef std::multimap<uint16_t, DSRecordContent> dsmap_t;
   dsmap_t dsmap;
   keyset_t validkeys;
 
   DNSName qname = lowestTA;
-  state = Secure; // the lowest Trust Anchor is secure
+  vState state = Secure; // the lowest Trust Anchor is secure
 
   while(zone.isPartOf(qname))
   {
@@ -381,13 +378,19 @@ vState getKeysFor(DNSRecordOracle& dro, const DNSName& zone, keyset_t &keyset)
 
         for(const auto& v : validrrsets) {
           LOG("Do have: "<<v.first.first<<"/"<<DNSRecordContent::NumberToType(v.first.second)<<endl);
-          if(v.first.second==QType::NSEC) { // check that it covers us!
+          if(v.first.second==QType::CNAME) {
+            LOG("Found CNAME for "<< v.first.first << ", ignoring records at this level."<<endl);
+            goto skipLevel;
+          }
+          else if(v.first.second==QType::NSEC) { // check that it covers us!
             for(const auto& r : v.second.records) {
               LOG("\t"<<r->getZoneRepresentation()<<endl);
               auto nsec = std::dynamic_pointer_cast<NSECRecordContent>(r);
               if(nsec) {
-                if(v.first.first == qname && !nsec->d_set.count(QType::DS))
+                if(v.first.first == qname && !nsec->d_set.count(QType::DS)) {
+                  LOG("Denies existence of DS!"<<endl);
                   return Insecure;
+                }
                 else if(v.first.first.canonCompare(qname) && qname.canonCompare(nsec->d_next) ) {
                   LOG("Did not find DS for this level, trying one lower"<<endl);
                   goto skipLevel;
index a568c2e5a7d5e10d44d9e5182c963c4855b63ad2..5934a3b6b805e20f0517cb109399b7c26325230e 100644 (file)
@@ -615,7 +615,7 @@ static void gatherRecordsFromZone(const std::string& zonestring, vector<DNSResou
     }
   }
   catch(std::exception& ae) {
-    throw ApiException("An error occured while parsing the zonedata: "+string(ae.what()));
+    throw ApiException("An error occurred while parsing the zonedata: "+string(ae.what()));
   }
 }
 
index e2542b3bfa5f0a7ac409dcd75d035da6bd7f4013..d98392fd73e202bd6246bc6f45a5a44bd11cd8b5 100644 (file)
@@ -1018,3 +1018,121 @@ class TestAdvancedNMGRule(DNSDistTest):
         (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
         self.assertEquals(receivedResponse, expectedResponse)
 
+class TestAdvancedLabelsCountRule(DNSDistTest):
+
+    _config_template = """
+    addAction(QNameLabelsCountRule(5,6), RCodeAction(dnsdist.REFUSED))
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    def testAdvancedLabelsCountRule(self):
+        """
+        Advanced: QNameLabelsCountRule(5,6)
+        """
+        # 6 labels, we should be fine
+        name = 'ok.labelscount.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '192.0.2.1')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        # more than 6 labels, the query should be refused
+        name = 'not.ok.labelscount.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        expectedResponse = dns.message.make_response(query)
+        expectedResponse.set_rcode(dns.rcode.REFUSED)
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        # less than 5 labels, the query should be refused
+        name = 'labelscountadvanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        expectedResponse = dns.message.make_response(query)
+        expectedResponse.set_rcode(dns.rcode.REFUSED)
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
+
+class TestAdvancedWireLengthRule(DNSDistTest):
+
+    _config_template = """
+    addAction(QNameWireLengthRule(54,56), RCodeAction(dnsdist.REFUSED))
+    newServer{address="127.0.0.1:%s"}
+    """
+
+    def testAdvancedWireLengthRule(self):
+        """
+        Advanced: QNameWireLengthRule(54,56)
+        """
+        name = 'longenough.qnamewirelength.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        response = dns.message.make_response(query)
+        rrset = dns.rrset.from_text(name,
+                                    3600,
+                                    dns.rdataclass.IN,
+                                    dns.rdatatype.A,
+                                    '192.0.2.1')
+        response.answer.append(rrset)
+
+        (receivedQuery, receivedResponse) = self.sendUDPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        (receivedQuery, receivedResponse) = self.sendTCPQuery(query, response)
+        self.assertTrue(receivedQuery)
+        self.assertTrue(receivedResponse)
+        receivedQuery.id = query.id
+        self.assertEquals(query, receivedQuery)
+        self.assertEquals(response, receivedResponse)
+
+        # too short, the query should be refused
+        name = 'short.qnamewirelength.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        expectedResponse = dns.message.make_response(query)
+        expectedResponse.set_rcode(dns.rcode.REFUSED)
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        # too long, the query should be refused
+        name = 'toolongtobevalid.qnamewirelength.advanced.tests.powerdns.com.'
+        query = dns.message.make_query(name, 'A', 'IN')
+        expectedResponse = dns.message.make_response(query)
+        expectedResponse.set_rcode(dns.rcode.REFUSED)
+
+        (_, receivedResponse) = self.sendUDPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
+
+        (_, receivedResponse) = self.sendTCPQuery(query, response=None, useQueue=False)
+        self.assertEquals(receivedResponse, expectedResponse)
index 1d2db69404ad09d947ce8fdec6c2c1fe2dc22295..b1b86f7106ef00bb81dce47922a179aee2ae55ab 100644 (file)
@@ -92,6 +92,9 @@ ns1.optout.example.      3600 IN A    {prefix}.14
 
 insecure-formerr.example. 3600 IN NS   ns1.insecure-formerr.example.
 ns1.insecure-formerr.example. 3600 IN A    {prefix}.2
+
+islandofsecurity.example.          3600 IN NS   ns1.islandofsecurity.example.
+ns1.islandofsecurity.example.      3600 IN A    {prefix}.9
         """,
         'secure.example': """
 secure.example.          3600 IN SOA  {soa}
@@ -102,9 +105,14 @@ host1.secure.example.    3600 IN A    192.0.2.2
 cname.secure.example.    3600 IN CNAME host1.secure.example.
 cname-to-insecure.secure.example. 3600 IN CNAME node1.insecure.example.
 cname-to-bogus.secure.example.    3600 IN CNAME ted.bogus.example.
+cname-to-islandofsecurity.secure.example. 3600 IN CNAME node1.islandofsecurity.example.
 
 host1.sub.secure.example. 3600 IN A    192.0.2.11
 
+;; See #4158
+sub2.secure.example. 3600 IN CNAME doesnotmatter.insecure.example.
+insecure.sub2.secure.example. 3600 IN NS ns1.insecure.example.
+
 *.wildcard.secure.example.    3600 IN A    192.0.2.10
 
 *.cnamewildcard.secure.example. 3600 IN CNAME host1.secure.example.
@@ -119,6 +127,12 @@ bogus.example.           3600 IN NS   ns1.bogus.example.
 ns1.bogus.example.       3600 IN A    {prefix}.12
 ted.bogus.example.       3600 IN A    192.0.2.1
 bill.bogus.example.      3600 IN AAAA 2001:db8:12::3
+        """,
+        'insecure.sub2.secure.example': """
+insecure.sub2.secure.example.        3600 IN SOA  {soa}
+insecure.sub2.secure.example.        3600 IN NS   ns1.insecure.example.
+
+node1.insecure.sub2.secure.example.  3600 IN A    192.0.2.18
         """,
         'insecure.example': """
 insecure.example.        3600 IN SOA  {soa}
@@ -154,6 +168,13 @@ secure.optout.example.          3600 IN NS   ns1.secure.optout.example.
 ns1.secure.optout.example.      3600 IN A    {prefix}.15
 
 node1.secure.optout.example.    3600 IN A    192.0.2.8
+        """,
+        'islandofsecurity.example': """
+islandofsecurity.example.          3600 IN SOA  {soa}
+islandofsecurity.example.          3600 IN NS   ns1.islandofsecurity.example.
+ns1.islandofsecurity.example.      3600 IN A    {prefix}.9
+
+node1.islandofsecurity.example.    3600 IN A    192.0.2.20
         """
     }
 
@@ -194,6 +215,12 @@ PrivateKey: efmq9G+J4Y2iPnIBRwJiy6Z/nIHSzpsCy/7XHhlS19A=
 Private-key-format: v1.2
 Algorithm: 13 (ECDSAP256SHA256)
 PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
+        """,
+
+        'islandofsecurity.example': """
+Private-key-format: v1.2
+Algorithm: 13 (ECDSAP256SHA256)
+PrivateKey: o9F5iix8V68tnMcuOaM2Lt8XXhIIY//SgHIHEePk6cM=
         """
     }
 
@@ -202,11 +229,11 @@ PrivateKey: xcNUxt1Knj14A00lKQFDboluiJyM2f7FxpgsQaQ3AQ4=
     # go into the _zones's zonecontent
     _auth_zones = {
         '8': ['ROOT'],
-        '9': ['secure.example'],
+        '9': ['secure.example', 'islandofsecurity.example'],
         '10': ['example'],
         '11': ['example'],
         '12': ['bogus.example'],
-        '13': ['insecure.example'],
+        '13': ['insecure.example', 'insecure.sub2.secure.example'],
         '14': ['optout.example'],
         '15': ['insecure.optout.example', 'secure.optout.example']
     }
index 9225ff602fcfbadc8921fe192eda3aec7ac2716c..22b63c89d891db5ebe0f9c660ed7d53dbc01d740 100644 (file)
@@ -1,6 +1,5 @@
 import os
 import socket
-#import unittest
 
 import dns
 from recursortests import RecursorTest
@@ -241,7 +240,7 @@ class TestFlags(RecursorTest):
         expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
         res = self.sendUDPQuery(msg, 'process')
 
-        self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+        self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD'], ['DO'])
         self.assertMatchingRRSIGInAnswer(res, expected)
 
     def testValidate_Secure_DO(self):
@@ -249,7 +248,7 @@ class TestFlags(RecursorTest):
         expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
         res = self.sendUDPQuery(msg, 'validate')
 
-        self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+        self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD'], ['DO'])
         self.assertMatchingRRSIGInAnswer(res, expected)
 
     ##
@@ -275,7 +274,7 @@ class TestFlags(RecursorTest):
         expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
         res = self.sendUDPQuery(msg, 'process')
 
-        self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+        self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD', 'CD'], ['DO'])
         self.assertMatchingRRSIGInAnswer(res, expected)
 
     def testValidate_Secure_DOCD(self):
@@ -283,7 +282,7 @@ class TestFlags(RecursorTest):
         expected = dns.rrset.from_text('ns1.example.', 0, dns.rdataclass.IN, 'A', '{prefix}.10'.format(prefix=self._PREFIX))
         res = self.sendUDPQuery(msg, 'validate')
 
-        self.assertMessageHasFlags(res, ['QR', 'RA', 'RD', 'CD'], ['DO'])
+        self.assertMessageHasFlags(res, ['AD', 'QR', 'RA', 'RD', 'CD'], ['DO'])
         self.assertMatchingRRSIGInAnswer(res, expected)
 
     ##
@@ -482,9 +481,9 @@ class TestFlags(RecursorTest):
         expected = dns.rrset.from_text('ted.bogus.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.1')
         res = self.sendUDPQuery(msg, 'process')
 
-        self.assertRcodeEqual(res, dns.rcode.NOERROR)
         self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
-        self.assertMatchingRRSIGInAnswer(res, expected)
+        self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
+        self.assertAnswerEmpty(res)
 
     def testValidate_Bogus_DO(self):
         msg = self.getQueryForBogus('', 'DO')
similarity index 76%
rename from regression-tests.recursor-dnssec/test_EDNS_fallback.py
rename to regression-tests.recursor-dnssec/test_Interop.py
index 49239e7cb5dc3a8e857c72ebb0faecbf5b497ef6..358e6031c756deedf9fca69e1246545f64b9eb5c 100644 (file)
@@ -24,6 +24,20 @@ class testInterop(RecursorTest):
         self.assertRcodeEqual(res, dns.rcode.NOERROR)
         self.assertRRsetInAnswer(res, expected)
 
+    def testCNAMEWithLowerEntries(self):
+        """
+        #4158, When chasing down for DS/DNSKEY and we find a CNAME, skip a level
+        """
+        expected = dns.rrset.from_text('node1.insecure.sub2.secure.example.', 0, dns.rdataclass.IN, 'A', '192.0.2.18')
+
+        query = dns.message.make_query('node1.insecure.sub2.secure.example.', 'A')
+        query.flags |= dns.flags.AD
+        res = self.sendUDPQuery(query)
+
+        self.assertRcodeEqual(res, dns.rcode.NOERROR)
+        self.assertMessageHasFlags(res, ['QR', 'RA', 'RD'], ['DO'])
+        self.assertRRsetInAnswer(res, expected)
+
     @classmethod
     def startResponders(cls):
         print("Launching responders..")
index a9e762f4cd24ab41ff1329dedb9479798e5cbbd9..300710dbd3bcd78b7254f14436d7e4252394277f 100644 (file)
@@ -1,10 +1,22 @@
 import dns
+import os
 from recursortests import RecursorTest
 
 class testSimple(RecursorTest):
     _confdir = 'Simple'
 
-    _config_template = """dnssec=validate"""
+    _config_template = """dnssec=validate
+auth-zones=authzone.example=configs/%s/authzone.zone""" % _confdir
+
+    @classmethod
+    def generateRecursorConfig(cls, confdir):
+        authzonepath = os.path.join(confdir, 'authzone.zone')
+        with open(authzonepath, 'w') as authzone:
+            authzone.write("""$ORIGIN authzone.example.
+@ 3600 IN SOA {soa}
+@ 3600 IN A 192.0.2.88
+""".format(soa=cls._SOA))
+        super(testSimple, cls).generateRecursorConfig(confdir)
 
     def testSOAs(self):
         for zone in ['.', 'example.', 'secure.example.']:
@@ -46,3 +58,41 @@ class testSimple(RecursorTest):
         res = self.sendUDPQuery(query)
 
         self.assertRcodeEqual(res, dns.rcode.SERVFAIL)
+
+    def testAuthZone(self):
+        query = dns.message.make_query('authzone.example', 'A', want_dnssec=True)
+
+        expectedA = dns.rrset.from_text('authzone.example.', 0, 'IN', 'A', '192.0.2.88')
+
+        res = self.sendUDPQuery(query)
+
+        self.assertRcodeEqual(res, dns.rcode.NOERROR)
+        self.assertRRsetInAnswer(res, expectedA)
+
+    def testLocalhost(self):
+        queryA = dns.message.make_query('localhost', 'A', want_dnssec=True)
+        expectedA = dns.rrset.from_text('localhost.', 0, 'IN', 'A', '127.0.0.1')
+
+        queryPTR = dns.message.make_query('1.0.0.127.in-addr.arpa', 'PTR', want_dnssec=True)
+        expectedPTR = dns.rrset.from_text('1.0.0.127.in-addr.arpa.', 0, 'IN', 'PTR', 'localhost.')
+
+        resA = self.sendUDPQuery(queryA)
+        resPTR = self.sendUDPQuery(queryPTR)
+
+        self.assertRcodeEqual(resA, dns.rcode.NOERROR)
+        self.assertRRsetInAnswer(resA, expectedA)
+
+        self.assertRcodeEqual(resPTR, dns.rcode.NOERROR)
+        self.assertRRsetInAnswer(resPTR, expectedPTR)
+
+    def testIslandOfSecurity(self):
+        query = dns.message.make_query('cname-to-islandofsecurity.secure.example.', 'A', want_dnssec=True)
+
+        expectedCNAME = dns.rrset.from_text('cname-to-islandofsecurity.secure.example.', 0, 'IN', 'CNAME', 'node1.islandofsecurity.example.')
+        expectedA = dns.rrset.from_text('node1.islandofsecurity.example.', 0, 'IN', 'A', '192.0.2.20')
+
+        res = self.sendUDPQuery(query)
+
+        self.assertRcodeEqual(res, dns.rcode.NOERROR)
+        self.assertRRsetInAnswer(res, expectedA)
+