IngoZenz / personaldnsfilter

See http://www.zenz-solutions.de/personaldnsfilter for details
GNU General Public License v2.0
611 stars 53 forks source link

DNSSEC and TCP requests: large response potentially dropped? #314

Open realbiz21 opened 2 days ago

realbiz21 commented 2 days ago

personalDNSFilter 1.50.55.3 on Android 14.

I am testing a free and open source XMPP client, Cheogram.

Cheogram performs DNSSEC validation when it connects to the user's chosen XMPP server. Cheogram includes an implementation of minidns to fulfill its needs. It is my understanding that Cheogram obtains the DNS servers from the system and communicates with those servers directly.

To perform validation, it requests the relevant DNSKEY and DS records of the XMPP server's domain components. In my environment, the records for the domain and the TLD fit within a UDP DNS packet, with a buffer size of 1024.

However, a DNSKEY query for <Root> will not fit in a UDP request of buffer size 1024. This will lead the DNS client to try a TCP request:

$ dig +dnssec +bufsize=1024 DNSKEY .

This leads to a TCP request with an 1141 byte TCP payload with 1139 byte DNS response.

For some unknown reason, when this request and response is routed through pDNSFilter (in VPN mode), the DNSKEY response for <Root> does not appear to arrive at the client. With pDNSf disabled, the exact same request and response (byte for byte sans checksums) reaches the client.

Working:

Not Working:

pDNSf properly sees the request from the client, displays it in the window log, requests the data from the LAN DNS servers, and receives that data from the LAN DNS servers. When logging is enabled, pDNSf logs the query and responses in the traffic log.

Is there any peculiarity with pDNSf that may cause large TCP DNS responses to get dropped?

IngoZenz commented 2 days ago

hmmh - currently pDNSf does not support TCP requests. Maybe it is related to this fact. But then it also should not log request and response...

realbiz21 commented 2 days ago

I have a better understanding now.

When pDNSf is not active,

  1. App uses UDP, buffer size of 1024, with UDP LAN server
  2. LAN server returns TC to the app, indicating UDP would be truncated
  3. App then uses TCP with the UDP LAN server
  4. TCP response from the LAN server arrives to the app
  5. App is happy

When pDNSf is active,

  1. App uses UDP, buffer size of 1024, with the pDNSf local VPN DNS server
  2. pDNSf uses UDP, buffer size of 1024, with the UDP LAN server
  3. LAN server returns TC to pDNSf, indicating UDP would be truncated
  4. pDNSf then does a TCP fallback with the TCP LAN server, resizing its buffers to accomodate the payload
  5. TCP response from LAN server arrives to pDNSf (DNS payload of 1139 bytes)
  6. pDNSf supplies the response to the app via UDP (DNS payload of 1139 bytes)
  7. App isn't happy (DNS payload of 1139 bytes)

What I believe is happening is that since pDNSf is the actor performing the TCP fallback, when it replies to the app, the app is still in "UDP mode."

This means its internal buffers of 1024 bytes may have been an accurate number, therefore receiving a 1139 byte UDP payload is missed, ignored, or improperly parsed by the app.

If pDNSf supported TCP for inbound DNS requests for apps, it would have the duty to return TC truncation to the app for the above scenario, so the app can switch to TCP and have appropriately-sized buffers.

Given that pDNSf does not support TCP inbound requests, the current handling by pDNSf is likely fine. Being: pDNSf can potentially return data larger than the app's indicated UDP buffer size. Therefore, the app must either use larger buffers by default or handle the case when the response is too large. Neither is ideal nor foolproof.

In this scenario, I'm going to recommend the app in question increase its default buffer sizes to the recommended value[1] of 1232 bytes, as the DNSKEY records for <Root> is a request the app makes unconditionally for all users of the app, regardless of the specific domain being DNSSEC-validated. At present, this is an 1139 byte payload. The app currently uses 1024 by default.

An ideal solution would be for pDNSf to support TCP inbound, though that's a feature request; not a bug.

[1]: https://www.dnsflagday.net/2020/#action-dns-software-vendors which reads:

It is important for DNS software vendors to comply with DNS standards, and to use a default EDNS buffer size (1232 bytes) that will not cause fragmentation on typical network links. Relevant standards include RFC 7766, RFC 6891 section 6.2.3. and RFC 6891 section 6.2.4.. The motivation for this effort is described in IETF draft intarea-frag-fragile section 6.1 and IETF draft iab-protocol-maintenance.

IngoZenz commented 2 days ago

Great analysis - I also think it is as you described. Regarding pDNSf, there is currently no plan to support TCP requests.