From de46202ab436913917e0222a7554077182cc4bc0 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 24 Feb 2026 22:07:33 +0000 Subject: [PATCH] Fix 27s call ringing delay: TURN credential mismatch and STUN DNS TTL=0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs caused ICE candidate gathering to fail and timeout (~27 seconds) before falling back to direct media on every call: 1. TURN credential mismatch — when TURN_PASSWORD was empty in .env, coturn defaulted to "changeme" but the entrypoint auto-generated a different random password for Asterisk. Every TURN auth attempt failed ("check_stun_auth: user easyasterisk credentials are incorrect"). 2. STUN DNS TTL=0 — using the FQDN as stunaddr caused DNS resolution that returned TTL=0, making Asterisk cancel recurring STUN resolution entirely. Since coturn runs on the same host (network_mode: host), rtp.conf now uses 127.0.0.1 which needs no DNS at all. Also documents the Android Call Integration audio issue (ConnectionService routes audio through the native telephony path, breaking VoIP RTP). https://claude.ai/code/session_01KWVtEt9MmZdywcu7WmgchX --- .env.example | 4 ++-- README.md | 16 ++++++++++++++++ docker-compose.yml | 2 +- docker/entrypoint.sh | 30 +++++++++++++++++------------- 4 files changed, 36 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index b1aa69c..381484a 100644 --- a/.env.example +++ b/.env.example @@ -60,8 +60,8 @@ VLAN_SUBNETS= # ── TURN/STUN Settings ────────────────────────────────────── # Used by coturn for TURN relay authentication. -# If TURN_PASSWORD is empty, a random password is generated on -# first startup and saved to /etc/easy-asterisk/config. +# Both coturn and Asterisk must use the SAME password. +# If empty, both default to "changeme" — set a real password here. # # These credentials are shared between coturn and Asterisk. # SIP clients do NOT need these - only the server uses them. diff --git a/README.md b/README.md index 69721c7..844f547 100644 --- a/README.md +++ b/README.md @@ -663,6 +663,22 @@ For **any SIP app**: - FQDN mode enables ICE (Interactive Connectivity Establishment) which handles network changes better - Alternatively, keep your phone on one network type (WiFi or mobile data) during calls +### "No audio when Call Integration is enabled" (Android) + +Some Android SIP apps (Sipnetic, Olinuxino, etc.) offer a "Call Integration" feature that routes calls through Android's native dialer (ConnectionService API). When enabled: + +- Incoming calls appear in the native phone app +- The headset media button works for answering +- **But the caller may get no audio** + +**Why this happens:** +Android's ConnectionService routes audio through the native telephony audio path, which doesn't always relay VoIP RTP audio correctly. This is a client-side Android issue, not an Asterisk problem. It affects both stock Android and LineageOS. + +**Fix:** +- Disable "Call Integration" in your SIP app's settings +- Use the SIP app's own answer button instead of the headset media button +- If you need headset button support, check if your SIP app supports media button handling without Call Integration + ### "My IP changed and FQDN stopped working" See [Dynamic IP Handling](#dynamic-ip-handling) section. You need to set up DDNS. diff --git a/docker-compose.yml b/docker-compose.yml index 5c97279..f09a53c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -55,7 +55,7 @@ services: - TURN_ENABLED=y - TURN_SERVER=${DOMAIN_NAME:?}:${TURN_PORT:-3478} - TURN_USERNAME=${TURN_USERNAME:-easyasterisk} - - TURN_PASSWORD=${TURN_PASSWORD:-} + - TURN_PASSWORD=${TURN_PASSWORD:-changeme} # ── RTP Port Range ── - RTP_START=${RTP_START:-10000} diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index ec51151..e82b70e 100644 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -54,17 +54,15 @@ else log_warn "Could not detect public IP. Set PUBLIC_IP in .env" fi -# ── 3. Generate TURN password if not provided ───────────────── +# ── 3. TURN credentials ───────────────────────────────────────── +# The password MUST match what coturn was started with. In Docker, both +# read from the same env-var / .env file, so we use the value as-is. +# Auto-generating a different password here would create a mismatch +# (coturn is already running with ITS copy of the env-var). TURN_USERNAME="${TURN_USERNAME:-easyasterisk}" -if [[ -z "${TURN_PASSWORD:-}" ]] || [[ "${TURN_PASSWORD}" == "changeme" ]]; then - # Check if we already generated one previously - if [[ -f "$CONFIG_FILE" ]] && grep -q "^TURN_PASSWORD=" "$CONFIG_FILE"; then - TURN_PASSWORD=$(grep "^TURN_PASSWORD=" "$CONFIG_FILE" | cut -d'"' -f2) - fi - if [[ -z "${TURN_PASSWORD:-}" ]] || [[ "${TURN_PASSWORD}" == "changeme" ]]; then - TURN_PASSWORD=$(gen_password) - log_info "Generated TURN password (saved to config)" - fi +TURN_PASSWORD="${TURN_PASSWORD:-changeme}" +if [[ "${TURN_PASSWORD}" == "changeme" ]]; then + log_warn "TURN password is the default 'changeme' — set TURN_PASSWORD in .env for better security" fi # ── 4. Detect local network ────────────────────────────────── @@ -282,15 +280,21 @@ EOF fi # ── rtp.conf (always regenerated - includes TURN credentials) ── -log_info "Configuring RTP with ICE + STUN + TURN..." +# Use 127.0.0.1 for stunaddr/turnaddr because coturn runs on the same host +# (network_mode: host). Using the FQDN would cause DNS resolution, and if the +# DNS TTL is 0 Asterisk cancels recurring resolution — breaking ICE entirely +# and adding a ~27-second timeout delay to every call. +turn_port="${turn_server##*:}" +local_turn="127.0.0.1:${turn_port:-3478}" +log_info "Configuring RTP with ICE + STUN + TURN (local: ${local_turn})..." cat > /etc/asterisk/rtp.conf << EOF [general] rtpstart=${RTP_START:-10000} rtpend=${RTP_END:-20000} strictrtp=yes icesupport=yes -stunaddr=${turn_server} -turnaddr=${turn_server} +stunaddr=${local_turn} +turnaddr=${local_turn} turnusername=${TURN_USERNAME} turnpassword=${TURN_PASSWORD} EOF