Skip to content

fix(udp): keep peer session on malformed packets and RST malformed CON - #676

Open
jkralik wants to merge 2 commits into
masterfrom
fix/issue-664-1
Open

jkralik wants to merge 2 commits into
masterfrom
fix/issue-664-1

Conversation

@jkralik

@jkralik jkralik commented Jul 7, 2026

Copy link
Copy Markdown
Member

Fixes #664 point 1

Summary by CodeRabbit

Summary by CodeRabbit

  • Fixes #664 point 1
    • Improved handling of malformed UDP/CoAP traffic: confirmable packets now receive a proper CoAP Reset response (with the correct Message ID) instead of tearing down the connection.
  • Bug Fixes
    • Enhanced decode failure reporting with more useful header/context details.
    • Better preserves existing per-peer UDP connections when invalid packets are received.
  • Tests
    • Added coverage to verify malformed confirmables are rejected with a Reset while keeping the connection intact.

Copilot AI review requested due to automatic review settings July 7, 2026 06:47
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds MalformedMessageError metadata for decode failures, changes coder field population order, and updates the UDP server to return a CoAP Reset for malformed confirmable packets while keeping the per-peer connection alive. New tests cover the Reset response and connection reuse.

Changes

Malformed message error and RST handling

Layer / File(s) Summary
MalformedMessageError type and Conn.Process integration
udp/client/conn.go
Adds exported MalformedMessageError with Type, MessageID, HasHeader, InvalidVersion, and Err, plus Error()/Unwrap(); Conn.Process now returns it on decode failure with best-effort header metadata.
Coder Decode field ordering change
udp/coder/coder.go
Reorders Decode to assign Type, Code, and MessageID earlier, clears Token/Payload/Options sooner, and moves the Token assignment later.
Server RST response for malformed confirmable messages
udp/server/server.go
Adds malformed-error classification, Reset datagram निर्माण, unknown-version handling, and centralized receive error handling; Serve now uses datagram/control-message helpers and can send an RST for malformed confirmable packets without closing the connection inline.
Tests for RST behavior and connection reuse
udp/server_test.go
Adds helpers for sending datagrams and checking Reset responses, plus a test that verifies malformed confirmable packets return Reset and reuse the same per-peer connection.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Peer
  participant Serve
  participant Conn as cc.Process
  participant Helper as handleProcessError

  Peer->>Serve: malformed confirmable datagram
  Serve->>Conn: Process(cm, buf)
  Conn-->>Serve: *MalformedMessageError
  Serve->>Helper: classify malformed / unknown version
  Helper->>Peer: write RST datagram with matching MID
  Serve->>Serve: continue receive loop
Loading

Possibly related PRs

  • plgd-dev/go-coap#663: Also changes udp/server/server.go receive-loop error handling and malformed-read processing in the UDP server path.

Suggested reviewers: jlevesy

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR addresses malformed-datagram session handling, but it does not implement the block-wise and If-None-Match requirements in #664. Implement the remaining RFC 7252/7959 behaviors: 4.08 for missing blocks, Content-Format consistency, duplicate Block1/Block2 rejection, and If-None-Match rules.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Out of Scope Changes check ✅ Passed The changes stay focused on UDP malformed-packet handling and related tests; no clearly unrelated scope was introduced.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately summarizes the main UDP behavior change: preserving peer sessions for malformed packets and sending RSTs for malformed confirmables.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-664-1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI 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.

Pull request overview

This PR adjusts the UDP server’s behavior on malformed inbound CoAP datagrams so that a single bad packet does not tear down the per-peer session, and (when possible) replies to malformed confirmable messages with a Reset (RST).

Changes:

  • Add malformed-packet classification and best-effort extraction of header metadata on decode failure.
  • Send an RST for malformed Confirmable messages when the CoAP header is present and usable.
  • Add a regression test ensuring malformed packets don’t recreate/tear down the per-peer connection.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
udp/server/server.go Keeps per-peer connection alive on decode failures and optionally sends RST for malformed CON packets.
udp/server_test.go Adds a UDP-level test asserting RST behavior and connection reuse across malformed packets.
udp/coder/coder.go Ensures header fields are populated early so callers can read Type/MID even when decode fails later.
udp/client/conn.go Wraps decode failures in a MalformedMessageError carrying best-effort header metadata.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread udp/server/server.go Outdated
@codecov-commenter

codecov-commenter commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.22222% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.48%. Comparing base (defc9c6) to head (da8090d).

Files with missing lines Patch % Lines
udp/server/server.go 68.18% 15 Missing and 13 partials ⚠️
udp/client/conn.go 83.33% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #676      +/-   ##
==========================================
- Coverage   76.63%   76.48%   -0.15%     
==========================================
  Files          77       77              
  Lines        6095     6184      +89     
==========================================
+ Hits         4671     4730      +59     
- Misses       1042     1058      +16     
- Partials      382      396      +14     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
udp/client/conn.go (1)

944-962: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer errors.Is over re-parsing the raw version bits.

