mirror of
https://github.com/coturn/coturn.git
synced 2026-10-24 00:08:57 +03:00
master
29 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1038f47bcc |
Deprecate option drop-invalid-packets (#2078)
This option has been on by default for multiple versions - removing the flag completely |
||
|
|
285a9b76c9 |
Merge commit from fork
The UDP listener's stateless first-packet fast path short-circuits a first datagram from an unknown source when the outcome does not depend on session state, so it never allocates a child socket or a session. Its credential-mode gate sat above the packet classification, so the short-circuit only applied in long-term-credential mode; in no-auth mode the same first packets fell through to create_ioa_socket_from_fd + open_client_connection_session and held that state until the to-be-allocated watchdog expired, even though the relay never acts on them. Move the credential-mode gate below the classification: the decisions that do not depend on credential type are made at the listener in every mode, and only the 401/438 challenge synthesis stays long-term-only. A real request (e.g. ALLOCATE) still takes the session path, and an error-producing request is still reaped on the initial packet, so the bytes on the wire are unchanged. Rename udp_stateless_nonce_fast_path to udp_stateless_fast_path to reflect the mode-independent role, and document it in docs/stateless-nonce.md. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
c6cd2106b6 |
Enable stateless nonce by default (#2036)
## What Makes `--stateless-nonce` the default, and closes two paths that created a per-packet session for unauthenticated UDP traffic even when it was enabled. ## Behaviour changes for operators 1. **The default challenge nonce goes from 16 to 24 characters.** RFC 8489 requires clients to treat NONCE as an opaque string of up to 128 characters, so compliant clients are unaffected. `--stateless-nonce=false` restores the stored random nonce and the 16-character challenge. 2. **The default signing key is ephemeral**, so a restart costs each client one `438` re-auth round-trip, as does a retry that lands on a different instance behind a load balancer. `--stateless-nonce-secret` shares the key across a fleet and avoids both. (This is not a regression — today a retry landing on another instance finds no session either — but it becomes the default story.) ## Validation Run in a container (Linux, isolated network namespace): - build clean; 17/17 unit tests - 11 example suites with zero FAIL lines: `run_tests`, `_conf`, `_stateless_nonce`, `_mobile`, `_dtls_default`, `_multiplex_peer`, `_stateless_binding`, `_ratelimit_401`, `_mobility_quota`, `_expiry`, `_mobility_resume_flood` - both fuzzing smoke targets (`ASan 0`, `ASan 1`) - `clang-format-15` clean; mandoc lint unchanged at 104 pre-existing warnings |
||
|
|
0c5ddc5481 |
Worktree stateless nonce mi fastpath (#2025)
What — the fast path fell through on the mere presence of MESSAGE-INTEGRITY, so 20 garbage bytes put a spoofed flood back on the session-creating path. Framed honestly as ~19 KB of alloc+zeroing per ~100-byte packet rather than an OOM, since close_after_auth_challenge already bounded steady-state memory. Headline number: 200 forged-MI packets → 200 sessions before, 0 after, all still answered. How, part 1 — nonce validated in the listener; 400 / 437 / 441 / 438 answered there in check_stun_auth's order; wrong-length MI treated as missing; a verifying nonce still falls through because the credential lookup can be async. Plus the --stun-only side improvement. How, part 2 — rejections charged to the existing --unauthorized-ratelimit bucket, with the reasoning for not dropping them: RFC 8489 §9.2.5 needs the 438 for stale-nonce recovery, and after part 1 that legitimate retry lands on this very path. Includes the amplification measurements (438 under 1:1 vs 401 at 3.6–5.4:1) to explain why this caps a vector rather than closing an amplification hole. Wire compatibility — nine shapes dumped attribute-by-attribute against a pre-change build, identical. Tests — the new probe script and its assertions, the two rate-limit cases, and the full green matrix including fuzz smoke and the Docker image Bats run. --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
eda6358d2f |
multiplex-peer: bound the shared per-thread peer demux table (#2017)
## What Moves the `--multiplex-peer` demux table out of `ioa_engine_t` into its own module, `src/apps/relay/mp_peer_table.c/.h`, and gives it two properties it did not have: - **A per-allocation endpoint limit.** New `--multiplex-peer-max-peers` (default 256). Beyond it, CREATE_PERMISSION and CHANNEL_BIND are answered with 508 and SEND indications are dropped. The first rejection per allocation is logged once. - **A reverse index** (session → its endpoints), so `mp_deregister_permission_peers` and `mp_deregister_session_peers` cost that session's own endpoints instead of a full scan of the table shared by every session on the relay thread. ## Why The table is keyed by exact peer IP:port, but permissions are per peer IP, so a single permission covers all 65535 ports of that IP: every SEND indication naming a fresh port added an entry, with no cap and no backpressure. The table is per relay thread and shared, so one busy allocation grows a structure its co-tenants pay for — and every permission expiry and session teardown walked the whole thing. Ephemeral-port churn produces the same growth without anyone trying, so this is also a robustness issue for high-volume deployments. ## Behaviour changes - `--multiplex-peer-max-peers <n>` (1–65535, default 256). Config-file key works too. No effect unless `--multiplex-peer` is set. - An allocation exceeding the limit gets **508** on CREATE_PERMISSION / CHANNEL_BIND. 508 is what this mode already returns for its other capacity limitation (EVEN-PORT), so the code is not new to multiplex-peer clients. - Nothing changes below the limit; the default is far above normal use (a typical allocation registers two endpoints — its peer's RTP and RTCP ports). ## Performance Microbenchmark of the old vs new code paths (arm64, `-O2`, keys precomputed so only table work is timed): | table entries | demux lookup (per datagram) | re-register (per SEND) | permission expiry, 2-endpoint session | |---|---|---|---| | 400 | 99 → 96 ns | 190 → 103 ns | 2.5 µs → 0.068 µs | | 4,000 | 116 → 119 ns | 236 → 125 ns | 9.3 µs → 0.055 µs | | 50,000 | 129 → 133 ns | 270 → 147 ns | 98.7 µs → 0.24 µs | | 500,000 | 501 → 510 ns | 1002 → 510 ns | 986 µs → 1.2 µs | - Data plane is untouched — same map, same lookup; deltas are noise. - The SEND path is ~2× cheaper: the old code did `ur_addr_map_get` **and** `ur_addr_map_put`, and `put` re-scans the bucket. Registering an endpoint the session already owns now returns after the `get`. - Memory: +128 B per endpoint for the index copy, ~310 B per session with endpoints (index array starts at 2 slots because `ioa_addr` is 128 B and the common case is RTP + RTCP). Worst case per allocation is now ~66 KB at the default limit rather than unbounded. The per-thread bound is sessions × limit, so a hard ceiling still wants `--total-quota`. ## Tests - `tests/test_mp_peer_table.c` (new, 8 cases): idempotent re-registration, cross-session conflict, a 4096-port registration run capped at 64, the limit being per-session rather than global, slot reuse after release, per-IP and per-family deregistration, and a deterministic assertion — via a `deregister_ops` counter — that deregistration never touches 200 entries belonging to another session. - `examples/run_tests_multiplex_peer.sh`: server start/stop refactored into functions; adds a run under `--multiplex-peer-max-peers=4` (normal relaying unaffected) and one under `=1` asserting the client sees 508 and the server logs the limit. The second SKIPs where multiplex-peer is inactive (non-Linux). ## Validation - macOS: 18/18 ctest, all `examples/run_tests*.sh`, both fuzzing smoke targets, `make lint` clean. - Linux (Docker): 17/17 ctest; `run_tests.sh`, `run_tests_conf.sh`, `run_tests_mobile.sh`, `run_tests_multiplex_peer.sh`, `run_tests_rfc5780.sh`, `run_tests_stateless_nonce.sh` all green. - Packaged image: `make docker.image dockerfile=debian` + `make test.docker` — 19 Bats tests, 0 failures. ## Reviewer notes - `deregister_ops` is a counter on the table struct read only by the unit test; it is one increment inside a loop that already runs. - `mp_deregister_peer()` had no callers before this change and still has none; it is kept as the exact-key counterpart of the two bulk helpers. - Docs updated: `docs/multiplex-peer.md` (table layout, files-changed table, CLI examples) and `examples/etc/turnserver.conf`. |
||
|
|
50339c9e11 |
docs: state OpenSSL 3.0+ as the supported target; drop stale guidance (#2011)
coturn already targets OpenSSL 3.x (the DH-param migration in #1809 dropped 1.1.1 build support, and RFC 7250 raw public keys need 3.2.1), but several docs still referenced OpenSSL 1.0.0a/1.0.1/1.0.2 and 0.9.8 as if they were supported or recommended baselines. Make the requirement explicit and consistent: OpenSSL 3.0 or newer is the supported target; older releases (1.x, 1.1.x, 0.9.x) and other libraries are not supported and are not tested or validated against. - docs/OpenSSL.md: lead with the 3.0+ requirement and the unsupported/not- validated statement; note the RFC 7250 raw-public-keys 3.2.1 sub-requirement and the deprecated patches/openssl-1.1.1 bridge; reframe the custom-prefix build guidance for "system ships < 3.0" instead of the old 1.0.1 example. - docs/Build.md: replace the "1.0.0a or better recommended" / 0.9.8-DTLS-over- IPv6 paragraph with the 3.0+ requirement. - README.md: mark the openssl dependency as 3.0 or newer. - README.turnserver: drop the pre-1.0.2 EC-curve and 1.0.2-ALPN version caveats (both moot at 3.0+). |
||
|
|
5fada2b30b |
docs: record the RFC 3489 deprecation roadmap; fix stale option text (#2009)
Follow-up to #2008. Documentation only — no functional change. ## Why #2008 split `--stun-backward-compatibility` and moved the RFC 3489 ("classic" STUN) request path behind a deprecated `--rfc3489-compatibility`. But the two-step retirement plan lived only in that PR's description: nothing in the tree said what gets removed, when, or what an operator should do about it. ## What this adds **`docs/rfc3489-deprecation.md`** — the reference for both audiences: - **Why** classic STUN is being retired (the magic-cookie split, RFC 5389 → RFC 8489). - **What each flag now controls**, plus the full address-attribute matrix across all four combinations. - **Migration**: which flag to set depending on *why* you originally set the combined one; setting both reproduces the old semantics exactly. - **"Do you still need it?"** — the exact log lines that reveal classic-STUN clients, including the detail that `OLD BINDING` needs *both* `--verbose` and `--log-binding`. - **What is NOT affected** — `--rfc5780` NAT behavior discovery shares the `CHANGE-REQUEST` attribute and is untouched; `MAPPED-ADDRESS` on modern responses stays. - **Removal roadmap** — step 1 (done) and step 2, with a per-file table of what comes out and two constraints recorded up front: - Keep `OLD_STUN_ATTRIBUTE_PASSWORD` (0x0007) — it is deliberately skipped so a modern client that gratuitously includes it isn't answered `420 Unknown Attribute`. Deleting that case turns tolerated-and-ignored into a hard error. - Expect an **API break**: the `old_stun_*` declarations ship as public headers (`src/client/*.h` → `include/turn/client/`, installed beside `lib/libturnclient.a`), which is why step 2 needs a major version. ## Also fixed `README.turnserver` — the plain-text twin of the man page — still carried the pre-split description: > Enable handling old STUN Binding requests using MAPPED-ADDRESS attribute in binding response (instead of XOR-MAPPED-ADDRESS). That was stale on two counts even before #2008 (the attribute is added *in addition to*, not *instead of*, `XOR-MAPPED-ADDRESS`), and it had no entry for the new option. Rewritten to match the man page, with the migration note. The new doc is linked from the RFC 3489 bullet in `README.md` and from item 22 in `STATUS.md`. ## Checks Every file path and symbol referenced by the new doc was verified to exist on current master. The behavior matrix is the one validated end-to-end in #2008. No C files touched, so the `clang-format` gate and build are unaffected. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
dfec35702c |
Return 508, not 400/440, when a well-formed Allocate cannot be satisfied (#2007)
## Summary RFC 8656 §7.2 assigns **508 (Insufficient Capacity)** to a well-formed request the server cannot satisfy; 400 means the request was malformed and 440 means the address family is unsupported. Two config-dependent Allocate paths used the wrong code: - **Dual allocation under `--external-ip`** returned **440**, which tells the client the server lacks an address family entirely — a client may permanently disable IPv6 against a server that supports it fine. Now 508. - **EVEN-PORT under `--multiplex-peer`** returned **400**, which tells a conformant client its request was malformed, so it will not retry without EVEN-PORT. Now 508, matching the sibling missing-shared-socket path in `create_relay_ioa_sockets()`. The `even_port` arm of the dual-allocation check is an unreachable backstop (the attribute loop already rejects ADDITIONAL-ADDRESS-FAMILY + EVEN-PORT in both attribute orderings); it is now commented as such and aligned with the upstream check's 400 and reason text instead of the old 440. Also updated: `run_tests_multiplex_peer.sh`'s randomized-EVEN-PORT retry now matches on error 508, and the stale EVEN-PORT notes in CLAUDE.md and docs/multiplex-peer.md are corrected (the latter claimed EVEN-PORT was silently ignored; it is rejected). ## Validation - macOS and Linux (Docker): clean builds, all unit tests, all example suites (`run_tests`, `_conf`, `_mobile`, `_multiplex_peer`, `_rfc5780`, `_stateless_nonce`) with zero FAIL lines; fuzzing smoke tests. - Both paths verified end-to-end: `turnutils_uclient -Z` against an `--external-ip` server now receives `error 508`, and an EVEN-PORT Allocate against a `--multiplex-peer` server (Linux) receives `error 508` with the matching server-log reason. |
||
|
|
142dd93b30 |
Add --stateless-nonce: bound memory under valid-structure STUN floods (#1999) (#2002)
Closes #1999. ## Problem A structurally valid STUN request from a spoofed UDP source commits real resources before any authentication: a child socket in the listener's address map plus an ~18KB `ts_ur_super_session` — allocated solely to store the random 401-challenge nonce — kept alive for 60 seconds by the to-be-allocated timer. Early packet validation (#1768) doesn't help against well-formed packets, and `--unauthorized-ratelimit` suppresses only the 401 *responses*, after the state already exists. At 10k spoofed pps that is hundreds of thousands of live sessions (multiple GB) from traffic that never authenticates. ## Approach With `--stateless-nonce` (off by default) the challenge nonce becomes an **authenticated timestamp cookie** instead of stored random state: ``` nonce = <8 hex chars: 32-bit issue time> || <16 hex chars: HMAC-SHA256(K, "<client-addr>|<ts-hex>") truncated> ``` Because the server can *validate* a nonce (parse timestamp → recompute MAC with a constant-time compare → check age against `--stale-nonce`, default 600s, with a 5s future-skew allowance), three things follow: 1. **Listener fast path** (`udp_stateless_nonce_fast_path`, `dtls_listener.c`): MESSAGE-INTEGRITY-less requests from unknown UDP sources are answered with the 401 challenge directly from the listener — no socket, no session. Packets the relay would silently ignore for a fresh source (indications, unbound channel data, STUN failing the full check) are dropped with no state either. 2. **Fresh-session nonce acceptance** (`check_stun_auth`): the client's authenticated retry lands on a brand-new session, which accepts a presented nonce whose MAC verifies for that client address and whose issue time is within the lifetime. Stale-nonce expiry is anchored to the real issue time. 3. **Challenge-session teardown**: a UDP session whose only output was a 401/438 (or whose challenge the ratelimit suppressed) is torn down immediately instead of idling for 60s. This bounds memory against floods that attach a garbage MESSAGE-INTEGRITY and therefore must travel the session path. Result: zero attacker memory cost for MI-less floods; one transient, immediately-freed session per packet for garbage-MI floods. `--stateless-nonce-secret=<secret>` (implies the flag) additionally derives the key via `K = SHA-256("coturn-stateless-nonce-v1" || secret)` so a fleet sharing the secret cross-validates nonces across restarts and load balancers. Without it the key is ephemeral per process. ## Wire transparency - The fast-path 401 replicates the session path byte-for-byte: same attributes in the same order (NONCE, REALM, THIRD-PARTY-AUTHORIZATION for oauth, SOFTWARE, FINGERPRINT), including realm selection by ORIGIN on ALLOCATE, plus the same `--unauthorized-ratelimit` suppression and Prometheus 401 counters. - Anything the fast path cannot answer identically falls through to the untouched session path: BINDING (RFC 5780), ALLOCATE under alternate/aux-server redirection, REFRESH under `--mobility`, CONNECTION-BIND, and all MI-carrying requests. - Nonce reuse within its lifetime is how RFC 8489 long-term auth already works (per-request freshness comes from MESSAGE-INTEGRITY over the random transaction ID); binding the nonce to the client address is strictly stronger than a stored random value. The nonce grows from 16 to 24 chars when the flag is on — RFC 8489 requires clients to echo nonces opaquely up to 128 chars. - With the flag off, every changed code path short-circuits to stock behavior. Design notes, threat model, and limitations: `docs/stateless-nonce.md`. ## Reviewer notes - The branch is four commits telling the story in order: the feature with windowed derivation, an int→bool type cleanup around the touched code, the switch to the timestamp-cookie format (exact expiry, no window scanning), and the shared fleet key. Happy to squash if preferred. - `NONCE_MAX_SIZE` grows to hold both nonce formats, and nonce emitters now use `strlen()` instead of the fixed `NONCE_MAX_SIZE - 1` (which would otherwise pad the shorter legacy random nonce). - `stun_is_command_message_full_check_str()` gained `bool` parameters (was `int`), rippling through its nine in-repo callers. - One pre-existing test-harness fix rides along: `run_tests.sh`/`run_tests_mobile.sh` raced clients against a still-initializing server on macOS (fixed `sleep 2` instead of log polling); both now poll on both platforms. The CLAUDE.md container command also gained `libmicrohttpd-dev` and a `git safe.directory` line it needs on a clean image. - Known limitation: key rotation for `--stateless-nonce-secret` means restarting with a new secret (one standard 438 re-auth per client); sign-with-new/verify-with-both is documented as future work. ## Validation - New Unity suite `tests/test_stateless_nonce.c`: format, determinism, per-input divergence, single-character tampering across the whole cookie, malformed inputs, age/skew boundaries, KDF determinism and secret binding, and a cross-server test (two keys from one secret validate each other's nonces; a third secret does not). - New end-to-end suite `examples/run_tests_stateless_nonce.sh`: standard relay workload with the feature on over UDP/TCP (+TLS/DTLS and threaded variants on Linux), asserting the listener fast path actually engaged and the secret-derived-key startup markers. - Full battery green on macOS and in a clean Linux container: cmake build + ctest, `run_tests.sh`, `run_tests_conf.sh`, `run_tests_mobile.sh`, `run_tests_multiplex_peer.sh`, `run_tests_rfc5780.sh`, `run_tests_stateless_nonce.sh`, fuzzing smoke (ASan targets 0/1), and the packaged Docker image bats tests (19 tests, 0 failures). |
||
|
|
ce8b25737d |
feat: RFC 8016 graceful dual-5-tuple mobility handoff (#1975)
Historically a MOBILITY-TICKET resume hard-switched the allocation onto the new client socket at REFRESH time and closed the old socket immediately. RFC 8016 instead requires the server to keep receiving on, and transmitting peer->client data to, the old 5-tuple until the client is active on the new one, and only then discard the old 5-tuple. Implement that make-before-break handoff: - handle_turn_refresh no longer transfers the socket. It rotates the mobility ticket, refreshes the allocation on the original session, and opens a bounded transition: the original session keeps the allocation and its old socket (peer->client egress stays on the old 5-tuple), while the resuming session is kept alive and linked as "pending" and the REFRESH success response is sent on the new socket. - read_client_connection promotes the allocation onto the new socket on the client's first packet from the new 5-tuple (mobile_complete_transition): the socket is moved to the allocation session, which closes the old socket and discards the old 5-tuple, and the packet is then served by the allocation. RFC names the trigger as the first Send/ChannelData; we promote on any first packet so control requests (CreatePermission/ChannelBind/Refresh) a roaming client issues from the new address are served by the allocation. - The per-session sweep aborts a transition left open past a 30s deadline, conservatively keeping the allocation on the old path and reaping the pending session. shutdown_client_connection unlinks a transition partner on teardown. Transition state (session ids + deadline) is added to ts_ur_super_session; the pending session's un-allocated watchdog is disarmed and its stray mobile-map entry removed while it legitimately holds no allocation. Also align resume error codes with RFC 8016: an unusable/unknown ticket now returns 437 (Allocation Mismatch) instead of the non-standard 404. examples/run_tests_mobile.sh now asserts, from the server log, that the handoff actually executed (mobile_complete_transition emits a marker), so a regression that skipped or broke the transition fails the test. docs/mobility-rfc8016.md documents the design, the trigger/deadline semantics, the quota note, and the deferred ticket-entropy hardening item. |
||
|
|
47ce433cde |
Add per-source rate-limiting of UDP 401 Unauthorized responses (#1957)
TURN/STUN long-term auth answers the first unauthenticated request with
a 401 Unauthorized carrying REALM/NONCE. On UDP, where source addresses
are spoofable and the 401 is larger than the request that triggers it,
the server becomes a reflection/amplification vector: an attacker forges
a victim's source IP and bounces amplified 401s at it.
This adds an opt-in mitigation that caps how many UDP 401 responses the
server emits toward any single source IP per second. Past the cap the
server stays silent, denying both the reflection and the amplification.
Authenticated relay traffic is untouched — only requests that reach the
401 branch spend budget.
Flags (off by default):
--unauthorized-ratelimit Enable the mitigation (UDP only).
--unauthorized-ratelimit-rps=<n> Max 401 responses per source IP per
second. Default 10.
Implementation:
- Lock-free, fixed 4096-bucket direct-mapped table (~80 KiB, no
malloc/free, no lock, no list scan on the hot path); the source IP
appears only as a 32-bit tag, never as a pointer. The port is stripped
from the key so an attacker cannot evade the limit by rotating the
source port. A fixed 1-second window backs the per-second limit.
- Colliding addresses share the live bucket budget instead of receiving
a fresh allowance, so hash collisions can never expand reflected output.
- Drop and collision diagnostics are each latched to one log line per
(bucket, window) to avoid log amplification.
- Portable 32-bit atomics over C11 <stdatomic.h> with an MSVC
Interlocked* fallback; the on/off flag and the per-second threshold are
pointers into turn_params so every relay thread sees live CLI values
without per-thread copies.
Observability (Prometheus):
- turn_unauthenticated_401_{requests,responses,dropped_responses}
- turn_ratelimit_{hash_collisions,occupied_buckets,total_buckets}
Tests & docs:
- Unity unit test (tests/test_ratelimit.c) covering live-bucket
collisions, occupancy tracking, and the collision counter.
- End-to-end positive/negative system test (run_tests_ratelimit_401.sh)
and a Prometheus counter test (run_tests_prom.sh).
- A 401-response-flood load harness under examples/loadtest/.
- docs/401-ratelimit.md design note, plus man page, README, Prometheus
docs, and the example/Docker turnserver.conf entries.
|
||
|
|
53f3ed9104 |
Prom https (#1956)
## Summary Adds an optional **HTTPS mode for the Prometheus `/metrics` endpoint**, and bundles three supporting improvements that surfaced while building and testing it: more reliable `libmicrohttpd` detection, a fix for slow `turnserver` startup on macOS, and expanded/robust Prometheus tests. ## Changes ### 1. Optional HTTPS for the Prometheus exporter The exporter previously served `/metrics` over plain HTTP only. It can now optionally serve over HTTPS via libmicrohttpd's TLS support. - `--prometheus-tls` — serve the metrics endpoint over HTTPS instead of HTTP. - `--prometheus-cert <file>` / `--prometheus-key <file>` — PEM cert/key for the endpoint; default to the server's own `--cert` / `--pkey` when unset. `start_prometheus_server()` loads the cert/key into memory, enables `MHD_USE_TLS`, and passes `MHD_OPTION_HTTPS_MEM_CERT/KEY`. If TLS is requested but no cert/key can be loaded (or libmicrohttpd lacks TLS support), it logs an error and does not start. The startup log now notes the `(http)`/`(https)` scheme. `docs/Prometheus.md` documents the new flags. *(`src/apps/relay/prom_server.c`, `mainrelay.c`, `mainrelay.h`, `docs/Prometheus.md`)* ### 2. Reliable `libmicrohttpd` detection in `configure` The autotools `configure` probed libmicrohttpd with a bare `cc -lmicrohttpd` link test (no include/lib paths), so it failed to find Homebrew-installed libmicrohttpd on macOS and silently disabled Prometheus — even though OpenSSL/libevent (also Homebrew) were found via pkg-config. `configure` now detects libmicrohttpd via `pkg-config` first (matching how libssl/libevent are already detected), falling back to the bare link probe on platforms without a `.pc` file. `./configure && make` now builds the exporter on macOS without manual `CFLAGS`/`LDFLAGS`. *(`configure`)* ### 3. Faster `turnserver` startup on macOS macOS lacks `pthread_barrier_*`, so the thread-startup rendezvous in `netengine.c` degraded to a flat `sleep(5)` — adding ~5s to every startup (and it was racy: threads proceeded even if setup took longer than the fixed sleep). Replaced the shim with a real portable barrier built on a mutex + condition variable, behind a unified macro API: **native `pthread_barrier` is still used on Linux**, the portable barrier is used only where it is unavailable. The previous racy `sched_yield()` spin-wait fallback is removed as well. *(`src/apps/relay/netengine.c`)* ### 4. Prometheus test coverage + robustness `examples/run_tests_prom.sh` now also covers the HTTPS endpoint (explicit cert/key, cert/key inherited from `--cert`/`--pkey`, and a negative test that an unreadable cert keeps the endpoint down, including a check that the TLS endpoint rejects plaintext HTTP). Readiness is now detected by polling the endpoint / server log instead of fixed `sleep` intervals, making the suite both faster (~70s → ~2s) and less flaky. *(`examples/run_tests_prom.sh`)* ## New flags | Flag | Description | |---|---| | `--prometheus-tls` | Serve `/metrics` over HTTPS. Requires libmicrohttpd built with TLS support. | | `--prometheus-cert <file>` | PEM certificate for the HTTPS endpoint (default: the server's `--cert`). | | `--prometheus-key <file>` | PEM private key for the HTTPS endpoint (default: the server's `--pkey`). | ## Validation - **macOS** (portable-barrier path): `clang-format` clean, 10/10 unit tests, and `run_tests.sh` / `run_tests_conf.sh` / `run_tests_multiplex_peer.sh` / `run_tests_prom.sh` all pass. Exporter now reachable at startup `t=0` (was `t=5`). - **Linux** (Docker, native-`pthread_barrier` path): clean build, 9/9 unit tests, and all four system-test suites pass (TCP/TLS/UDP/DTLS). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
1ec81eff10 |
Build Prometheus exporter from vendored local sources (#1955)
## Summary The Prometheus exporter previously linked the [digitalocean/prometheus-client-c](https://github.com/digitalocean/prometheus-client-c) (`prom`) library, which is **no longer maintained**. This replaces it with a minimal, self-contained Prometheus client under `src/prometheus/` that is built straight from coturn's own sources and implements exactly the slice of the API coturn uses: counters, gauges, a single default registry, and the text-exposition serializer. The vendored client is **drop-in API-compatible**, so `prom_server.c` is unchanged. Histograms, summaries, custom registries and the upstream `promhttp` handler are intentionally omitted (coturn serves `/metrics` with its own libmicrohttpd handler). The exporter is now **always compiled in**; the runtime `--prometheus` flag still controls whether it starts. **libmicrohttpd** — actively maintained — is the only remaining external dependency and is now required by the build. ## Changes - **`src/prometheus/{prom.h,prom.c}`** — vendored client. Thread-safe (per-metric mutex on the increment path, read under lock in the bridge), label-aware, with proper Prometheus value/label escaping and clean integer formatting for counters. No deps beyond pthread/libc. - **CMake** — replace `find_package(Prometheus)` with required `find_package(MicroHTTPD)`; add `cmake/FindMicroHTTPD.cmake`; remove dead `cmake/FindPrometheus.cmake` and the `WITH_PROMETHEUS` option. - **`configure` / `Makefile.in`** — drop `-lprom`, compile the vendored source. - **Docker** (`debian`, `alpine`) and **CI** (`ubuntu-build-deps`, `mingw`) — remove the `prometheus-client-c` build/install steps; keep libmicrohttpd. - **`docs/Prometheus.md`** rewritten. - **`tests/test_prometheus.c`** — Unity tests for counter accumulation/monotonicity, gauge inc/dec, and label-value escaping. ## Validation - Full build green; all unit tests pass (incl. new `test_prometheus`); `clang-format-15` clean. - Live exporter scraped over HTTP — all `run_tests_prom.sh` scenarios (default / custom address / port / path, plus the negative case) pass. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
273f2e8a0c |
Add optional TLS transport for Redis connections (#1951)
The Redis driver previously connected only over plaintext TCP, so a Redis server configured for TLS-only (tls-port) was unreachable. Add an opt-in TLS transport built on the hiredis_ssl library, covering both the synchronous userdb path and the asynchronous statsdb path. Fixes #651 New connection-string keys (for --redis-userdb and --redis-statsdb): tls/ssl, ca/cacert, capath, cert, clientkey, sni/servername, verify (peer|none). Server-certificate verification is on by default. - Extract the connection-string parser into a dependency-free unit (dbd_redis_conninfo.c/.h) and unit-test it (tests/test_redis_conninfo.c). - hiredis_ssl is detected via pkg-config; without it tls=true is rejected with an explicit error and the build still compiles (hiredis-only). - The blocking sync handshake honors connect_timeout so a plaintext endpoint mistakenly addressed with tls=true cannot hang. - Document the options in docs/Redis.md, the man page, and example config. --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
8c7d8fcb86 |
Enable --udp-recvmmsg by default on Linux (#1930)
## Summary Flips the Linux default for `--udp-recvmmsg` from **off** to **on**. Operators opt out with `--udp-recvmmsg=false` (or `=0`). > **Stacked on #1929.** This depends on the recvmmsg-scoping change in #1929 and is based on that branch, so the diff shows only the default-on change. GitHub will auto-retarget the base to `master` once #1929 merges. Merge #1929 first. ## Why this is now safe The original objection to default-on (recorded in `docs/PerformanceIterationLog.md`) was the **per-session-relay-socket prealloc tax**: `--udp-recvmmsg` applied the 16-buffer batch path to every connected relay socket, which only ever carries one flow, so the churn ate the listener-side win. #1929 scoped recvmmsg to **shared fan-in sockets only** (`udp_recvmmsg_eligible`: the client listener, plus the per-thread shared relay socket under `--multiplex-peer`). Per-session relay sockets now stay on the single-recv path regardless of the flag, so that tax is gone. The one socket touched by default — the client listener — is a genuine fan-in point: - batches whenever client concurrency is non-trivial (measured `avg_batch ≈ 16` under load), and - costs little when idle (few packets ⇒ few prealloc cycles). ## What changed - `mainrelay.c`: `turn_params.udp_recvmmsg` default `false → true` (Linux only). - Removed the now-dead `--multiplex-peer` auto-enable block and the `udp_recvmmsg_set_explicitly` tracking it relied on; multiplex-peer gets its recvmmsg window from the default. The opt-out flows through the normal `get_bool_value` path. - Help text, `man/man1/turnserver.1`, `examples/etc/turnserver.conf`, `CLAUDE.md`, and `docs/PerformanceIterationLog.md` updated for the new default + opt-out. Per-session relay sockets and DTLS session sockets are unchanged. ## Validation - **Format:** clang-format 15.0.7 clean. - **macOS:** build + ctest 6/6 + `run_tests.sh` pass. - **Linux (Docker, clean build):** ctest 5/5; `run_tests.sh`, `run_tests_conf.sh`, `run_tests_multiplex_peer.sh` all pass (no FAIL). - **Runtime proof (loopback, `--udp-recvmmsg-log`):** - Default, no flag: recvmmsg active, `calls=13714 packets=219306 avg_batch=15.99`. - `--udp-recvmmsg=false`: zero recvmmsg activity — opt-out confirmed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b17f5c482f |
Add --udp-sendmmsg-log to observe egress sendmmsg/UDP-GSO batching (#1927)
## Summary Adds a Linux-only `--udp-sendmmsg-log` flag (mirroring `--udp-recvmmsg-log`) that logs per-relay-thread **egress** batch statistics every 10 s: flush count, total datagrams, average batch occupancy, UDP-GSO engagement (`gso_flushes`/`gso_datagrams`/`gso_frac`), and a per-flush occupancy histogram. ## Motivation `--multiplex-peer` enables `sendmmsg`/UDP-GSO coalescing on the egress path, but there was no way to see whether it actually coalesces anything. While investigating this I confirmed a non-obvious property worth documenting: - Per-session UDP **client** sockets are children of the listener (`parent_s`), and `udp_send_fd()` returns the shared **listener fd** for all of them. Combined with the `recvmmsg`-driven batch window, relay→client **downlink** sends to *different clients* already coalesce into one `sendmmsg` on the listener fd — the listener fd is effectively a shared client-facing send socket. (In non-multiplex mode each allocation has its own relay socket, so a `recvmmsg` drain spans only one session and the downlink batch is a singleton — cross-client batching genuinely requires `--multiplex-peer`.) - UDP-GSO only engages when destination **and** segment size match across a batch, so at low per-flow packet rates (VoIP-style, tens of pps per flow) it rarely fires. This flag makes both effects measurable instead of assumed. ## Example Captured under a `--multiplex-peer --udp-gso` load: ``` udp-sendmmsg stats: flushes=21 datagrams=27 avg_batch=1.29 gso_flushes=0 \ gso_datagrams=0 gso_frac=0.000 hist_1=17 hist_2=2 hist_3_4=2 hist_5_8=0 hist_9_16=0 hist_17_32=0 ``` - `avg_batch` — mean datagrams per flush (1.0 = no coalescing) - `gso_frac` — fraction of datagrams sent via UDP-GSO (~0 = GSO not earning its keep) - `hist_*` — per-flush occupancy histogram Both rise with aggregate pps and sit near the floor on lightly loaded servers. ## Implementation - Counters on `ioa_engine` (behind `#if defined(__linux__)`), bumped once per flush in `udp_sendmmsg_flush()` and once per `recvmmsg` call — no per-datagram cost. - New `--udp-sendmmsg-log` CLI flag + 10 s periodic logger in the engine timer, mirroring the existing recvmmsg stats path. - Docs: a new "Egress Batching (sendmmsg / UDP-GSO) and Observability" section in `docs/multiplex-peer.md`. ## Testing - macOS: build + `ctest` (6/6) + `run_tests.sh` pass (instrumentation is `#if __linux__`, validating flag plumbing + the non-Linux stub path). - Linux (clean Ubuntu 24.04 Docker): build + `ctest` (4/4) + `run_tests.sh` / `run_tests_conf.sh` / `run_tests_multiplex_peer.sh` pass, and a `--multiplex-peer --udp-gso --udp-sendmmsg-log` load run emitted the stats line above. |
||
|
|
e0c37a3aa0 |
Multiplexpeer (#1916)
## Summary Adds **`--multiplex-peer`**, a non-standard relay mode that replaces the per-allocation peer-side port bind with **one shared IPv4+IPv6 UDP socket pair per relay thread**. Sessions are demultiplexed by exact peer IP:port in a per-thread `mp_table`. This lifts the ~16 k allocation cap that the default 49152-65535 relay port range imposes, and dramatically reduces kernel-level UDP receive-buffer drops under high pps. Design and trade-offs: [docs/multiplex-peer.md](docs/multiplex-peer.md). ## What changes ### Server (turnserver) - **`--multiplex-peer`** (cross-platform) — enable the shared per-thread relay sockets. Replaces the per-session port bind. Implies sendmmsg batching on Linux and default-enables `--udp-recvmmsg` (override with `--udp-recvmmsg=0`). Incompatible with EVEN-PORT — those Allocates are rejected with 400. - **`--multiplex-peer-port <port>`** (cross-platform, default 3480) — base port; thread `i` binds `<base>+2i` (IPv4) and `<base>+2i+1` (IPv6). A 4-thread server consumes 8 ports. - **`--udp-gso`** (Linux-only CLI) — UDP-GSO (`UDP_SEGMENT` cmsg) on the relay send path. Requires `--multiplex-peer` (which is what enables the sendmmsg batching GSO piggybacks on); passing `--udp-gso` alone is a silent no-op. - **CLI surface tightened**: `--udp-recvmmsg`, `--udp-recvmmsg-log`, `--udp-gso` and their fields are now `#if defined(__linux__)` — absent from `--help`, rejected with `unrecognized option`, and the code paths compile out on macOS/Windows. - **Windows portability**: `SO_REUSEPORT` in `mp_open_socket` wrapped in `#ifdef` (MSVC's Winsock doesn't define it; REUSEPORT was defensive anyway because the per-thread port layout is unique by construction). - **`--sock-buf-size` honoured at startup**: the shared multiplex-peer relay socket now calls `set_ioa_socket_buf_size` in `mp_open_socket` so the configured rcvbuf is in effect from the moment the socket exists, not deferred to the first Allocate. ### turnutils_uclient (loadgen) - **`--no-even-port`** — force `ep = -1` on Allocate. The default path randomly attaches EVEN-PORT (with no-R bit) even under `-c`, which `--multiplex-peer` strictly rejects with 400; this flag makes alloc-flood runs against multiplex-peer deterministic. - **Legacy `timer_handler` now wraps the per-tick send batch with `uclient_send_batch_begin/_end`** — without this, runs with `--sender-threads 0` (the default for `-m < 4`) silently fell through every send to plain `send(2)`. strace A/B: 205 k `sendto` → 61 k `sendmsg` (GSO) + 4 k `sendmmsg` + small `sendto` residual for control. ## Measured impact (3-droplet DigitalOcean, c-4 / 4 vCPU, 8 concurrent UDP streams, 45 s) | | baseline | `--udp-recvmmsg` | `--multiplex-peer` | `--multiplex-peer --udp-gso` | |---|---:|---:|---:|---:| | Server NIC rx pps (UDP relay both legs) | 350 k | 334 k | 326 k | 294 k | | Server `UdpInDatagrams` pps | 279 k | 292 k | 300 k | 294 k | | **Server `UdpRcvbufErrors` pps** | **71 k** | 42 k | 26 k | **0.3 k (−99.6 %)** | | **`turnserver` process CPU** | **387 %** | 205 % | 283 % | **133 % (−65 %)** | | Server host idle | 22 % | 49 % | 41 % | **68 %** | Same loadgen-side packet rate (~2 M pps reported by uclient `send_pps` after the legacy-path batching fix). Iteration log: [docs/PerformanceIterationLog.md](docs/PerformanceIterationLog.md). ## Test plan - [x] `ctest --test-dir build` — 3/3 pass (test_ioaddr, test_stun_msg, test_http_server) on macOS + Linux. - [x] `examples/run_tests.sh` — 4 protocols + 4 threaded + load-gen smoke on Linux; 4 protocols on macOS. - [x] `examples/run_tests_conf.sh` — same coverage, conf-driven. - [x] `examples/run_tests_multiplex_peer.sh` — UDP/TCP/TLS/DTLS via `--multiplex-peer --multiplex-peer-port=35000` on macOS + Linux. - [x] Flag matrix smoke on macOS: `--multiplex-peer`, `--multiplex-peer-port=42000`, `--multiplex-peer --udp-gso` (no-op), `uclient --no-even-port`, `uclient --listener-threads N --sender-threads M` — all pass; `--udp-recvmmsg` / `--udp-gso` correctly rejected with `unrecognized option`. - [x] Flag matrix smoke on Linux (Docker): same + `--udp-recvmmsg` accepted, `--multiplex-peer` auto-enables `--udp-recvmmsg`, `--udp-recvmmsg=0` overrides the auto-enable. - [x] Windows compile fix verified — `SO_REUSEPORT` no longer referenced unconditionally. - [x] 3-droplet perf matrix completed; per-hop UDP counters captured. ## Docs updated - New: [docs/multiplex-peer.md](docs/multiplex-peer.md) - [README.turnserver](README.turnserver): full entries for `--multiplex-peer`, `--multiplex-peer-port`, `--udp-gso`; clarified `--udp-recvmmsg` auto-enable semantics. - [README.turnutils](README.turnutils): added `--no-even-port`, plus previously-undocumented `--listener-threads` / `--sender-threads` loadgen pool flags. - [examples/etc/turnserver.conf](examples/etc/turnserver.conf): commented `udp-recvmmsg`, `udp-recvmmsg-log`, `udp-gso`, `multiplex-peer`, `multiplex-peer-port` keys with one-paragraph descriptions and pointer to `docs/multiplex-peer.md`. - Man pages regenerated via `./make-man.sh`. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
5959ecfb13 |
Add UDP-GSO send path (--udp-gso) (#1907)
## Summary - New `--udp-gso` flag (Linux, requires `--udp-sendmmsg`) collapses same-destination, same-size sendmmsg batches into a single `sendmsg` with a `UDP_SEGMENT` cmsg, so the kernel allocates one super-skb that traverses the network stack once and is segmented at egress instead of running `udp_sendmsg → ip_finish_output → __dev_queue_xmit` per datagram. - Also wraps the relay-side `recvmmsg` callback loop in `udp_sendmmsg_batch_begin/end` so peer→client sends triggered inside a recv batch can also coalesce — without that wrapping the relay path issues one `sendto` per delivered datagram. - Sticky-disable on `EINVAL/ENOPROTOOPT` for older kernels/NICs that lack UDP-GSO; one warning logged, then transparent fallback to the existing `sendmmsg` and `udp_send` paths. ## Why The `--udp-recvmmsg` and `--udp-sendmmsg` follow-ups confirmed (see [docs/PerformanceIterationLog.md](docs/PerformanceIterationLog.md)) that on the relay flood workload the dominant cost is the per-datagram kernel TX path. mmsg-style batching reduces only the syscall entry/exit, not the per-skb stack traversal — UDP-GSO collapses both. ## Result DigitalOcean nyc1 c-4, 30 s alternating A/B, `-Y packet -m 1`, eth1 TX as the authoritative server forwarding metric: | Variant | eth1 RX | eth1 TX | sys CPU | idle CPU | |---|---:|---:|---:|---:| | baseline (no flags) | 322,091 | 127,445 | 22.9 % | 67.5 % | | `--udp-recvmmsg --udp-sendmmsg --udp-gso` | 266,068 | **257,996** | 15.0 % | 78.7 % | | baseline (no flags) | 309,475 | 125,573 | 20.9 % | 70.7 % | | `--udp-recvmmsg --udp-sendmmsg --udp-gso` | 275,992 | **225,366** | 14.9 % | 74.3 % | Mean server forwarding rate: **126.5 k → 241.7 k pps (+91 %, 1.91×)**, mean system CPU **21.9 % → 14.9 %** — about **2.8× CPU efficiency** (TX pps per system-CPU-%). Full perf-children comparison and methodology in the new section of [docs/PerformanceIterationLog.md](docs/PerformanceIterationLog.md). ## Notes for reviewers - `--udp-gso` is opt-in and requires `--udp-sendmmsg` (the help text states the dependency). Without `--udp-sendmmsg` the batch state never accumulates and GSO has nothing to flush. - GSO eligibility resets on every `_begin/_end`. Mixed-destination, mixed-size, or oversize batches transparently fall back through `sendmmsg` / `udp_send`. - Rebased onto current `master`; the recvmmsg dependency is already merged via #1906. ## Test plan - [x] `cmake --build build --target turnserver` (RelWithDebInfo + ASan local builds clean) - [x] `ctest --test-dir build --output-on-failure` — 3/3 unit tests pass - [x] `examples/run_tests.sh` — TCP/TLS/UDP pass; DTLS pre-existing failure on macOS environment, unrelated to this change - [x] DigitalOcean A/B perf validation captured above - [ ] Reviewer to confirm CI green on Linux build/test/CodeQL --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
a5005c4193 |
Relay recvmmsg (#1906)
## Summary Extends the existing Linux-only `--udp-recvmmsg` flag from the UDP listener socket to also cover **connected per-session UDP relay sockets**, so steady-state client→relay and peer→relay traffic on plain UDP is read in batches of up to 16 datagrams per `recvmmsg(2)` instead of one `recvmsg` per packet. DTLS sessions still go through the SSL read path and are unchanged. The flag stays **opt-in**: receive-side batching works correctly, but on the current `m=1` / `m=100` benchmarks throughput is flat to slightly negative — the bottleneck has moved past receive (see results below). ## What's in the change - **Shared receive helpers** (`src/apps/relay/ns_ioalib_engine_impl.c`, `src/apps/relay/ns_ioalib_impl.h`): - `ioa_parse_udp_recvmsg_cmsg()` — single TTL/TOS/`IP_RECVERR` cmsg parser used by both `udp_recvfrom()` and the new batch path. Replaces the duplicated parser previously inlined in `dtls_listener.c` and `udp_recvfrom()`. - `ioa_init_recvmmsg_hdr()` — single initializer for `mmsghdr`/`iovec`/cmsg/source-address fields, also used by the listener. - New `IOA_UDP_RECVMMSG_MAX_BATCH = 16` constant; both listener and relay paths now share it. - **Connected relay batch read** (`socket_udp_read_batch_recvmmsg` in `ns_ioalib_engine_impl.c`): called from `socket_input_worker` for non-SSL UDP sockets when `--udp-recvmmsg` is on. Allocates per-message `stun_buffer_list_elem`s, calls `recvmmsg(MSG_DONTWAIT)`, dispatches each datagram through the existing `read_cb` path, and falls back cleanly on `ENOSYS`/`EINVAL`/`EOPNOTSUPP` (auto-disables the flag) and on `EAGAIN`/short-batch (releases unused buffers). - **Per-engine scratch state**: the `mmsghdr[16]` / `iovec[16]` / cmsg / src-addr arrays live on `ioa_engine`, not on every socket — keeps memory flat at thousands of allocations. - **TTL/TOS-sized cmsg buffers** in the listener: the listener previously over-allocated `64 KiB` per slot; it now uses the same TTL+TOS sizing as the relay path. - **Opt-in occupancy stats** behind a new `--udp-recvmmsg-log` flag: every 10 s the relay logs `udp-recvmmsg stats: calls=… packets=… avg_batch=… wouldblock=… unavailable=… no_buffer=… hist_1=… hist_2=… hist_3_4=… hist_5_8=… hist_9_16=…`. Counters are always tracked (cheap); the periodic log is gated by the new flag so default operation is silent. - **CLI plumbing**: `--udp-recvmmsg-log` long option in `mainrelay.c`/`mainrelay.h`, `cli_print_flag` entry in `turn_admin_server.c`, doc updates in `README.turnserver`. - **Docs**: `docs/PerformanceIterationLog.md` records the iteration steps, validation, and two rounds of DigitalOcean A/B numbers. `CLAUDE.md` load-test instructions updated to mention the new flag and the `tot_recv_msgs` / `tot_recv_bytes` workaround. |
||
|
|
69bc0e7351 |
Load generator mode in turnutils_uclient (#1894)
## Summary Adds load-generator modes to `turnutils_uclient` for repeatable TURN server performance testing: - Adds `-Y packet|alloc|invalid` load modes. - Supports packet flood, allocation flood, and invalid-packet flood workflows. - Adds unique local client ports for allocation flood mode. - Removes default packet pacing in load-generator modes unless explicitly set. - Adds helper scripts under `examples/loadtest/`. - Documents load-test usage in `README.turnutils`, `man/man1/turnutils.1`, `CLAUDE.md`, and `docs/PerformanceIterationLog.md`. The performance log captures DigitalOcean benchmark methodology, A/B lessons, hot-path findings, and future optimization candidates. |
||
|
|
69aa0e4401 |
Keep only NEV_UDP_SOCKET_PER_THREAD network engine (#1849)
Remove the two engine implementations (NEV_UDP_SOCKET_PER_SESSION and NEV_UDP_SOCKET_PER_ENDPOINT) and all the dispatch/selection logic around them. NEV_UDP_SOCKET_PER_THREAD is now the sole, unconditional implementation. - mainrelay.h: removed _NET_ENG_VERSION enum, typedef, and net_engine_version / net_engine_version_txt struct fields - mainrelay.c: removed NE_TYPE_OPT CLI option, set_network_engine(), per-endpoint branch in print_features(), and all remaining net_engine_version references - netengine.c: removed run_udp_listener_thread(), setup_socket_per_endpoint_udp_listener_servers() (~190 lines), setup_socket_per_session_udp_listener_servers() (~90 lines); simplified setup_barriers(), setup_relay_server(), run_general_relay_thread(), setup_general_relay_servers(), and setup_server() by eliminating all engine-type conditionals - turn_admin_server.c: replaced dynamic engine version lookups with hardcoded values (3 / "UDP thread per CPU core") in CLI and HTTPS status handlers --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|
|
a6028ddd8a |
Improve PostgreSQL.md Clarity (#1833)
A rewrite and reformat of the PostgreSQL.md file: - Applies some minor formatting to the document. - Applies consistency between the 'turn' user and 'coturn' database names. - Adds some warnings about issues I encountered during my own installation process (public schema error) - Adds a minor example of restarting a service with SystemD |
||
|
|
ce56a78a7a |
Add modules diagram to developer doc (#1685)
Add basic diagram with modules of coturn inside Developer.md. I also wanted to move the existing Flow chart to mermaid so that our doc doesn't depend on external tools but i couldn't find the time to do it properly (horizontal arrows are hard to represent with mermaid) so i postponed that part. This is the result of the mermaid diagram:  |
||
|
|
460cfa38af |
Update SQLite.md (#1429)
Markdown is now proper |
||
|
|
d33112f84a | Update FlowChart (#1377) | ||
|
|
05900adb2e |
Doc: add flowchart (#1328)
The flowchart will be kept updated: - Use [drawio](https://app.diagrams.net/) to edit. 1. Open [drawio](https://app.diagrams.net/) in brower 2. Menu → File → Open from ... → Device: Select [FlowChart.html](FlowChart.html) 3. Edit flow chart 4. Export to svg: Menu → File → Export as... → SVG... |
||
|
|
da6bc6b0c4 | Fix typos (#1345) | ||
|
|
294f897ccd |
CMake: find prometheus(fix #1304) (#1315)
fix #1304 |
||
|
|
85c11b69e3 |
Move and split documentation files (#1096)
This is not a long term documentation works - this is a temp proposal to modernize existing documentation - Create docs folder - Move existing documentation files into docs - Split one massive INSTALL files into smaller files (no text editing was done except small markdown header formatting) - Rename files to be markdowns for nicer rendering - Bad md rendering is expected in many places |