aa141517a3
The RTP seq/timestamp extraction in input_udp_recv was disabled since
be32351 (2020) with a "TODO: Figure out why this does not work" comment.
The root cause was a buffer offset bug: recvfrom writes the UDP payload
at recv_buf + ipheader_bytes, but the extraction code read recv_buf[2..7]
— the reserved IP-header prefix area — returning garbage.
872 lines
40 KiB
Plaintext
872 lines
40 KiB
Plaintext
librist - Reliable Internet Stream Transport
|
|
============================================
|
|
|
|
A library implementing the Video Services Forum (VSF) Technical
|
|
Recommendations TR-06-1 (Simple Profile) and TR-06-2 (Main Profile).
|
|
|
|
|
|
Changes for 0.2.16 (pre-release):
|
|
---------------------------------
|
|
|
|
ABI/API:
|
|
- ABI version 10:4:6 (soversion 4, binary-compatible with 0.2.15)
|
|
- API version 4.7.0 (unchanged)
|
|
|
|
Maintenance release for 0.2.15. No source or binary changes are
|
|
required of downstream consumers.
|
|
|
|
Bug Fixes:
|
|
|
|
Windows:
|
|
- Windows tools fail to start when remote logging is enabled.
|
|
Running `ristreceiver -r 127.0.0.1:port ...` on Windows 11 would
|
|
exit immediately with "Failed to setup logging!" because winsock
|
|
had not been initialised before the public logging API tried to
|
|
open a UDP socket. Fixes #208 (reported by Roman Dissertori).
|
|
- EAP-SRP authentication fails on Windows with multiple peers.
|
|
A 0.2.15 regression: an outbound packet that drew an ICMP
|
|
port-unreachable reply caused Windows to corrupt the next
|
|
incoming UDP datagram on the same socket, which then broke the
|
|
EAP handshake. Most visible with bonded ethernet+wifi peers,
|
|
Linux ristsender -> Windows ristreceiver. Fixes #209 (reported
|
|
and confirmed fixed by Roman Dissertori).
|
|
- Windows socket failure log lines now include the actual WSA
|
|
error code, so user bug reports from Windows are diagnosable.
|
|
- Cryptographic random number generator now seeds reliably on
|
|
Windows runtimes where the legacy CryptoAPI (CryptAcquireContext
|
|
/ CryptGenRandom) is missing or unconfigured - notably wine and
|
|
some sandboxed container builds. The seeding path now uses the
|
|
modern BCryptGenRandom (CNG) API, supported on every shipping
|
|
Windows version. Without this fix, the 0.2.15 CSPRNG hardening
|
|
refused to start the SRP/AES code paths in those environments
|
|
rather than silently downgrade. Fixes #210.
|
|
|
|
Tools:
|
|
- ristsender now preserves incoming RTP sequence numbers and
|
|
timestamps when the --rtp-sequence and --rtp-timestamp options
|
|
are used. Previously these options had no effect.
|
|
|
|
Hardening / follow-ups to 0.2.15:
|
|
|
|
- EAP-SRP authentication retry exhaustion did not drop the failed
|
|
peer. A latent sign-comparison bug meant a peer that failed
|
|
every authentication retry was kept around in a half-
|
|
authenticated state instead of being dropped. Not externally
|
|
reported.
|
|
- Crypto random number generator could silently downgrade to a
|
|
weak fallback. If the CSPRNG seed call failed at startup (no
|
|
kernel entropy source - sandboxed container, embedded image
|
|
without /dev/urandom, broken build), the failure was dropped
|
|
and the wall-clock-derived stopgap was used for the lifetime of
|
|
the process. Now logs loudly and refuses to hand out random
|
|
bytes. Not externally reported.
|
|
- Internal EAP error code documentation: clarify a guard comment
|
|
that read as a removable paranoia check but is actually
|
|
protecting against a NULL function pointer.
|
|
|
|
Post-0.2.15 audit follow-ups (reported by Thomas Guillem):
|
|
|
|
Receiver flow / peer accounting:
|
|
- Receivers no longer crash or corrupt their peer list when a
|
|
peer is added to a flow at the same instant another thread is
|
|
removing one. The list of peers per flow was being walked by
|
|
one thread under one lock and modified by another thread under
|
|
a different lock; the two paths now agree. A 256-peer-per-flow
|
|
cap is also enforced so a misconfigured (or hostile) sender
|
|
cannot grow the list without bound.
|
|
- Receivers stop creating flows once 256 active flows already
|
|
exist. Previously the count was checked and the new flow
|
|
allocated in separate steps, so a burst of new flow IDs could
|
|
race past the limit. Now the count check and the slot insert
|
|
happen under the same lock.
|
|
|
|
EAP-SRP authenticator:
|
|
- Authenticated peers can no longer be deauthenticated by a
|
|
spoofed EAPOL-LOGOFF. A successfully authenticated session
|
|
used to reset to start-over state on receipt of any LOGOFF
|
|
packet, which made the auth state machine trivially DoSable
|
|
from off-path. LOGOFF is now only honoured before the session
|
|
reaches authenticated state, and a legitimate LOGOFF before
|
|
that point also resets the retry counter so a counter
|
|
wrap-around can't lock the peer out for the lifetime of the
|
|
process.
|
|
- A spoofed EAP FAILURE no longer pushes the local state machine
|
|
into permanent failure. FAILURE packets are now matched
|
|
against the identifier of the in-flight EAP exchange and
|
|
dropped if they don't match, the same way Request/Response are
|
|
matched. A genuine FAILURE still latches the peer out, but
|
|
after a 30-second quiet period the peer is allowed to retry
|
|
from the start so a transient burst of bad responses doesn't
|
|
require restarting the process.
|
|
- EAP authenticators no longer answer EAP Identity requests
|
|
themselves. The handler used to run on both roles, so a
|
|
misbehaving (or attacker-controlled) authenticatee could
|
|
persuade the authenticator to discard its own session state
|
|
and start over. The handler is now restricted to the
|
|
authenticatee role, and even there it is rate-limited so the
|
|
pre-auth reset cannot be triggered repeatedly to wipe
|
|
in-progress sessions.
|
|
- EAP SRP setup now caps the advertised modulus (N) and
|
|
generator (g) lengths at 1024 bytes (8192-bit groups, the
|
|
largest standard RFC 5054 group). A peer that advertised
|
|
multi-megabyte values used to allocate that much memory and
|
|
burn that much CPU on bignum imports before deciding the
|
|
handshake was bogus.
|
|
|
|
PSK encryption:
|
|
- The PSK nonce generator no longer falls back to a
|
|
wall-clock-derived value when the CSPRNG misbehaves. Nonces
|
|
are required to come from the system CSPRNG; if it fails or
|
|
returns suspect output the key is marked broken, encryption
|
|
refuses to produce ciphertext for that key (sends zeroes
|
|
instead so a downstream consumer cannot mistake it for a
|
|
successful encrypt), and the existing audible logging fires.
|
|
Decryption was already locked out in the same condition; this
|
|
closes the encrypt-side gap. The non-security-critical
|
|
`prand_u32` helper used elsewhere in the tree keeps its
|
|
wall-clock fallback because its callers don't need
|
|
unpredictability.
|
|
- PSK decryption now skips the PBKDF2 key derivation when the
|
|
key is already locked out. A bursty stream of malformed
|
|
encrypted packets at a peer whose key state was already
|
|
compromised used to spin tens of thousands of PBKDF2 rounds
|
|
per packet before rejecting it. The lockout check now happens
|
|
first, so the per-packet cost drops to a few comparisons.
|
|
- PSK AES-CTR dispatch no longer silently falls back to AES-128
|
|
on an unknown key size. The earlier switch-case used a
|
|
fallthrough that meant a corrupted or unsupported key length
|
|
would still be processed (with the wrong cipher), which
|
|
masked configuration errors. Unknown key sizes now return
|
|
without touching the buffer.
|
|
|
|
SRP / cryptographic primitives:
|
|
- The Nettle build of SRP no longer feeds uninitialized stack
|
|
into the bignum library when the CSPRNG returns no entropy.
|
|
The Nettle random callback's signature returns void, so a
|
|
CSPRNG failure was previously dropped on the floor and the
|
|
caller computed against whatever was on the stack. The
|
|
callback now zeroes its output buffer and signals the caller
|
|
via a side-channel context; the bignum operation then fails
|
|
cleanly. The mbedTLS build was already correct.
|
|
- The SRP NG (N/g) group identifier enum now keeps its
|
|
pre-0.2.15 integer values. The 0.2.15 release deleted the two
|
|
smallest groups (NG_512, NG_768 - both below current
|
|
minimum-strength thresholds) but shifted every remaining
|
|
enum entry down by two as a side effect. The integer "2",
|
|
which any external caller might reasonably have hardcoded
|
|
for NG_1024, silently started meaning NG_4096. The deleted
|
|
values 0 and 1 are now explicit reserved slots that return
|
|
"no such group" - code that uses the named constants
|
|
(`LIBRIST_SRP_NG_1024` etc.) is unaffected, code that used
|
|
literal integers either gets the group it asked for or a
|
|
clean error.
|
|
|
|
Statistics JSON:
|
|
- The stats JSON payloads (sender flow, sender per-peer wrapper,
|
|
receiver flow) now carry an explicit top-level
|
|
`schema_version` field. The current value is 2 - version 1 was
|
|
the duplicate-`peer`-key sender shape that v0.2.15 replaced
|
|
with a `peers` array (NEWS noted the change, but downstream
|
|
parsers had no way to detect which shape they were getting).
|
|
Any future incompatible shape change must bump this number.
|
|
- The sender `cname` field copy is now explicitly
|
|
NUL-terminated. The previous strncpy with a destination-sized
|
|
length depended on a separate bounds check elsewhere in the
|
|
receive path to keep the result safe; the contract is now
|
|
self-contained.
|
|
|
|
Build / portability:
|
|
- librist now requires mbedTLS >= 2.7 (March 2018), and emits a
|
|
clean #error if built against anything older. The compatibility
|
|
branch for pre-2.7 mbedTLS releases (which used the
|
|
void-returning SHA-256 API) had no CI coverage and was the
|
|
same untestable corner that produced a 0.2.14 build break -
|
|
dropping it removes the surface area entirely. Every still-
|
|
supported distro is well past 2.7; the bundled tree is 2.28.x.
|
|
- On Windows, the mbedTLS CSPRNG seed no longer reaches into
|
|
private mbedtls_entropy_context fields to keep its source
|
|
count happy. It now seeds the CTR_DRBG directly from
|
|
BCryptGenRandom, skipping the entropy module entirely on
|
|
Windows. Non-Windows builds still use the normal entropy
|
|
initialisation path.
|
|
|
|
Test / CI:
|
|
- New unit test (logging_init_order) that exercises the
|
|
rist_logging_set remote-address path before any rist_*_create
|
|
call. Would have caught #208 on the first Windows-CI run.
|
|
- The full meson test suite now runs against the cross-compiled
|
|
Windows binaries under wine, not just on Linux. Every protocol
|
|
test that gates Linux releases also gates Windows releases,
|
|
including the SRP / AES encryption suites which are now built
|
|
with mbedTLS on Windows (see the #210 fix above). The wine
|
|
runtime was already provisioned in the CI image; only the test
|
|
invocation was missing. Catches Windows-runtime regressions
|
|
without requiring a native Windows runner.
|
|
- Per-test results from the Linux job now show up directly in the
|
|
GitLab MR UI via meson's JUnit output, so reviewers don't have
|
|
to read job logs to see which test failed.
|
|
|
|
|
|
Changes for 0.2.15 (2026-05-14):
|
|
--------------------------------
|
|
|
|
ABI/API:
|
|
- ABI version 10:3:6 (soversion 4, binary-compatible with 0.2.14)
|
|
- API version 4.7.0 (unchanged)
|
|
|
|
Security release. Existing 0.2.14 installations remain
|
|
binary-compatible; downstream consumers do not need to recompile.
|
|
|
|
Security Fixes:
|
|
|
|
Receive-side parsers and key handling were audited end to end. Each
|
|
fix below is independent.
|
|
|
|
Receive path / RTCP:
|
|
- rist-common: bounds-check the RTP and RTP-extension headers.
|
|
The earlier check only covered the 4-byte reduced port subheader;
|
|
a truncated packet underflowed payload.size to ~SIZE_MAX which
|
|
then drove a malloc(len + 32) on the receiver enqueue path.
|
|
- rist-common: harden the RTCP dispatcher. Off-by-one in
|
|
bytes_left, integer overflow in 4*(1+records) (CPU spin on a
|
|
single packet), and an SDES handler that let 255 bytes be
|
|
memcpy'd into a 128-byte struct member, overrunning the adjacent
|
|
peer config/key state.
|
|
- rist-common: bound the XR block parser, clamp the on-the-wire
|
|
length to what we received, and guard the DLRR delay subtraction
|
|
so it can no longer pin last_rtt near UINT64_MAX (which would
|
|
permanently disable NACK timing on the peer).
|
|
- rist-common: validate the sender NACK record count before
|
|
iterating. ntohs(rtcp->len) - 2 underflowed to ~65K on a
|
|
malformed feedback packet, enqueueing OOB bytes as retransmit
|
|
requests and leaking them back to the peer.
|
|
- rist-common: add the missing return after the VSF length check,
|
|
and derive odd_nonce from the actual nonce byte rather than
|
|
GRE flags1 (the latter silently disabled the OTF passphrase
|
|
rotation feature on the receive side).
|
|
- rtcp: guard the RR and echo-response RTT subtractions and
|
|
size-check the SR / echo-request / echo-response casts (same
|
|
RTT-underflow primitive as above on the RR/echo paths).
|
|
- rtcp: correct the XR echoreq RTCP length field per RFC 3550;
|
|
receivers were reading 4 bytes past the actual XR block.
|
|
- mpegts: bound NPD expansion to the 7-packet wire encoding.
|
|
expand_null_packets used attacker-controlled payload_len and
|
|
npd_bits to drive ts_count and write up to 11 KB into a 10 KB
|
|
recv_npd buffer (overrunning into the adjacent RTCP TX buffer),
|
|
and shifted by a negative amount when ts_count > 7.
|
|
- gre: zero-init keepalive info on short packet and check return,
|
|
otherwise a truncated keepalive disclosed stack memory through
|
|
the user logging callback.
|
|
- udpsocket: NUL-terminate the URL hostname after strncpy so
|
|
udpsocket_resolve_host cannot over-read.
|
|
|
|
PSK / AES-CTR:
|
|
- crypto: reject zero-nonce PSK packets instead of skipping
|
|
decryption. The previous early-return left the recv buffer
|
|
untouched and the caller treated the attacker's plaintext as a
|
|
freshly-decrypted Main Profile packet, bypassing the configured
|
|
PSK per packet. Pre-auth, single packet.
|
|
- crypto: correct nettle AES-CTR dispatch for 128/192-bit keys.
|
|
AES-128 was using nettle_aes192_encrypt against an aes128
|
|
context; the AES-192 case was missing entirely, leaving an
|
|
uninitialised function pointer hot on the EAP password-rotation
|
|
path.
|
|
|
|
EAP / SRP:
|
|
- eap: validate EAPOL/EAP/SRP-TLV lengths end to end. The body
|
|
length check was inverted and inner dispatchers walked past the
|
|
recv buffer with attacker-supplied lengths, exfiltrating heap
|
|
bytes through username copies and AES-CTR rewrites. Adjacent
|
|
fixes cover EAP header / dispatch / passphrase-flags reads.
|
|
- eap: NULL-check auth_ctx and propagate handle_A failure (notably
|
|
A == 0 mod N) so a rejected SRP exchange actually fails auth
|
|
instead of sending an uninitialised B back to the client. NULL
|
|
check the SRP client ctx and propagate the int-typed return of
|
|
the A/B writers (a -1 wrapped to ~SIZE_MAX and crashed on the
|
|
last_pkt malloc cache path).
|
|
- eap: bound rist_eap_send_passphrase to the destination buffer;
|
|
an over-long passphrase from the API or YAML config overran
|
|
ctx->unsollicited_passphrase into adjacent heap fields used by
|
|
the periodic retransmit.
|
|
- eap: rate-limit the EAP_CODE_FAILURE restart loop. The handler
|
|
unconditionally called eap_reset_data + log + EAPOL_START with
|
|
no retry counting, so a spoofed FAILURE per round-trip kept us
|
|
bouncing through reset+restart forever.
|
|
- eap: write the empty server-name placeholder into outpkt rather
|
|
than pkt (pure copy/paste typo that corrupted the recv buffer
|
|
on every successful identity response).
|
|
- srp: propagate failure when the client A, server B, or u is
|
|
congruent to 0 mod N. The checks existed but the failure path
|
|
left ret==0 so the caller continued the exchange. Auth bypass
|
|
primitive on both sides.
|
|
|
|
PRNG:
|
|
- crypto: route prand_u32 through the CSPRNG. The previous
|
|
implementation called rand() seeded with a wall-clock NTP
|
|
timestamp; the result is used for the PSK GRE nonce (input to
|
|
PBKDF2), the EAP identifier and the peer SSRC. Reuse the mbedTLS
|
|
CTR-DRBG / GnuTLS RNG that already backs SRP key generation.
|
|
- crypto: fix operator-precedence bug in random_get_string and
|
|
add an upper bound on the local rand buffer. Affected callers
|
|
that ask for a runtime-derived password.
|
|
|
|
Hygiene (not security-impacting on their own but shipped in the same
|
|
release):
|
|
- flow: handle realloc/calloc failure on the peer-list growth path
|
|
and rist_receiver_missing; correct two log format strings
|
|
(uint32_t printed with PRIu64; pointer where a PRIu32 was due).
|
|
- libevsocket: preserve the global context list when removing the
|
|
head; ctx_del previously cleared CTX_LIST instead of advancing
|
|
it to c->next.
|
|
- eap, logging, udpsocket: NULL-check the malloc/calloc results
|
|
that were dereferenced unconditionally; free the calloc'd ctx
|
|
on pthread_mutex_init failure in eap_clone_ctx; close the bound
|
|
socket on the multicast-join failure leg.
|
|
- network: on the GNU/Hurd MAC-address path, check socket() and
|
|
close it on the getifaddrs() failure leg.
|
|
|
|
Additional fixes from the VideoLAN security audit:
|
|
|
|
Independent receive-side / EAP / SRP audit shared by VideoLAN.
|
|
The findings already covered above are not re-listed; the items
|
|
below close the residual gaps.
|
|
|
|
EAP / SRP:
|
|
- eap: ignore EAP_REQUEST_IDENTITY once we're past
|
|
EAP_AUTH_STATE_SUCCESS so a single forged identity request can
|
|
no longer tear an established session down and re-emit the
|
|
configured username on the wire.
|
|
- eap: refuse RESPONSE/IDENTITY on the authenticatee side
|
|
(lookup_func is NULL there per the calloc setup path; the
|
|
handler used to deref it unconditionally).
|
|
- eap: free verifier_data on every exit from
|
|
process_eap_response_identity. Three error paths returned -1
|
|
before reaching the cleanup block, leaking the four heap
|
|
allocations the verifier lookup callback handed back.
|
|
- srp: jump to failed: instead of returning out of
|
|
BIGNUM_WRITE_BYTES when MPI write fails inside calc_x; the
|
|
return -1 path leaked hash_data.
|
|
- srp: check librist_crypto_srp_hash_bignum return in
|
|
calculate_m. The helper returns -1 (and leaves the output
|
|
buffer untouched) when the bignum exceeds its 1024-byte
|
|
staging buffer; the unchecked path XOR'd uninitialised stack
|
|
into the SRP transcript.
|
|
- srp: BIGNUM_INIT k and B in the authenticator constructor.
|
|
Relying on calloc-zeroed mbedtls_mpi state happened to work
|
|
today but the public mbedTLS contract requires explicit init.
|
|
- srp: bound A and B operand sizes against N before exp_mod.
|
|
handle_A and client_handle_B accepted any length from the
|
|
wire; mbedtls_mpi_exp_mod runtime grows with operand size.
|
|
- srp: NULL the caller's *bytes_s/*bytes_v in _create_verifier
|
|
failure paths so eap_reset_data does not double-free them.
|
|
- srp: capture mbedtls_mpi_fill_random return when generating
|
|
the salt; the previous `if (ret != 0)` re-checked the prior
|
|
BIGNUM_FROM_STRING.
|
|
- srp: repair the !USE_SHA_RET branch of librist_crypto_srp_hash
|
|
(referenced symbols from a different function and was missing
|
|
its trailing semicolon).
|
|
- srp: drop the sub-1024-bit RFC 5054 groups (NG_512, NG_768).
|
|
The enum was internal; the unit-test fixture that wanted the
|
|
deterministic 512-bit exchange now inlines the constants.
|
|
|
|
PSK / AES-CTR:
|
|
- psk: fail closed when PBKDF2 setup fails on the mbedTLS path.
|
|
mbedtls_md_setup / mbedtls_pkcs5_pbkdf2_hmac errors used to
|
|
fall through into mbedtls_aes_setkey_enc with the
|
|
stack-allocated aes_key buffer in undefined state.
|
|
- psk: don't let an attacker-chosen nonce reset the bad-packet
|
|
lockout. _librist_crypto_psk_decrypt now keeps bad_decryption
|
|
set across nonce rolls so the five-bad-packets cap is no
|
|
longer trivially bypassed by sending a different nonce on
|
|
every shot.
|
|
- psk: use memcpy for the GRE nonce load instead of a punned
|
|
uint32_t cast (strict-aliasing UB; faulted on architectures
|
|
that require aligned uint32_t loads).
|
|
|
|
Receive path:
|
|
- rist-common: lift the rist_rtp_hdr size check above the
|
|
Simple/REDUCED conditional so Main-profile FULL/EAPOL paths
|
|
can no longer read past recv_bufsize.
|
|
- udp: size the NPD scratch buffer for the real worst case
|
|
(7 * 204 + sizeof(rist_rtp_hdr_ext)). The previous 6 * 204 + 4
|
|
was four bytes short for the (count == 7, suppressed == 1)
|
|
output and let suppress_null_packets smash four bytes of
|
|
stack past tmp_buf.
|
|
- rist-common: cast keepalive json_len to int for %.*s. size_t
|
|
via varargs is undefined per the C standard.
|
|
|
|
Flow / threading:
|
|
- flow: take f->mutex when growing peer_lst; the realloc could
|
|
move storage and concurrent walkers (rist_best_rtt_index, the
|
|
output thread, stats) read peer_lst without synchronization.
|
|
- flow: cap receiver flows at RIST_MAX_FLOWS (256) and walk
|
|
FLOWS under flows_lock. Without a cap an attacker that can
|
|
land traffic on a receiver can exhaust memory by walking the
|
|
32-bit flow_id space.
|
|
- flow: NULL-check the dataout_fifo_queue calloc; the
|
|
data-output thread used to write into it unconditionally.
|
|
|
|
TUN / RTCP TX / utility:
|
|
- tun_linux, tun_darwin: bounds-check prefix_len before computing
|
|
the netmask. prefix_len < 0 hit undefined behaviour in C and
|
|
prefix_len == 32 produced a 32-bit shift on a uint32_t (also
|
|
UB).
|
|
- tun_darwin: parse the utun unit suffix with strtoul, not atoi,
|
|
with full validation; falls back to "let the kernel pick" on
|
|
parse failure.
|
|
- rtp: clamp SDES name_len to 255 before computing sdes_size so
|
|
the on-the-wire length byte and the actual bytes copied stay
|
|
in agreement.
|
|
- rist-common: make compare() return a real three-way result
|
|
(qsort's contract is < 0 / 0 / > 0; the previous boolean cast
|
|
was undefined per POSIX).
|
|
- rist-common: pick the actual median in recalculate_clock_offset
|
|
(the index was biased high by one).
|
|
|
|
Bug Fixes:
|
|
- rist-common: initialize peer->sd to -1 in peer_initialize so a
|
|
DNS resolution failure no longer leaves a zero socket
|
|
descriptor wired up to the event loop, which on Windows turned
|
|
into an infinite POLLERR loop and on Unix could attach the RIST
|
|
state to fd 0 (stdin). (!312, Yannick Le Roux)
|
|
- logging: check log_socket >= 0 instead of just non-zero in the
|
|
rist_log_impl UDP path, matching the LOGGING_SETTINGS_INITIALIZER
|
|
sentinel (the early-exit was tightened in f7ace4d but the send
|
|
branch still treated 0 as a valid fd). (!313, Daisuke Matsunami)
|
|
- stats: emit sender peers under a "peers" array. The previous
|
|
shape used "peer" as the key for every peer object, producing
|
|
invalid JSON and silently dropping all but one peer in strict
|
|
parsers. (#206, Manuel Alejandro)
|
|
|
|
Tools:
|
|
- prometheus-exporter: implement rist_prometheus_parse_sender_stats.
|
|
The hook for ristsender's JSON stats callback was a TODO stub, so
|
|
sender-side rist_sender_peer_* gauges/counters never showed up at
|
|
the scrape endpoint. The parser now walks the new "peers" array
|
|
and feeds rist_prometheus_handle_sender_peer_stats; metric names
|
|
and shape are unchanged from the receiver-callback path.
|
|
(closes the second half of #206)
|
|
- ristsender: drop a duplicate rist_sender_stats_callback_set call
|
|
in setup_rist_peer that overwrote the per-instance arg with NULL,
|
|
breaking sender attribution when more than one sender ran in the
|
|
same process.
|
|
- udp2udp: log a one-shot warning when --metrics is enabled noting
|
|
that the relay produces no rist_* series (it uses librist only
|
|
for URL parsing and the httpd shell).
|
|
|
|
Windows / MinGW Build:
|
|
- rist.c: return integer (not NULL) from the PTHREAD_START_FUNC
|
|
bodies. The macro expands to a DWORD __stdcall function on
|
|
Windows without HAVE_PTHREADS, which gcc14 rejected.
|
|
- meson: always probe clock_gettime via has_header_symbol on
|
|
Windows, not only when have_mingw_pthreads=true. Modern
|
|
mingw-w64 ships clock_gettime as a static inline in <time.h>
|
|
regardless of winpthreads, so contrib/time-shim.c collided
|
|
with the toolchain definition on the default build.
|
|
- rist-common: drain WSAECONNRESET with a real recvfrom buffer
|
|
on the rist_peer_sockerr POLLERR path. The previous NULL-buffer
|
|
call returned WSAEFAULT and left the indication in the socket
|
|
queue, causing WSAPoll to fire POLLERR in a tight loop and
|
|
flood the log with "Socket error!" whenever a peer went away.
|
|
(#205, Manuel Alejandro)
|
|
- tools: centralize the strtok_r/strtok_s shim in a header so
|
|
rist2rist (and any future tool that needs strtok_r) builds on
|
|
MinGW / MSVC. ristsender and ristreceiver had inline shims;
|
|
rist2rist's recent multi-peer bonding work added strtok_r
|
|
without one and failed to link. (#207, Thierry Lelegard)
|
|
|
|
Changes for 0.2.14 (2026-04-25):
|
|
--------------------------------
|
|
|
|
ABI/API:
|
|
- ABI version 10:2:6 (soversion 4, binary-compatible with 0.2.13)
|
|
- API version 4.7.0 (unchanged)
|
|
|
|
Maintenance release. No public API or ABI signature changes.
|
|
Existing 0.2.13 installations remain binary-compatible; downstream
|
|
consumers do not need to recompile and relink.
|
|
|
|
Bug Fixes:
|
|
|
|
- fix(sender): serialize retry-queue dequeue under peerlist_lock
|
|
to avoid a use-after-free. The retry-queue cleanup path could
|
|
race with peer teardown and dereference a peer that had already
|
|
been freed on a sibling thread, occasionally producing a sender
|
|
crash under sustained NACK pressure with multi-peer
|
|
configurations. The dequeue is now performed with the peerlist
|
|
lock held for the full critical section.
|
|
- fix(macos): honor the -1/errno contract in the time-shim and
|
|
zero-initialize the timespec before calling clock_gettime_osx.
|
|
rist__get_system_boottime() previously returned the errno value
|
|
as its result instead of -1, which silently produced corrupted
|
|
NTP timestamps on macOS in restricted-syscall environments
|
|
(containers, sandboxes that block sysctl(KERN_BOOTTIME)). The
|
|
function now returns sysctl()'s actual return value, and
|
|
timestampNTP_u64() / timestampNTP_RTC_u64() defensively
|
|
zero-init their timespec so a clock_gettime failure can no
|
|
longer leak uninitialized stack into the time arithmetic and
|
|
falsely trip the "stream is dead" timeout.
|
|
|
|
Windows / MSVC / MinGW:
|
|
|
|
- contrib/poll_win: fix the Windows poll() emulation busy-spinning
|
|
at 100% CPU when the watched fd set contains only sockets
|
|
(the common case for librist). The previous implementation fell
|
|
through to a 0 ms select() loop whenever no pipe/handle fds were
|
|
present; the timeout argument is now correctly honoured in the
|
|
sockets-only path.
|
|
- msvc: round out the contrib stdatomic shim and select native
|
|
C11 atomics where the toolchain supports them. Restores
|
|
correct atomic_uint_fast64_t behaviour on MSVC builds where
|
|
earlier shim coverage left some operations as plain reads /
|
|
writes, which (under heavy contention) could let the receiver
|
|
flow's last_pkt_received drift and produce false stream-dead
|
|
events on Windows.
|
|
|
|
Tools:
|
|
|
|
- tools/rist2rist: add multi-peer bonding (multiple input or
|
|
output URLs on the command line), MPEG-TS Null Packet Deletion
|
|
(NPD), and Prometheus metrics export. rist2rist can now serve
|
|
as a load-balancing relay sitting close to the source of truth,
|
|
where small recovery buffers and large retry queues let
|
|
downstream receivers absorb network impairments without
|
|
inflating end-to-end latency.
|
|
|
|
Contrib (bundled mbedtls):
|
|
|
|
- contrib/mbedtls: refresh bundled source from 2.26.0 to 2.28.10
|
|
(last LTS release of the 2.x series, 2025-03-24). 2.28.x is
|
|
strictly ABI-compatible with 2.26.x, so no librist code change
|
|
was required. Notable mbedtls fixes pulled in:
|
|
* CVE-2025-27809 (TLS server impersonation via missing
|
|
mbedtls_ssl_set_hostname)
|
|
* CVE-2025-27810 (TLS 1.2 Finished message corruption on
|
|
allocator/HW failure)
|
|
* CVE-2024-45157 (PSA HMAC_DRBG selection regression)
|
|
* Multi-year accumulation of fixes in AES, ECP, MPI, RSA,
|
|
PK, PKCS5, bignum and the new constant_time.c
|
|
side-channel-hardened primitives.
|
|
librist itself only consumes the classic mbedtls crypto API
|
|
(AES-CTR, CTR-DRBG, entropy, SHA-256, PBKDF2, MPI), so the
|
|
upgrade is a drop-in replacement for the bundled path. The
|
|
system-mbedtls build path (-Dbuiltin_mbedtls=false, the
|
|
default when a system libmbedcrypto is detected) is not
|
|
touched by this commit.
|
|
- contrib/mbedtls: widen the hardclock gate from defined(_MSC_VER)
|
|
to defined(_WIN32) in the bundled timing.c, so MinGW and
|
|
Clang-Windows builds now also pick up the
|
|
QueryPerformanceCounter implementation of
|
|
mbedtls_timing_hardclock().
|
|
|
|
Contributors:
|
|
- Sergio Ammirata
|
|
|
|
|
|
Changes for 0.2.13 (2026-04-18):
|
|
--------------------------------
|
|
|
|
ABI/API:
|
|
- ABI version 10:1:6 (soversion 4, binary-compatible with 0.2.12)
|
|
- API version 4.7.0 (unchanged)
|
|
|
|
Build Fixes:
|
|
|
|
Platform build-fix release. No changes to the RIST protocol, public
|
|
API, or ABI signatures. All existing 0.2.12 installations remain
|
|
binary-compatible; downstream consumers do not need to recompile.
|
|
|
|
This release is only relevant if you build librist targeting one of
|
|
the platforms listed below. Native Linux, native macOS, FreeBSD,
|
|
and classic Win32 builds with MSVC are unaffected.
|
|
|
|
Windows desktop built with mingw-w64 (gcc or llvm-mingw):
|
|
- meson: detect clock_gettime with has_header_symbol
|
|
v0.2.12's probe used cc.has_function('clock_gettime'), which
|
|
does not resolve the static inline definition shipped by
|
|
mingw-w64's <pthread_time.h>. The resulting HAVE_CLOCK_GETTIME=0
|
|
caused contrib/time-shim.h to emit a conflicting extern
|
|
declaration and broke any downstream build with strict warnings
|
|
("redefinition of 'clock_gettime'"). Switching to
|
|
cc.has_header_symbol detects the inline correctly.
|
|
|
|
Windows UWP (i686/x86_64/aarch64-w64-mingw32 UWP toolchains):
|
|
- Also fixed by the has_header_symbol probe above.
|
|
- network.c: replace GetAdaptersInfo with GetAdaptersAddresses
|
|
_librist_network_get_macaddr() called GetAdaptersInfo, which is
|
|
not part of the UWP (WINAPI_FAMILY_APP) API surface;
|
|
llvm-mingw's UWP iphlpapi import library does not export it,
|
|
so UWP consumers of librist.a failed to link with "undefined
|
|
symbol: _GetAdaptersInfo". Replaced with GetAdaptersAddresses,
|
|
supported on classic Win32 desktop and on UWP since Windows 8.
|
|
|
|
Embedded Apple SDKs (iOS, tvOS, watchOS, visionOS, all archs):
|
|
- tun_darwin.c: build as stubs on non-macOS Apple platforms
|
|
The v0.2.12 TUN support was guarded by #ifdef __APPLE__, but
|
|
<sys/kern_control.h> and <net/if_utun.h> only ship with the
|
|
macOS SDK. Every non-macOS Apple cross-build failed with
|
|
"fatal error: 'sys/kern_control.h' file not found", and simply
|
|
gating off the file produced a later link error because
|
|
src/rist.c and src/rist-common.c call rist_tun_read /
|
|
rist_tun_write unconditionally. Pull in <TargetConditionals.h>,
|
|
restrict the utun implementation to
|
|
defined(__APPLE__) && TARGET_OS_OSX, and add a second
|
|
#elif defined(__APPLE__) branch with stub implementations of
|
|
the seven public rist_tun_* entry points returning -1, matching
|
|
the pattern src/tun_win.c already uses on Windows. Real utun
|
|
support on macOS is unchanged.
|
|
|
|
macOS used as a cross-build HOST:
|
|
- meson: skip Homebrew prefix lookup for cross-builds
|
|
The Homebrew fallback that adds /opt/homebrew or /usr/local
|
|
include paths was running unconditionally on any Darwin host.
|
|
Macs cross-compiling librist for iOS, tvOS, visionOS, Android,
|
|
or mingw could hang or fail at configure. Gated the lookup on
|
|
a native macOS build. Native-macOS-for-macOS builds are
|
|
unaffected.
|
|
|
|
Contributors:
|
|
- Sergio Ammirata
|
|
|
|
No source-file changes outside of meson.build, src/network.c, and
|
|
src/tun_darwin.c.
|
|
|
|
|
|
Changes for 0.2.12 (2026-04-17):
|
|
--------------------------------
|
|
|
|
ABI/API:
|
|
- ABI version 10:0:6 (soversion 4, binary-compatible with 0.2.11)
|
|
- API version 4.7.0
|
|
|
|
New Features:
|
|
- Cross-platform TUN support moved into the library
|
|
(rist_tun_open/close/read/write/set_ip/set_mtu/bring_up)
|
|
macOS (utun), Linux (/dev/net/tun), Windows (stub)
|
|
- data_fd forwarding API: rist_sender_data_fd_set,
|
|
rist_receiver_data_fd_set, rist_data_fd_stats_get
|
|
librist reads/writes directly to the fd, no application
|
|
code in the data path (works with sockets, FIFOs, TUN fds)
|
|
- New risttunnel tool: point-to-point IP tunnel over RIST
|
|
- YAML config file support for ristsender/ristreceiver with native parser
|
|
- Receiver session timeout API and --session-timeout CLI option
|
|
- Peer statistics in receiver output
|
|
- Multicast interface (miface) in sender peer statistics JSON
|
|
- avg_buffer_time field exposed in receiver_flow stats struct
|
|
- New udp2udp tool for UDP relay
|
|
- Profile selection (--profile) in rist2rist
|
|
- Sender input queue statistics
|
|
- MPEG-TS null packet suppression byte tracking
|
|
- Differentiated bitrate tracking and sender stats API
|
|
|
|
Bug Fixes:
|
|
- Fix sender queue full detection: replace wrap-unsafe index
|
|
arithmetic (which silently misfired on ring-buffer wraparound)
|
|
with a size-vs-max comparison. Prevents in-flight retransmit
|
|
buffers from being silently overwritten under sustained load.
|
|
- Fix OOB main-profile data delivery: rist_send_seq_rtcp no
|
|
longer strips the first 4 bytes of zero-header OOB packets,
|
|
and rist_oob_dequeue now iterates child peers when the OOB
|
|
buffer references a listener peer (matching main-channel
|
|
behaviour instead of failing silently).
|
|
- Fix receiver crash on configurable RTT multiplier path
|
|
- Fix bogus lost-packet counters
|
|
- Fix premature stream stop on ECHO timeout for single-path (closes #196)
|
|
- Fix multi-sender queue issues (static buffer_size moved to context)
|
|
- Thread-safe FIFO access in rist_receiver_data_read2
|
|
- Fix memory leaks (authentication failures, sender queue)
|
|
- Fix OpenMetrics response: add missing trailing EOF line
|
|
- Fix rist_client_flow_quality metric unit
|
|
- Fix OpenMetrics Content-Type header for Prometheus compatibility
|
|
- Fix RTT min/max invalid values
|
|
- Fix const correctness in URL parameter parsing
|
|
- Fix signed/unsigned comparison statements
|
|
- Fix missing strings.h include in yamlparse.c
|
|
- Fix clock used when processing RR with lsr older than last SR
|
|
- Fix -Wmisleading-indentation warning in receiver FIFO overflow path
|
|
- Fix tools build on macOS/Windows/FreeBSD: include endian-shim.h
|
|
for be16toh in ristsender.c and ristreceiver.c
|
|
- Fix tools build: always compile tunnel_interface/tun_mode into
|
|
rist_tools_config_object (consumers reference them unconditionally)
|
|
- Fix missing_counter bookkeeping: track queue length instead of
|
|
NACK-sent count. Previously the counter stayed at 0 on paths that
|
|
never emit NACKs (e.g. simple-profile multicast), causing the
|
|
missing_queue JSON stat to under-report and the congestion guard in
|
|
receiver_mark_missing to never trip.
|
|
- Fix build-only-platform-matching TUN source file; previously the
|
|
non-matching src/tun_*.c were compiled into an empty translation
|
|
unit and tripped -Werror=pedantic on Ubuntu/MinGW.
|
|
- Fix risttunnel build on MinGW: include <windows.h> for Sleep().
|
|
|
|
Improvements:
|
|
- Refactored sender queue management for better memory efficiency
|
|
- Improved UDP send reliability with EAGAIN retry
|
|
- Improved cname buffer safety
|
|
- Refactored flow timeout semantics
|
|
|
|
Build System:
|
|
- TUN support is now always compiled; removed the use_tun meson option
|
|
- CI: drop -Duse_tun=true flag from ubuntu build job
|
|
- Fix false positive mbedtls detection in meson build
|
|
- Use MbedTLS cmake config module
|
|
- Check brew exists before calling (macOS)
|
|
- Fix compiler warnings in aes.h (Steve Lhomme)
|
|
|
|
Acknowledgements:
|
|
- Dave Evans for RR clock fix
|
|
- Steve Lhomme for aes.h warning fixes
|
|
- Laur for the receiver FIFO misleading-indentation report
|
|
|
|
|
|
Changes for 0.2.11 (2024-11-15):
|
|
--------------------------------
|
|
|
|
New Features:
|
|
- Ephemeral listening ports support (add/remove ports after initialization)
|
|
- New function rist_sender_npd_get for null packet deletion status
|
|
- New function rist_peer_get_cname to extract peer's private cname
|
|
- Enhanced CI test application with packet integrity checks
|
|
|
|
Bug Fixes:
|
|
- Fixed potential socket deadlock condition under MS Windows
|
|
- Fixed ristsender confusion with multicast addresses sharing destination port
|
|
- Fixed compile warnings under different C++ versions
|
|
- Fixed theoretical IP checksum calculation issue
|
|
- Fixed ristreceiver closing when tun://@ used in output URL
|
|
- Fixed deadlock condition on FIFO overflow
|
|
- Fixed invalid verbose-level parameter handling
|
|
- Fixed binding to raw UDP socket
|
|
- Fixed memory allocation order in stats structures
|
|
- Fixed Mac homebrew arm64 build
|
|
- Fixed null packet deletion
|
|
- Fixed units on sender Prometheus stats
|
|
- Fixed keepalive packet data corruption bug
|
|
- Fixed improper RTP/RTCP classification (affected ST2110)
|
|
- Fixed flow timeout to follow session timeout
|
|
- Updated peer config timing_mode field handling
|
|
|
|
Build:
|
|
- Updated meson version to avoid deprecated items
|
|
|
|
Acknowledgements:
|
|
- Special thanks to Thierry Lelegard for null packet deletion bug fix
|
|
|
|
|
|
Changes for 0.2.10 (2023-10-30):
|
|
--------------------------------
|
|
|
|
- Development branch merge with accumulated fixes and improvements
|
|
|
|
|
|
Changes for 0.2.9 (2023-10-11):
|
|
-------------------------------
|
|
|
|
Bug Fixes:
|
|
- Fix compilation on 32-bit systems
|
|
- Fix regression in stats obj RTT value
|
|
- Remove unneeded locking in buffer scaling
|
|
- Fix "too old packages" error due to buffer scaling
|
|
- Fix empty buffer time check
|
|
- Disable buffer negotiation when sender max buffer < receiver buffer
|
|
- Fix deadlock caused by wrong lock order when removing peers
|
|
- Fix building Prometheus code against older libmicrohttpd
|
|
- Fix compilation on Hurd
|
|
|
|
|
|
Changes for 0.2.8 (2023-08-22):
|
|
-------------------------------
|
|
|
|
New Features:
|
|
- SRP (Secure Remote Password) passphrase exchange
|
|
- Passphrase changing support via U1 bit on M1/M2 packets
|
|
- Fully implemented passphrase changing on multicast
|
|
|
|
Bug Fixes:
|
|
- Add GRE check for bit 1 and GRE Ver set to 0
|
|
- Fix SIGSEGV when peer_rtcp pointer is null
|
|
|
|
Improvements:
|
|
- Refactored peer matching
|
|
- Refactored cryptographic randomness from SRP
|
|
- Added peer lock for thread safety
|
|
- Default PSK keysize to 256 if not set
|
|
|
|
|
|
Changes for 0.2.7 (2022-04-07):
|
|
-------------------------------
|
|
|
|
New Features:
|
|
- Multiplexing support
|
|
- TUN (tunnel) interface support
|
|
|
|
|
|
Changes for 0.2.6 (2021-07-22):
|
|
-------------------------------
|
|
|
|
Improvements:
|
|
- Move per-packet messages to debug level to prevent log flooding
|
|
- Release data earlier when real-time in buffer exceeds 1.1x buffer duration
|
|
- Drop stale packets older than 2x buffer duration
|
|
|
|
|
|
Changes for 0.2.5 (2021-07-20):
|
|
-------------------------------
|
|
|
|
- Maintenance release with minor fixes
|
|
|
|
|
|
Changes for 0.2.4 (2021-06-25):
|
|
-------------------------------
|
|
|
|
- Maintenance release with stability improvements
|
|
|
|
|
|
Changes for 0.2.3 (2021-06-22):
|
|
-------------------------------
|
|
|
|
- Maintenance release with bug fixes
|
|
|
|
|
|
Changes for 0.2.2 (2021-06-16):
|
|
-------------------------------
|
|
|
|
- Maintenance release
|
|
|
|
|
|
Changes for 0.2.1 (2021-06-10):
|
|
-------------------------------
|
|
|
|
- Stability fixes for initial public release
|
|
|
|
|
|
Changes for 0.2.0 (2021-05-12):
|
|
-------------------------------
|
|
|
|
Initial official public release of librist.
|
|
|
|
Core Features:
|
|
- Full support for VSF TR-06-1 (Simple Profile)
|
|
- Partial support for VSF TR-06-2 (Main/Advanced Profile):
|
|
* PSK Encryption (AES 128 and 256)
|
|
* Bidirectional connection initiation
|
|
* Multipath support (load balanced or redundant link bonding)
|
|
* One-to-many distribution (media server mode)
|
|
* Tested packet recovery up to 50% continuous loss
|
|
* Jitter-tolerant output timing
|
|
|
|
Tools:
|
|
- ristsender - RIST sender application
|
|
- ristreceiver - RIST receiver application
|
|
- rist2rist - RIST relay/proxy application
|
|
|
|
Platform Support:
|
|
- Linux, macOS, Windows, FreeBSD
|
|
- Cross-compilation support via meson
|
|
|
|
|
|
Project Information:
|
|
--------------------
|
|
|
|
Website: https://code.videolan.org/rist/librist
|
|
License: BSD 2-Clause "Simplified" License
|