Skip to content

[Fix] Preserve req->pos during reply validation to prevent packet truncation#5739

Merged
vstakhov merged 2 commits into
masterfrom
vstakhov-rdns-fix-truncation
Nov 10, 2025
Merged

[Fix] Preserve req->pos during reply validation to prevent packet truncation#5739
vstakhov merged 2 commits into
masterfrom
vstakhov-rdns-fix-truncation

Conversation

@vstakhov

Copy link
Copy Markdown
Member

Problem

The rdns library was truncating DNS packets on retransmit, causing the OPT additional section to be missing. This made resolvers like Knot-Resolver and PowerDNS-Recursor unable to parse retransmitted packets.

Root Cause

The rdns_request_reply_cmp function modifies req->pos as a side effect during reply validation. The req->pos field serves dual purposes:

  • During packet construction: tracks the write position (full packet length)
  • During reply validation: tracks the read position for comparison

When a request times out and needs retransmit:

  1. Initial send uses correct req->pos (includes OPT section)
  2. Reply validation overwrites req->pos with end of question section
  3. Retransmit uses corrupted req->pos, truncating the packet

Solution

Save and restore req->pos around the reply comparison logic in rdns_parse_reply. This ensures the original packet length is preserved for potential retransmits while still allowing the comparison function to work as designed.

The fix avoids adding new struct fields and has minimal performance impact (one variable save/restore).

…ncation on retransmit

The rdns_request_reply_cmp function modifies req->pos as a side effect
during reply validation. This caused packet truncation on retransmits
because req->pos (which tracks the full packet length) was overwritten
with the end position of the question section.

On timeout/retry, rdns_send_request would use the corrupted req->pos
value, resulting in truncated packets missing the OPT additional section.
This made resolvers like Knot and PowerDNS unable to parse retransmitted
packets.

Fix by saving and restoring req->pos around the reply comparison logic.
@vstakhov

Copy link
Copy Markdown
Member Author

@droid review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for preserving the original packet length. I spotted one blocking issue: switching the request to the TCP channel now happens before the TCP output buffer is allocated, so the failure path leaves the request detached from both hashes.

}
}

/* Switch IO channel from UDP to TCP */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: by moving the channel switch before allocating oc, the failure path returns false after we have already removed the request from the UDP hash, changed req->io, and potentially regenerated the id. Because we exit before assigning kh_value, re-arming the timer, or releasing old_ioc, the request is left detached while the old channel keeps its ref. Please defer the switch until after calloc succeeds (as before) or add explicit rollback on error.

When switching from UDP to TCP (e.g., on truncated response), the code
was copying the packet to the TCP output buffer before regenerating the
transaction ID to avoid collisions. This resulted in both UDP and TCP
packets having the same transaction ID.

Fix by moving the ID regeneration and IO channel switch logic before the
memcpy to the TCP output buffer, ensuring the TCP packet contains the
updated transaction ID.
@vstakhov
vstakhov force-pushed the vstakhov-rdns-fix-truncation branch from 4f5c277 to c774de1 Compare November 10, 2025 09:39
@vstakhov

Copy link
Copy Markdown
Member Author

@droid review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Saving and restoring req->pos prevents the retransmit truncation, and the TCP reschedule path now copies the packet after any ID regeneration.

@vstakhov
vstakhov merged commit 1b4619e into master Nov 10, 2025
27 checks passed
@vstakhov
vstakhov deleted the vstakhov-rdns-fix-truncation branch November 10, 2025 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] DNS TCP packets have trailing data, causing problems with Knot-Resolver

1 participant