You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
netcode 1.4.5: the connect token lifecycle, and the fixes around it (#184)
* netcode 1.4.5: the connect token lifecycle, and the fixes around it
A connect token history entry now carries a state. It is created pending when the server
accepts a connection request for a connect token it has not seen, admits retransmitted
requests from that same address while the handshake runs, and becomes consumed when the
client is installed in a client slot. A consumed entry admits nothing, whatever the source
address, so the keys inside a connect token encrypt exactly one session. Entries live until
their connect token expires, and a history whose entries all hold unexpired tokens refuses a
new connect token instead of evicting one. An entry's time is set at creation and never
refreshed. The encryption mapping carries the index of the history entry its handshake
belongs to, so installing a client consumes the right entry without anything new on the wire.
The server refuses any connect token that could have been issued before it started:
max_connect_token_lifetime in the server config is the longest lifetime the backend issues,
and a connection request whose connect token expire timestamp minus that lifetime is earlier
than the server start time is ignored, alongside the existing expiry check and before the
decrypt. The field defaults to NETCODE_DEFAULT_MAX_CONNECT_TOKEN_LIFETIME, and the examples
and harnesses set it to the lifetime they issue.
Also in this release:
- Key material is erased with sodium_memzero rather than memset: the encryption manager's
keys on reset and on removal, the client's connect token and context, the server's
challenge key on stop and its configured private key on destroy.
- IPv4 addresses are converted through ntohl and htonl and IPv6 halves through memcpy, so
address conversion is correct on big endian machines, and CI proves it on s390x under QEMU.
- netcode_packet_queue_clear pops until the queue is empty rather than freeing the first
num_packets slots, so a partially drained queue is cleared correctly. The drain loops its
two callers ran first are gone.
- The soak harness formats addresses with snprintf.
- The default static install is self contained: the vendored sodium objects are compiled into
libnetcode, and the install carries an exported CMake package that consumers find with
find_package(netcode CONFIG) and link as netcode::netcode. The system libsodium build
exports its libsodium instead. CI links a program against both from a clean prefix.
- Shared library builds are refused on Windows, where netcode.h declares no export macro.
Tests: test_connect_token_entries covers the pending and consumed rules, the unrefreshed
entry time, the full history refusal and reuse after expiry; a client and server wired
directly through the send and receive overrides drive
test_client_server_connection_request_retransmission, which drops the first server packets so
the client retransmits its connection request, test_client_server_replay_across_sessions,
which replays a first-session datagram into a second session, and
test_client_reconnect_with_used_connect_token and
test_client_error_connect_token_predates_server_start, which pin the two refusals. Building
the test runner with -DNETCODE_NONCE_AUDIT=ON records the key and nonce of every packet the
suite encrypts and fails on a repeat; it is a CI leg.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* The test wire drops packets once either end is being destroyed
netcode_server_destroy and netcode_client_destroy each send disconnect packets on the way
out, and the wire handed them straight to the other end, which the first destroy had already
freed. The wire is down once teardown starts, which is what an application shutting down
finds too.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
* The connect token entry fields say what they are
The entry time is created-at, not last-seen, and the expire timestamp is the connect token's,
which is what bounds the entry's life. Both read as ordinary bookkeeping without that.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: BUILDING.md
+11-2Lines changed: 11 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -35,8 +35,15 @@ By default netcode builds as a static library against the vendored libsodium sub
35
35
cmake --install build --prefix /some/prefix
36
36
37
37
-`NETCODE_SYSTEM_SODIUM=ON` links the system libsodium instead of the vendored copy (they are interchangeable — the vendored subset is a byte-identical slice of upstream).
38
-
-`BUILD_SHARED_LIBS=ON` builds `libnetcode` as a shared library.
39
-
-`cmake --install` installs `netcode.h` and the library (`NETCODE_INSTALL=OFF` disables the install target, e.g. when embedding netcode as a subproject).
38
+
-`BUILD_SHARED_LIBS=ON` builds `libnetcode` as a shared library. Shared builds are not supported on Windows: `netcode.h` declares no export macro, so a DLL built from these sources exports nothing, and CMake refuses the combination. On Windows link the static library or compile `netcode.c` into your application.
39
+
-`cmake --install` installs `netcode.h`, the library, and a CMake package. In the default vendored build the sodium objects are compiled into `libnetcode`, so the installed library is self contained; the system libsodium build exports the libsodium it found instead.
pointing CMake at the install prefix with `-DCMAKE_PREFIX_PATH=/some/prefix`. `NETCODE_INSTALL=OFF` disables the install target, e.g. when embedding netcode as a subproject.
40
47
41
48
## Floating point: netcode builds with -ffp-contract=off
42
49
@@ -65,6 +72,8 @@ To build everything with AddressSanitizer and UndefinedBehaviorSanitizer, config
65
72
66
73
Fuzz harnesses for the untrusted-input surface live in `fuzz/` and are built with `-DNETCODE_FUZZ=ON`. See [fuzz/README.md](fuzz/README.md) for details.
67
74
75
+
`-DNETCODE_NONCE_AUDIT=ON` builds the test runner with the key and nonce of every packet it encrypts recorded, and adds a test that fails if any pair repeats. It is a test-only option: nothing it adds is compiled into the library.
76
+
68
77
## Building on Windows
69
78
70
79
You need Visual Studio to build the source code. If you don't have Visual Studio you can [download the community edition for free](https://visualstudio.microsoft.com/downloads/).
@@ -69,6 +69,17 @@ option(NETCODE_FUZZ "Build the fuzz targets" OFF)
69
69
option(NETCODE_BUILD_TESTS"Build netcode tests and examples"${NETCODE_TOP_LEVEL})
70
70
option(NETCODE_SYSTEM_SODIUM"Link against the system libsodium instead of the vendored copy"OFF)
71
71
option(NETCODE_INSTALL"Generate the install target (netcode.h and the netcode library)"ON)
72
+
option(NETCODE_NONCE_AUDIT"Record the key and nonce of every packet the tests encrypt and fail on a repeat"OFF)
73
+
74
+
# netcode.h declares no export macro, so a Windows DLL built from these sources exports
75
+
# nothing and every consumer fails to link. Static is the supported shape on Windows: link
76
+
# the static library, or compile netcode.c straight into your application.
77
+
78
+
if(BUILD_SHARED_LIBSANDWIN32)
79
+
message(FATAL_ERROR"netcode does not support shared library builds on Windows. Build the static library (the default), or compile netcode.c into your application.")
80
+
endif()
81
+
82
+
include(GNUInstallDirs)
72
83
73
84
# sanitizers apply to the whole build. the vendored crypto is exempted from UBSan
74
85
# below (third-party SIMD code uses intentional type punning / unaligned access that
@@ -102,7 +113,7 @@ else()
102
113
# vendored libsodium subset, amalgamated into a single header + source pair.
103
114
# see sodium/NOTES.md for how it is generated and validated.
struct netcode_server_t * server = netcode_server_create( server_address, &server_config, time );
@@ -56,6 +57,8 @@ if ( !server )
56
57
}
57
58
```
58
59
60
+
`max_connect_token_lifetime` is the longest lifetime in seconds your backend issues connect tokens with. The server refuses any connect token that could have been issued before it started, and this is how it knows which those are, so set it to the lifetime your backend uses.
61
+
59
62
Then start the server with the number of client slots you want:
0 commit comments