PowerDNS / pdns

PowerDNS Authoritative, PowerDNS Recursor, dnsdist
https://www.powerdns.com/
GNU General Public License v2.0
3.7k stars 909 forks source link

getPool(""):getCache():dump("/tmp/myfile.txt") -- doesn't dump ECS values #14649

Open PenelopeFudd opened 1 month ago

PenelopeFudd commented 1 month ago

Short description

With DNSDist, if you dump a cache to disk, it doesn't include all of the cached information. Specifically, it doesn't include ECS subnet information.

Environment

Steps to reproduce

  1. dnsdist -k "$password" -c 127.0.0.1:1111 -C /dev/null <<< 'getPool("pdns"):getCache():dump("/tmp/dnsdist-dump")'
  2. more /tmp/dnsdist-dump

Expected behaviour

; dnsdist's packet cache dump follows
;
v1.pv-txt.pool.dns.net. 23 TXT scope 11.22.33.44/24; rcode 0, key 2867384720, length 394, received over UDP 1, added 1725921938
v1.pv-txt.pool.dns.net. 22 TXT scope 11.22.55.66/24; rcode 0, key 3176530280, length 394, received over UDP 1, added 1725921937

Actual behaviour

; dnsdist's packet cache dump follows
;
v1.pv-txt.pool.dns.net. 23 TXT ; rcode 0, key 2867384720, length 394, received over UDP 1, added 1725921938
v1.pv-txt.pool.dns.net. 22 TXT ; rcode 0, key 3176530280, length 394, received over UDP 1, added 1725921937

Other information

Actually, would it be possible to see the answers, and not just their lengths?

Thanks!

phonedph1 commented 1 month ago

Hrm - maybe something like this (but with qtype/qclass in proper positions) - includes qclass, subnet and DO bit. Is there a 'nice' way to print query flags too?

I was half into this a few months ago but never got around to finishing it.

-        fprintf(filePtr.get(), "%s %" PRId64 " %s ; rcode %" PRIu8 ", key %" PRIu32 ", length %" PRIu16 ", received over UDP %d, added %" PRId64 "\n", value.qname.toString().c_str(), static_cast<int64_t>(value.validity - now), QType(value.qtype).toString().c_str(), rcode, entry.first, value.len, value.receivedOverUDP ? 1 : 0, static_cast<int64_t>(value.added));
+        fprintf(filePtr.get(), "%s %" PRId64 " %s %s ; scope %s, rcode %" PRIu8 ", key %" PRIu32 ", length %" PRIu16 ", received over UDP %d, added %" PRId64 ", dnssec %d\n", value.qname.toString().c_str(), static_cast<int64_t>(value.validity - now), QType(value.qtype).toString().c_str(), QClass(value.qclass).toString().c_str(), value.subnet ? value.subnet.get().toString().c_str() : "", rcode, entry.first, value.len, value.receivedOverUDP ? 1 : 0, static_cast<int64_t>(value.added), value.dnssecOK ? 1 : 0);
; dnsdist's packet cache dump follows
;
google.com. 293 A IN ; scope 8.8.8.0/24, rcode 0, key 2426484888, length 66, received over UDP 1, added 1725937615, dnssec 0
google.com. 25 TXT CHAOS ; scope 1.2.4.0/24, rcode 2, key 4091352989, length 50, received over UDP 1, added 1725937587, dnssec 1
google.com. 17 TXT CHAOS ; scope ::/128, rcode 2, key 556335832, length 63, received over UDP 1, added 1725937579, dnssec 0
google.com. 298 A IN ; scope 8.8.8.0/24, rcode 0, key 3904309213, length 66, received over UDP 1, added 1725937620, dnssec 1
google.com. 22 TXT CHAOS ; scope 1.2.4.0/24, rcode 2, key 4244306876, length 50, received over UDP 1, added 1725937584, dnssec 0
google.com. 49 A IN ; scope , rcode 5, key 227726898, length 39, received over UDP 1, added 1725937611, dnssec 0
rgacogne commented 1 month ago

Actually, would it be possible to see the answers, and not just their lengths?

Unfortunately dnsdist doesn't know how to parse most of the DNS records, because it doesn't have to, and we would like to keep it that way to reduce the amount of complexity. We could display the content of the response without decoding the content of records, just their owner names, types, classes and lengths. We might also offer an option to dump the raw (hex?) content of records.

@phonedph1 looks like a nice improvement, would you like to PR it? :)

PenelopeFudd commented 1 month ago

As long as I can see everything in the cache (in hex if nothing else), with some indication as to what parts are being used as the cache key and what the initial and current ttl are, that'd be nice.
If there was a way to dump the packets in a mocked-up pcap form (i.e. dummy source/destination), then parsing could be done with an external program (tcpdump/wireshark/etc).

Habbie commented 1 month ago

sidenote: base64 would be shorter

Habbie commented 1 month ago

If there was a way to dump the packets in a mocked-up pcap form (i.e. dummy source/destination), then parsing could be done with an external program (tcpdump/wireshark/etc).

sdig from pdns-utils can parse packets from stdin, so if we have hex or base64 full packets, that would be as simple as echo [base64 goes here] | openssl base64 -d | sdig stdin 0 . A

Habbie commented 1 month ago

note to self: sdig says Reply to question for .. even when I hand it a query packet.

phonedph1 commented 1 month ago

Amazing.

