While I was working on my presentation about “Secure DNS” for this year’s SharkFest, the Wireshark Developer and User Conference, I recognised that I’m still missing some DNS-related packet captures in the Ultimate PCAP, that is DNS over TLS and DNS over HTTPS. And while working on it with the DNSDiag toolkit (thanks, Babak!), I came across DNS over QUIC and DNS over HTTP/3. 😂 Here we go:
Please find the packets/sessions in the Ultimate PCAP with the following Display Filter, which filters for the used Do{T|H|Q|H3}-endpoints. For the sake of completeness, I also captured a standard UDP and TCP DNS session.
|
1 |
ipv6.addr in {2a13:1001::86:54:11:1,2a13:1001::86:54:11:201,2a10:50c0::ad1:ff,2a10:50c0::ad2:ff} |
Also, refer to the packet comments of the first packet of each session, where I have listed the respective command.
Setup
For the DNS-servers, I primarily used the DNS4EU “Protective resolution” (UDP, TCP, DoT, DoH), while AdGuard DNS for DoQ and DoH3. Thanks to the DNSDiag tool “dnsping”, which supports all of those variants. (I used version 2.9.0 during my tests.) These were my six commands, each querying four times the A record of “heise.de”:
|
1 2 3 4 5 6 |
./dnsping.py --server 2a13:1001::86:54:11:1 -c 4 heise.de ./dnsping.py --server 2a13:1001::86:54:11:1 --tcp -c 4 heise.de ./dnsping.py --server protective.joindns4.eu --tls -c 4 heise.de ./dnsping.py --server protective.joindns4.eu --doh -c 4 heise.de ./dnsping.py --server dns.adguard-dns.com --quic -c 4 heise.de ./dnsping.py --server dns.adguard-dns.com --http3 -c 4 heise.de |
Here’s the complete log:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
weberjoh@nuc:~/dnsdiag$ ./dnsping.py --server 2a13:1001::86:54:11:1 -c 4 heise.de dnsping.py DNS: [2a13:1001::86:54:11:1]:53, hostname: heise.de, proto: UDP, class: IN, type: A, flags: [RD] 42 bytes from [2a13:1001::86:54:11:1]: seq=1 time=8.661 ms NOERROR 42 bytes from [2a13:1001::86:54:11:1]: seq=2 time=12.478 ms NOERROR 42 bytes from [2a13:1001::86:54:11:1]: seq=3 time=11.645 ms NOERROR 42 bytes from [2a13:1001::86:54:11:1]: seq=4 time=11.245 ms NOERROR --- [2a13:1001::86:54:11:1] dnsping statistics --- 4 requests transmitted, 4 responses received, 0% lost min=8.661 ms, avg=11.007 ms, max=12.478 ms, stddev=1.646 ms weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ ./dnsping.py --server 2a13:1001::86:54:11:1 --tcp -c 4 heise.de dnsping.py DNS: [2a13:1001::86:54:11:1]:53, hostname: heise.de, proto: TCP, class: IN, type: A, flags: [RD] 42 bytes from [2a13:1001::86:54:11:1]: seq=1 time=22.471 ms NOERROR 42 bytes from [2a13:1001::86:54:11:1]: seq=2 time=24.437 ms NOERROR 42 bytes from [2a13:1001::86:54:11:1]: seq=3 time=23.537 ms NOERROR 42 bytes from [2a13:1001::86:54:11:1]: seq=4 time=22.864 ms NOERROR --- [2a13:1001::86:54:11:1] dnsping statistics --- 4 requests transmitted, 4 responses received, 0% lost min=22.471 ms, avg=23.327 ms, max=24.437 ms, stddev=0.861 ms weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ ./dnsping.py --server protective.joindns4.eu --tls -c 4 heise.de dnsping.py DNS: protective.joindns4.eu:853, hostname: heise.de, proto: TLS, class: IN, type: A, flags: [RD] 42 bytes from protective.joindns4.eu: seq=1 time=87.545 ms NOERROR 42 bytes from protective.joindns4.eu: seq=2 time=91.599 ms NOERROR 42 bytes from protective.joindns4.eu: seq=3 time=91.103 ms NOERROR 42 bytes from protective.joindns4.eu: seq=4 time=90.454 ms NOERROR --- protective.joindns4.eu dnsping statistics --- 4 requests transmitted, 4 responses received, 0% lost min=87.545 ms, avg=90.175 ms, max=91.599 ms, stddev=1.815 ms weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ ./dnsping.py --server protective.joindns4.eu --doh -c 4 heise.de dnsping.py DNS: protective.joindns4.eu:443, hostname: heise.de, proto: HTTPS, class: IN, type: A, flags: [RD] 42 bytes from protective.joindns4.eu: seq=1 time=50.702 ms NOERROR 42 bytes from protective.joindns4.eu: seq=2 time=50.802 ms NOERROR 42 bytes from protective.joindns4.eu: seq=3 time=44.650 ms NOERROR 42 bytes from protective.joindns4.eu: seq=4 time=45.645 ms NOERROR --- protective.joindns4.eu dnsping statistics --- 4 requests transmitted, 4 responses received, 0% lost min=44.650 ms, avg=47.950 ms, max=50.802 ms, stddev=3.261 ms weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ ./dnsping.py --server dns.adguard-dns.com --quic -c 4 heise.de dnsping.py DNS: dns.adguard-dns.com:853, hostname: heise.de, proto: QUIC, class: IN, type: A, flags: [RD] 42 bytes from dns.adguard-dns.com: seq=1 time=49.083 ms NOERROR 42 bytes from dns.adguard-dns.com: seq=2 time=43.731 ms NOERROR 42 bytes from dns.adguard-dns.com: seq=3 time=29.795 ms NOERROR 42 bytes from dns.adguard-dns.com: seq=4 time=70.312 ms NOERROR --- dns.adguard-dns.com dnsping statistics --- 4 requests transmitted, 4 responses received, 0% lost min=29.795 ms, avg=48.230 ms, max=70.312 ms, stddev=16.817 ms weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ weberjoh@nuc:~/dnsdiag$ ./dnsping.py --server dns.adguard-dns.com --http3 -c 4 heise.de dnsping.py DNS: dns.adguard-dns.com:443, hostname: heise.de, proto: HTTP3, class: IN, type: A, flags: [RD] 42 bytes from dns.adguard-dns.com: seq=1 time=45.593 ms NOERROR 42 bytes from dns.adguard-dns.com: seq=2 time=28.860 ms NOERROR 42 bytes from dns.adguard-dns.com: seq=3 time=37.118 ms NOERROR 42 bytes from dns.adguard-dns.com: seq=4 time=37.592 ms NOERROR --- dns.adguard-dns.com dnsping statistics --- 4 requests transmitted, 4 responses received, 0% lost min=28.860 ms, avg=37.291 ms, max=45.593 ms, stddev=6.834 ms weberjoh@nuc:~/dnsdiag$ |
Wiresharking all the Stuff
Since the payload is encrypted in all four cases, it’s only the headers (TCP/UDP destination ports) and the TLS handshake, if present, that are of interest. Depending on the variant, you can see the Server Name Indication (SNI) and the server certificate.
As with the current Wireshark version 4.6.0, DoQ is decoded as DTLS, while it should be QUIC. (Feature request is out. Feature request implemented after less than 24 hours.) As a workaround, use the “Decode As…” function, as used in the screenshot:
Traffic Logs on a Palo
Just because I was curious, those are the detected applications as seen from a Palo Alto Networks firewall (without TLS interception), PAN-OS 11.2.9, Application Version 9034-9733 (10/27/25):
It is not surprising that none of the DNS sessions were recognised as ‘DNS’, since the user data is completely encrypted. It is also interesting to note that direct DoQ traffic on the well-known UDP port 853 was only recognised as ‘unknown-udp’, while DoH3, which also runs via QUIC, albeit on the ‘classic’ QUIC port UDP 443, was recognised accordingly.
Thanks for watching. ;)
RFCs
Appendix
Additionally, I added some more DoT and DoH packets to the Ultimate PCAP, originating from other tools: dig, as well as real user traffic from Firefox using DoH. Refer to the packet comments. For dig, I used the following commands:
|
1 2 |
dig @test2.weberlab.de netsec.blog +https dig @test2.weberlab.de weberlab.de aaaa +dnssec +tls |
Soli Deo Gloria!
Photo by Markus Winkler on Unsplash.








very interesting work
thanks for sharing!