#2272·blocky

Regression since v0.32.0 (#2102): small responses sent without name compression break embedded client (Garmin Index scale)

Author: HoMS1987Created Sep 19, 2026Updated Sep 19, 2026

Summary

Since #2102 (first released in v0.32.0) small responses are sent without DNS name compression. A Garmin Index smart scale (first generation) discards these responses and never completes its time sync / setup. With a resolver that compresses (AVM FRITZ!Box) it works.

I know RFC 1035 §4.1.4 allows senders to omit pointers ("Programs are free to avoid using pointers in messages they generate") and requires receivers to understand them, so strictly speaking the scale's resolver is at fault. But it is a closed-firmware device that will not be fixed, and before #2102 blocky compressed every response (forced Compress = true), so this is a behaviour change that breaks previously working clients.

Environment

  • blocky v0.35.0 (ghcr.io/0xerr0r/blocky:v0.35.0), Kubernetes, plain UDP on port 53
  • upstream: FRITZ!Box
  • client: Garmin Index smart scale, first generation (not the S2), Wi-Fi 2.4 GHz

Observed behaviour (packet capture on the blocky node)

The scale sends a non-EDNS query (ar=0). blocky answers immediately with a correct response, but the scale retransmits the same query with the same transaction ID and source port in a backoff loop (every ~4–8 s), i.e. it discards blocky's answer:

20:59:02.731 UDP 192.168.2.27:61538 -> 192.168.2.212:53   id=0x4f97 QUERY  clock.garmin.com A
20:59:02.731 UDP 192.168.2.212:53 -> 192.168.2.27:61538   id=0x4f97 flags=0x8180 NOERROR an=1  A 204.77.162.174
20:59:06.645 UDP 192.168.2.27:61538 -> 192.168.2.212:53   id=0x4f97 QUERY  clock.garmin.com A   <- same id, retransmit
20:59:06.645 UDP 192.168.2.212:53 -> 192.168.2.27:61538   id=0x4f97 flags=0x8180 NOERROR an=1  A 204.77.162.174
20:59:11.150 ...                                          id=0x4f97 (retransmit again)
...

The scale never gets past this step (it never queries gold.garmin.com, the upload endpoint). Nothing is blocked: the query log shows CACHED/RESOLVED + NOERROR for every request. With the FRITZ!Box as DNS server, setup completes.

Byte comparison

Same query (clock.garmin.com A, no EDNS), answer section:

blocky    (66 bytes): 05 63 6c 6f 63 6b 06 67 61 72 6d 69 6e 03 63 6f 6d 00 00 01 00 01 00 00 00 50 00 04 cc 4d a2 ae
FRITZ!Box (50 bytes): c0 0c 00 01 00 01 00 00 00 a8 00 04 cc 4d a2 ae

Header flags, rcode, question, record type/class and address are identical; only the owner-name encoding (and TTL) differ. For comparison, 1.1.1.1, 8.8.8.8 and 9.9.9.9 all return the compressed 50-byte form for this query, so clients like this one are effectively only ever tested against compressed answers.

Cause

server/client_query.go: since #2102 Truncate decides compression and disables it when the message fits uncompressed; #2213 re-enables it only for responses larger than 512 bytes (res.Len() > dns.MinMsgSize). A single-A answer therefore goes out uncompressed.

Suggestion

Compress all responses again (as before #2102), or add a config option to force compression. #2102 notes the gain is avoiding a compression-map allocation per response; I'd argue compatibility with fragile embedded clients is worth that cost, at least as an opt-in.

Verification status

The scale used to work with blocky, and it stopped working around the time v0.32.0 was released, which matches #2102. I have not verified this by downgrading (it's my production resolver), so the causal link is based on the capture, the byte comparison and the timeline.