mirror of
https://github.com/coturn/coturn.git
synced 2026-10-24 00:08:57 +03:00
Fixes #1322. The issue reports `clnet_allocate: !!!: relay addr cannot be received (2)` when asking `turnutils_uclient` for an IPv6 relay. It turns out to be two independent bugs that both end in that same line, one on each side of the wire. Both are reproduced by the new test, which fails on `master` and passes here. ## What the RFC requires RFC 8656 Section 3 defines the relayed transport address as *"A transport address on the TURN server that is used for relaying packets between the client and a peer"*, and Section 7.2 leaves the server two outcomes for an Allocate carrying `REQUESTED-ADDRESS-FAMILY`: > If the server does not support the address family requested by the client in REQUESTED-ADDRESS-FAMILY, or if the allocation of the requested address family is disabled by local policy, it MUST generate an Allocate error response, and it MUST include an ERROR-CODE attribute with the 440 (Address Family not Supported) response code. Section 7.3 puts one duty on the client: > If the client receives an Allocate success response, then it MUST check that the mapped address and the relayed transport address or addresses are part of an address family or families that the client understands and is prepared to handle. ## 1. `--external-ip` was applied across address families (server) `external_ip` is a single `ioa_addr` and `--external-ip` may only be given once, but it was substituted for the relayed address of *both* families. On a server with an IPv4 `--external-ip`, an IPv6 allocation was therefore advertised under an unrelated IPv4 address that no peer can reach — neither an IPv6 relay nor a 440. ``` --relay-ip=127.0.0.1 --relay-ip=::1 --external-ip=127.0.0.1, client over ::1 with -x INFO IPv4. Received relay addr: 127.0.0.1:30654 INFO clnet_allocate: !!!: relay addr cannot be received (2) ``` The relay socket really was IPv6; only the advertised address was wrong. As a second effect, the substituted family made the response draw `LIFETIME` from the IPv4 relay session that was never created for that allocation. `turn_set_xor_relayed_addr()` now applies the external IP only to a relay of its own family, which is the same rule the `ioa_addr_add_mapping()` setup path already follows in `mainrelay.c`. The option's documentation is updated to match. ## 2. `turnutils_uclient` rejected an IPv6 relay it can handle (client) The acceptance test took an IPv6 relayed address only when IPv6 had been requested *explicitly*, and consulted the global `default_address_family` rather than the `af` argument the caller passed to `clnet_allocate()`. Against a server running `--allocation-default-address-family=keep` (or the deprecated `--keep-address-family`), which answers an IPv6 client with an IPv6 relay and no `REQUESTED-ADDRESS-FAMILY` in play, a perfectly usable relay was refused — as reported in the issue thread. Section 7.3 asks only that the client refuse a family it *cannot handle*, so the check now rejects solely on a contradiction with the family this allocation requested. Note this does not make `keep` conformant — it is a deliberate deviation, and `turnserver -h` says as much (*"It breaks RFC6156 section-4.2 (violates default IPv4)"*). The client simply stops rejecting a relay it is able to use. ## Testing `examples/run_tests_ipv6_relay.sh` covers both configurations end to end, asserting a completed relayed round trip rather than just a successful allocate. It uses ports off the defaults the other scripts share, and SKIPs where no IPv6 loopback is bindable. Wired into the clang, cmake, linux and macos workflows. Verified red before green: reverting only `src/` and rebuilding turns both cases into failures. - macOS: build + `ctest` 18/18; Linux container: build + `ctest` 17/17 - Linux: `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`, `run_tests_ipv6_relay.sh`, `run_tests_dtls_default.sh` — all OK - Fuzzing smoke tests (ASan targets 0 and 1) — OK - Packaged Docker image (debian, arm64) + bats — 19 tests, 0 failures, 2 skipped ## Deliberately not in this PR - The dual-allocation `508` guard when `external_ip_set` is left as is; with family-aware rewriting it could now be relaxed to allow a v4-rewritten + v6-as-is pair, but that is a separate behavioral change. - `--external-ip` stays single-family. A per-family form would be the complete answer for IPv6-NAT deployments. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>