; dnsdist's packet cache dump follows
;
powerdns.com. 292 IN A ; ecs 70.0.0.0/8, rcode 0, key 3175689385, length 254, received over UDP 1, added 1726003963, dnssecOK 1, base64response 94CBoAABAAMAAAABCHBvd2VyZG5zA2NvbQAAAQABwAwAAQABAAABLAAExzxnocAMAAEAAQAAASwABMc8Zz3ADAAuAAEAAAEsAKAAAQgCAAABLGbraYBmz7oAjLUIcG93ZXJkbnMDY29tAEYxPKnCR4OBvr28SQdRYoUy5WHBg1gYo5+WAHlbjyvPY8lIST23ueA6l5HG5xFicGUhF7CLoTZsSF7KigGogTbQMdQCnr8CaUMGSjhHa2DdTzLgS9lhaSYrII2aIyJMCzm6gnJhjpANhhm4Ijde10aya3fbYIBeVplVs1QRLKw/AAApAgAAAIAAAAkACAAFAAEIAEY=
powerdns.com. 55 IN A ; ecs empty, rcode 5, key 1913889586, length 41, received over UDP 1, added 1726003966, dnssecOK 0, base64response fcmBhQABAAAAAAABCHBvd2VyZG5zA2NvbQAAAQABAAApAgAAAAAAAAA=
$ echo 94CBoAABAAMAAAABCHBvd2VyZG5zA2NvbQAAAQABwAwAAQABAAABLAAExzxnocAMAAEAAQAAASwABMc8Zz3ADAAuAAEAAAEsAKAAAQgCAAABLGbraYBmz7oAjLUIcG93ZXJkbnMDY29tAEYxPKnCR4OBvr28SQdRYoUy5WHBg1gYo5+WAHlbjyvPY8lIST23ueA6l5HG5xFicGUhF7CLoTZsSF7KigGogTbQMdQCnr8CaUMGSjhHa2DdTzLgS9lhaSYrII2aIyJMCzm6gnJhjpANhhm4Ijde10aya3fbYIBeVplVs1QRLKw/AAApAgAAAIAAAAkACAAFAAEIAEY= | base64 -d | ./sdig stdin 0 . A
ID 63360 was not expected, this response was not meant for us!
Reply to question for qname='powerdns.com.', qtype=A
Rcode: 0 (No Error), RD: 1, QR: 1, TC: 0, AA: 0, opcode: 0
0   powerdns.com.   300 IN  A   199.60.103.161
0   powerdns.com.   300 IN  A   199.60.103.61
0   powerdns.com.   300 IN  RRSIG   A 8 2 300 [expiry] [inception] [keytag] powerdns.com. ...
2   .   32768   IN  OPT AAgABQABCABG
EDNS Subnet response: 70.0.0.0/8, scope: 0.0.0.0/0, family = 2
index d6323d8b3..f5cb1399d 100644
--- a/pdns/dnsdistdist/dnsdist-cache.cc
+++ b/pdns/dnsdistdist/dnsdist-cache.cc
@@ -28,6 +28,7 @@
 #include "dnsdist-ecs.hh"
 #include "ednssubnet.hh"
 #include "packetcache.hh"
+#include "base64.hh"

 // NOLINTNEXTLINE(bugprone-easily-swappable-parameters): too cumbersome to change at this point
 DNSDistPacketCache::DNSDistPacketCache(size_t maxEntries, uint32_t maxTTL, uint32_t minTTL, uint32_t tempFailureTTL, uint32_t maxNegativeTTL, uint32_t staleTTL, bool dontAge, uint32_t shards, bool deferrableInsertLock, bool parseECS) :
@@ -507,7 +508,8 @@ uint64_t DNSDistPacketCache::dump(int fileDesc)
           rcode = dnsHeader.rcode;
         }

-        fprintf(filePtr.get(), "%s %" PRId64 " %s ; rcode %" PRIu8 ", key %" PRIu32 ", length %" PRIu16 ", received over UDP %d, added %" PRId64 "\n", value.qname.toString().c_str(), static_cast<int64_t>(value.validity - now), QType(value.qtype).toString().c_str(), rcode, entry.first, value.len, value.receivedOverUDP ? 1 : 0, static_cast<int64_t>(value.added));
+        std::string rawResponse = Base64Encode(value.value);
+        fprintf(filePtr.get(), "%s %" PRId64 " %s %s ; ecs %s, rcode %" PRIu8 ", key %" PRIu32 ", length %" PRIu16 ", received over UDP %d, added %" PRId64 ", dnssecOK %d, base64response %s\n", value.qname.toString().c_str(), static_cast<int64_t>(value.validity - now), QClass(value.qclass).toString().c_str(), QType(value.qtype).toString().c_str(), value.subnet ? value.subnet.get().toString().c_str() : "empty", rcode, entry.first, value.len, value.receivedOverUDP ? 1 : 0, static_cast<int64_t>(value.added), value.dnssecOK ? 1 : 0, rawResponse.c_str());
       }
       catch (...) {
         fprintf(filePtr.get(), "; error printing '%s'\n", value.qname.empty() ? "EMPTY" : value.qname.toString().c_str());
PenelopeFudd commented 1 month ago

Should we do anything about the "ID 63360 was not expected, this response was not meant for us!" message? Although for the life of me I can't think of what would make sense:

Habbie commented 1 month ago

Should we do anything about the "ID 63360 was not expected, this response was not meant for us!" message?

that plus the thing I noted probably warrant a separate ticket about "improving sdig stdin". Want to do those honours?

Habbie commented 1 month ago

that plus the thing I noted probably warrant a separate ticket about "improving sdig stdin".

this directly became PR #14665. Let me know there if you can think of any other relevant improvements :)