From: Remi Gacogne Date: Mon, 12 Nov 2018 16:21:47 +0000 (+0100) Subject: dnsdist: Add 'zero-scope' support over TCP as well X-Git-Tag: rec-4.2.0-alpha1~42^2~5 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b8ea1bc6b8710b9bdf66a212c0c11dc84f09aff7;p=pdns dnsdist: Add 'zero-scope' support over TCP as well --- diff --git a/pdns/dnsdist-tcp.cc b/pdns/dnsdist-tcp.cc index e369aa0e0..b2ccfe5c5 100644 --- a/pdns/dnsdist-tcp.cc +++ b/pdns/dnsdist-tcp.cc @@ -433,21 +433,56 @@ void tcpClientThread(int pipefd) ds = policy.policy(servers, &dq); } + uint32_t cacheKeyNoECS = 0; + uint32_t cacheKey = 0; + boost::optional subnet; + char cachedResponse[4096]; + uint16_t cachedResponseSize = sizeof cachedResponse; + uint32_t allowExpired = ds ? 0 : g_staleCacheEntriesTTL; + bool useZeroScope = false; + + bool dnssecOK = false; + if (packetCache && !dq.skipCache) { + dnssecOK = (getEDNSZ(dq) & EDNS_HEADER_FLAG_DO); + } + if (dq.useECS && ((ds && ds->useECS) || (!ds && serverPool->getECS()))) { + // we special case our cache in case a downstream explicitly gave us a universally valid response with a 0 scope + if (packetCache && !dq.skipCache && !ds->disableZeroScope && packetCache->isECSParsingEnabled()) { + if (packetCache->get(dq, consumed, dq.dh->id, cachedResponse, &cachedResponseSize, &cacheKeyNoECS, subnet, dnssecOK, allowExpired)) { + DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.consumed, dq.local, dq.remote, (dnsheader*) cachedResponse, sizeof cachedResponse, cachedResponseSize, true, &queryRealTime); +#ifdef HAVE_PROTOBUF + dr.uniqueId = dq.uniqueId; +#endif + dr.qTag = dq.qTag; + + if (!processResponse(holders.cacheHitRespRulactions, dr, &delayMsec)) { + goto drop; + } + +#ifdef HAVE_DNSCRYPT + if (!encryptResponse(cachedResponse, &cachedResponseSize, sizeof cachedResponse, true, dnsCryptQuery, nullptr, nullptr)) { + goto drop; + } +#endif + handler.writeSizeAndMsg(cachedResponse, cachedResponseSize, g_tcpSendTimeout); + g_stats.cacheHits++; + continue; + } + + if (!subnet) { + /* there was no existing ECS on the query, enable the zero-scope feature */ + useZeroScope = true; + } + } + if (!handleEDNSClientSubnet(dq, &(ednsAdded), &(ecsAdded), g_preserveTrailingData)) { vinfolog("Dropping query from %s because we couldn't insert the ECS value", ci.remote.toStringWithPort()); goto drop; } } - uint32_t cacheKey = 0; - boost::optional subnet; - bool dnssecOK = false; if (packetCache && !dq.skipCache) { - char cachedResponse[4096]; - uint16_t cachedResponseSize = sizeof cachedResponse; - uint32_t allowExpired = ds ? 0 : g_staleCacheEntriesTTL; - dnssecOK = (getEDNSZ(dq) & EDNS_HEADER_FLAG_DO); if (packetCache->get(dq, (uint16_t) consumed, dq.dh->id, cachedResponse, &cachedResponseSize, &cacheKey, subnet, dnssecOK, allowExpired)) { DNSResponse dr(dq.qname, dq.qtype, dq.qclass, dq.consumed, dq.local, dq.remote, (dnsheader*) cachedResponse, sizeof cachedResponse, cachedResponseSize, true, &queryRealTime); #ifdef HAVE_PROTOBUF @@ -616,7 +651,8 @@ void tcpClientThread(int pipefd) break; } firstPacket=false; - if (!fixUpResponse(&response, &responseLen, &responseSize, qname, origFlags, ednsAdded, ecsAdded, rewrittenResponse, addRoom)) { + bool zeroScope = false; + if (!fixUpResponse(&response, &responseLen, &responseSize, qname, origFlags, ednsAdded, ecsAdded, rewrittenResponse, addRoom, useZeroScope ? &zeroScope : nullptr)) { break; } @@ -632,7 +668,19 @@ void tcpClientThread(int pipefd) } if (packetCache && !dq.skipCache) { - packetCache->insert(cacheKey, subnet, origFlags, dnssecOK, qname, qtype, qclass, response, responseLen, true, dh->rcode, dq.tempFailureTTL); + if (!useZeroScope) { + /* if the query was not suitable for zero-scope, for + example because it had an existing ECS entry so the hash is + not really 'no ECS', so just insert it for the existing subnet + since: + - we don't have the correct hash for a non-ECS query + - inserting with hash computed before the ECS replacement but with + the subnet extracted _after_ the replacement would not work. + */ + zeroScope = false; + } + // if zeroScope, pass the pre-ECS hash-key and do not pass the subnet to the cache + packetCache->insert(zeroScope ? cacheKeyNoECS : cacheKey, zeroScope ? boost::none : subnet, origFlags, dnssecOK, qname, qtype, qclass, response, responseLen, true, dh->rcode, dq.tempFailureTTL); } #ifdef HAVE_DNSCRYPT