Line 952 re-implements the version check that coder.Decode already performs and reports via coder.ErrMessageInvalidVersion. Duplicating the bit-mask logic here couples this file to the coder's wire-format details and can silently drift if that layout changes.

♻️ Proposed refactor
 		malformedErr := &MalformedMessageError{Err: err}
-		if len(datagram) >= 1 && datagram[0]>>6 != 1 {
+		if errors.Is(err, coder.ErrMessageInvalidVersion) {
 			malformedErr.InvalidVersion = true
 		}
 		if len(datagram) >= 4 && !malformedErr.InvalidVersion {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@udp/client/conn.go` around lines 944 - 962, The version check in Conn.Process
is duplicating coder wire-format logic by inspecting datagram[0] directly.
Update the malformed-message handling to detect coder.ErrMessageInvalidVersion
with errors.Is on the unmarshal error instead of re-parsing the version bits,
and keep the existing MalformedMessageError population and message release flow
in Conn.Process.
udp/server/server.go (1)

122-131: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Redundant/fragile classification alongside errors.As.

maybeRejectMalformedConfirmable already narrows to *client.MalformedMessageError via errors.As (which is the only type that reaches this path from Conn.Process). The extra sentinel-error whitelist here is a second, weaker gate that must be kept manually in sync with every decode-error type coder/message can produce — a future new decode error not added to this list would silently suppress the RST even though malformedErr is fully populated.

Consider dropping isMalformedMessageError and relying solely on errors.As(processErr, &malformedErr) plus the existing HasHeader/Type/MID checks.

♻️ Proposed simplification
-func isMalformedMessageError(err error) bool {
-	return errors.Is(err, coder.ErrMessageTruncated) ||
-		errors.Is(err, coder.ErrMessageInvalidVersion) ||
-		errors.Is(err, message.ErrInvalidTokenLen) ||
-		errors.Is(err, message.ErrInvalidOptionHeaderExt) ||
-		errors.Is(err, message.ErrOptionTruncated) ||
-		errors.Is(err, message.ErrOptionUnexpectedExtendMarker) ||
-		errors.Is(err, message.ErrOptionsTooSmall) ||
-		errors.Is(err, message.ErrInvalidEncoding)
-}
-
 func makeResetDatagram(mid int32) []byte {
 func (s *Server) maybeRejectMalformedConfirmable(l *coapNet.UDPConn, cc *client.Conn, cm *coapNet.ControlMessage, processErr error) {
-	if !isMalformedMessageError(processErr) {
-		return
-	}
 	var malformedErr *client.MalformedMessageError
 	if !errors.As(processErr, &malformedErr) {
 		return
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@udp/server/server.go` around lines 122 - 131, The malformed-message check in
isMalformedMessageError is redundant and fragile because
maybeRejectMalformedConfirmable already uses errors.As against
*client.MalformedMessageError from Conn.Process. Remove the sentinel-error
whitelist and rely on the existing errors.As-based narrowing plus the
HasHeader/Type/MID validation in maybeRejectMalformedConfirmable, keeping the
logic centered on the MalformedMessageError path.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@udp/server/server.go`:
- Around line 238-243: The cc.Process error handling currently treats every
failure as malformed, but only client.MalformedMessageError should use
maybeRejectMalformedConfirmable and continue. Update the error path in server.go
around cc.Process to detect malformed confirmable cases explicitly, and for all
other cc.Process errors keep the close-on-error fallback so oversized-datagram
and request-monitor failures still close the connection while preserving the
existing malformed RST/continue behavior.

---

Nitpick comments:
In `@udp/client/conn.go`:
- Around line 944-962: The version check in Conn.Process is duplicating coder
wire-format logic by inspecting datagram[0] directly. Update the
malformed-message handling to detect coder.ErrMessageInvalidVersion with
errors.Is on the unmarshal error instead of re-parsing the version bits, and
keep the existing MalformedMessageError population and message release flow in
Conn.Process.

In `@udp/server/server.go`:
- Around line 122-131: The malformed-message check in isMalformedMessageError is
redundant and fragile because maybeRejectMalformedConfirmable already uses
errors.As against *client.MalformedMessageError from Conn.Process. Remove the
sentinel-error whitelist and rely on the existing errors.As-based narrowing plus
the HasHeader/Type/MID validation in maybeRejectMalformedConfirmable, keeping
the logic centered on the MalformedMessageError path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 50eb4a65-e0bf-494b-a4aa-c93ed7178251

📥 Commits

Reviewing files that changed from the base of the PR and between defc9c6 and 3870429.

📒 Files selected for processing (4)
  • udp/client/conn.go
  • udp/coder/coder.go
  • udp/server/server.go
  • udp/server_test.go

Comment thread udp/server/server.go Outdated
Comment thread udp/server/server.go Outdated
@sonarqubecloud

sonarqubecloud Bot commented Jul 7, 2026

Copy link
Copy Markdown

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.

RFC 7252 / 7959 Conformance Issues — Malformed-Datagram Session Teardown and Block-wise Transfer Gaps

3 participants