Skip to content

Apply the unconnected socket requirements to added paths - #63

Merged
masa-koz merged 1 commit into
seera-mainfrom
masa-koz/unconnected-socket-new-path
Jul 31, 2026
Merged

Apply the unconnected socket requirements to added paths#63
masa-koz merged 1 commit into
seera-mainfrom
masa-koz/unconnected-socket-new-path

Conversation

@masa-koz

Copy link
Copy Markdown

Description

QUIC_PARAM_CONN_UNCONNECTED_UDP_SOCKET was only honoured on the connection's first path. QuicConnOpenNewPath, which opens the binding for every path added afterwards with QUIC_PARAM_CONN_ADD_PATH, still passed the path's remote address to QuicLibraryGetBinding:

    if (QuicAddrIsWildCard(&Path->Route.RemoteAddress) && QuicAddrGetPort(&Path->Route.RemoteAddress) == 0) {
        UdpConfig.RemoteAddress = &Connection->Paths[0].Route.RemoteAddress;
    } else {
        UdpConfig.RemoteAddress = &Path->Route.RemoteAddress;
    }

So the added path got a connected socket and a binding of its own, even though the connection had asked for the opposite. A connection using the parameter to keep several destinations on one local port would silently get a second local port as soon as it added a path.

Changes

UdpConfig.RemoteAddress is left NULL when the parameter is set, which is what marks the binding unconnected and lets the lookup match on local port alone.

The two requirements QuicConnStart already enforces are enforced here too, before the binding is built:

  • A shared binding. An unconnected socket receives datagrams from any remote address, so packets are matched to a connection by connection ID alone. Returns QUIC_STATUS_INVALID_STATE.
  • A specific local address. An unconnected socket has no source address of its own, and the path's first packet goes out before anything has been learned from the peer. Returns QUIC_STATUS_INVALID_PARAMETER.

Both log through ConnError with the same wording QuicConnStart uses. The early goto Error is safe: the label only releases PathID, which is NULL at that point.

Status code change

The local address requirement now reports QUIC_STATUS_INVALID_PARAMETER rather than QUIC_STATUS_INVALID_STATE, in QuicConnStart as well. It describes an address the caller passed in, not a state the connection is in. docs/Settings.md and the existing test are updated to match.

Testing

Two tests, because ADD_PATH reaches this code by two different routes.

QuicTestUnconnectedSocketAddPathBeforeStart — before the connection is started, QuicConnAddPath configures Paths[0] and returns without opening a binding (if (!Connection->State.Connected) goto Done;), so the address it sets has to satisfy QuicConnStart's requirement instead. Covers a wildcard local address being rejected, and a specific one connecting on the address that was named.

QuicTestUnconnectedSocketAddPathAfterStart — after the handshake, QuicConnAddPath runs QuicConnOpenNewPath and the binding is opened there. Two listeners are used so the added path has a remote address a connected socket could not have reached. Covers:

Requested Expected Enforced by
same local + same remote QUIC_STATUS_ADDRESS_IN_USE QuicConnAddPath's duplicate check
wildcard local + other remote QUIC_STATUS_INVALID_PARAMETER the new check in QuicConnOpenNewPath
same local + other remote success, local port unchanged UdpConfig.RemoteAddress = NULL

Both new checks were verified to be the ones failing the tests by disabling each in turn and watching the corresponding case flip to a failure.

Results:

  • *UnconnectedSocket*:*Path*:*Migration*:*ConnectionParam*:*Datagram* passes in full: 112 tests.
  • *Basic* passes in full: 497 tests.
  • No compiler warnings.

Not covered: the !ShareBinding branch in QuicConnOpenNewPath. Setting QUIC_PARAM_CONN_UNCONNECTED_UDP_SOCKET requires a shared binding, and QUIC_PARAM_CONN_SHARE_UDP_BINDING cannot be changed once the connection is started, so the combination is unreachable through the API. It stays as a guard against a binding that was un-shared afterwards.

Documentation

docs/Settings.md records the corrected status code and notes that each additional path opened with QUIC_PARAM_CONN_ADD_PATH needs a specific local address for the same reason.

QUIC_PARAM_CONN_UNCONNECTED_UDP_SOCKET was only honoured on the connection's
first path. QuicConnOpenNewPath, which opens the binding for every path added
afterwards with QUIC_PARAM_CONN_ADD_PATH, still passed the path's remote
address to QuicLibraryGetBinding, so the added path got a connected socket
and a binding of its own even though the connection had asked for the
opposite.

Carry the parameter through, and enforce the same two requirements
QuicConnStart does before doing so. A shared binding is required, since an
unconnected socket is demultiplexed by connection ID alone. A specific local
address is required, since an unconnected socket has no source address of its
own and the path's first packet goes out before anything has been learned
from the peer.

The local address requirement now reports QUIC_STATUS_INVALID_PARAMETER
rather than QUIC_STATUS_INVALID_STATE, in both QuicConnOpenNewPath and
QuicConnStart: it describes an address the caller passed in, not a state the
connection is in.

Two tests cover the two ways ADD_PATH reaches this code. Before the
connection is started it configures Paths[0] and defers the binding to
QuicConnStart; afterwards it opens a binding of its own through
QuicConnOpenNewPath. The latter checks that a wildcard local address is
rejected, that the address pair already in use is not a new path, and that a
path to a second server shares the local port the connection is already on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@masa-koz
masa-koz merged commit fc4661f into seera-main Jul 31, 2026
464 of 479 checks passed
@masa-koz
masa-koz deleted the masa-koz/unconnected-socket-new-path branch July 31, 2026 05:52
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.92308% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/core/connection.c 76.92% 3 Missing ⚠️

📢 Thoughts on this report? Let us know!

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.

1 participant