TURN over TCP and TLS

Tip

PJSUA-LIB readers — symbol equivalents are listed at the bottom of this page.

Overview

By default a TURN client reaches the TURN server over UDP and the allocated relay also runs over UDP (RFC 5766, updated by RFC 8656). On networks that block outbound UDP — restrictive corporate firewalls, some hotel/airport networks, certain mobile carriers — the TURN allocation never completes and TURN candidates are unavailable. The remedy is to reach the TURN server over TCP or TLS instead. Connection-oriented transports traverse most firewalls that block UDP outright, and TLS additionally encrypts the client-server leg.

PJSIP supports all three client-server transports via the pj_turn_tp_type enum:

pj_turn_tp_type value

Client-to-server transport

PJ_TURN_TP_UDP

UDP datagrams (default; lowest latency)

PJ_TURN_TP_TCP

TCP connection

PJ_TURN_TP_TLS

TLS over TCP

All three are part of base TURN (RFC 5766 / RFC 8656). The same enum is reused for the allocation transport requested from the server, but PJ_TURN_TP_TLS is only valid as a client-server transport, never as an allocation transport — see Allocations vs the data path below.

Client-server TCP and TLS support is long-standing in PJNATH (the TURN integration originally landed via ticket #485 and TURN TLS via #1017). #2754 is a separate, later addition that implements RFC 6062 TCP allocations, i.e. TCP between the TURN server and a peer — useful for application protocols that need a reliable byte stream end-to-end. SIP media does not use this.

Choosing a connection type

  • UDP — start here. Lowest latency, fewest moving parts. Use unless you have evidence UDP is being blocked.

  • TCP — fall back when UDP is dropped. Slightly higher latency (head-of-line blocking when the network is lossy) but reliable through almost every firewall.

  • TLS — use when network policy specifically requires encrypted traffic to leave the network (e.g. corporate compliance, DPI bypass), or when the TURN server only exposes a TLS listener on port 5349.

Some deployments configure two account-level TURN entries — one UDP primary, one TLS backup on a different port — and let the application decide which to use based on local probing. PJSIP itself does not do automatic UDP→TCP→TLS fallback; the application picks one.

PJSUA2 — TURN over UDP or TCP

Set pj::AccountNatConfig::turnConnType to one of PJ_TURN_TP_UDP or PJ_TURN_TP_TCP:

AccountConfig acfg;
// ... existing config ...
acfg.natConfig.iceEnabled    = true;
acfg.natConfig.turnEnabled   = true;
acfg.natConfig.turnServer    = "turn.example.com:3478";
acfg.natConfig.turnConnType  = PJ_TURN_TP_TCP;
acfg.natConfig.turnUserName  = "user";
acfg.natConfig.turnPassword  = "secret";

try {
    account.create(acfg);
} catch(Error& err) {
}

Warning

The PJSUA2 AccountNatConfig does not currently surface the TURN TLS settings struct (pj_turn_sock_tls_cfg). The doxygen on pj::AccountNatConfig::turnConnType lists only PJ_TURN_TP_UDP and PJ_TURN_TP_TCP. Setting turnConnType to PJ_TURN_TP_TLS in PJSUA2 will attempt a TLS connection, but with the TLS configuration left at its zero-initialised default the verification behaviour is whatever the SSL backend defaults to — typically no CA verification, which is insecure. To configure TURN TLS properly — CA file, client cert, ciphers — drop down to the PJSUA-LIB pjsua_turn_config struct (next section).

PJSUA-LIB — TURN over UDP, TCP, or TLS

The PJSUA-LIB pjsua_turn_config struct exposes the full TURN configuration including TLS:

pjsua_acc_config acc_cfg;
pjsua_acc_config_default(&acc_cfg);

acc_cfg.turn_cfg_use = PJSUA_TURN_CONFIG_USE_CUSTOM;

pjsua_turn_config *t = &acc_cfg.turn_cfg;
t->enable_turn   = PJ_TRUE;
t->turn_server   = pj_str("turn.example.com:5349");
t->turn_conn_type = PJ_TURN_TP_TLS;

t->turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
t->turn_auth_cred.data.static_cred.username    = pj_str("user");
t->turn_auth_cred.data.static_cred.data_type   = PJ_STUN_PASSWD_PLAIN;
t->turn_auth_cred.data.static_cred.data        = pj_str("secret");
t->turn_auth_cred.data.static_cred.realm       = pj_str("example.com");

/* TLS-specific bits — only applicable when turn_conn_type is TLS */
t->turn_tls_setting.ca_list_file = pj_str("/etc/ssl/certs/turn-ca.pem");
t->turn_tls_setting.cert_file    = pj_str("/etc/pjsip/turn-client.pem");
t->turn_tls_setting.privkey_file = pj_str("/etc/pjsip/turn-client.key");

pjsua_acc_id acc_id;
pjsua_acc_add(&acc_cfg, PJ_TRUE, &acc_id);

For the global default (applied to accounts that keep turn_cfg_use = PJSUA_TURN_CONFIG_USE_DEFAULT) the same fields exist on pjsua_media_config directly (enable_turn, turn_server, turn_conn_type, turn_auth_cred, turn_tls_setting).

The pj_turn_sock_tls_cfg struct in pjnath/include/pjnath/turn_sock.h includes additional fields:

  • ca_buf / cert_buf / privkey_buf — for in-memory credentials instead of file paths (useful on platforms without a writable filesystem)

  • cert_lookup / cert_direct — backend-specific options (Windows Schannel certificate store; OpenSSL direct credentials)

  • password — passphrase for an encrypted private key

  • ssock_param — full pj_ssl_sock_param (protocols, ciphers, ECDH curves, signature algorithms, renegotiation, socket options, timeout). Default protocol is PJ_TURN_TLS_DEFAULT_PROTO (TLS 1.0 + 1.1 + 1.2).

pjsua CLI

The pjsua sample app exposes the full set:

$ ./pjsua --use-ice --use-turn \
          --turn-srv turn.example.com:5349 \
          --turn-tls \
          --turn-tls-ca-file /etc/ssl/certs/turn-ca.pem \
          --turn-tls-cert-file /etc/pjsip/turn-client.pem \
          --turn-tls-privkey-file /etc/pjsip/turn-client.key \
          --turn-user user --turn-passwd secret

Relevant flags:

  • --turn-srv NAME:PORT — TURN server (mandatory)

  • --turn-tcp — use TCP connection to TURN

  • --turn-tls — use TLS connection to TURN

  • --turn-tls-ca-file — CA bundle for server verification

  • --turn-tls-cert-file / --turn-tls-privkey-file / --turn-tls-privkey-pwd — client cert (when mutual TLS is required)

  • --turn-tls-cipher — preferred cipher list

  • --turn-tls-neg-timeout — TLS handshake timeout

Without --turn-tcp or --turn-tls the default is UDP.

Allocations vs the data path

TURN has two layers of transport:

  1. Client-to-server (control + data tunnel) — set by turn_conn_type. Carries TURN messages and (via channel data / Send indications) the media itself.

  2. Server-to-peer (the actual relayed media) — set by pj_turn_alloc_param::peer_conn_type. UDP by default.

For typical SIP-over-ICE applications, leaving peer_conn_type at its UDP default is correct — the peer reaches the relay over UDP even if you reach the server over TCP/TLS. RFC 6062 TCP allocations (where the peer-side transport is also TCP) require an extra pj_turn_sock_connect() call per peer and are rarely useful in SIP/SDP media; they are intended for application-specific protocols that need reliable byte streams.

Note

PJSIP does not surface the TURN allocation’s peer_conn_type through pjsua_turn_config — PJSUA-LIB and PJSUA2 always allocate a UDP relay regardless of turn_conn_type. Standalone PJNATH applications can set pj_turn_alloc_param::peer_conn_type when needed.

Defaults and ports

These are compile-time settings, overridable in pjlib/include/pj/config_site.h.

Interaction with ICE

The TURN connection type only affects how the relayed candidate is allocated. Once the allocation succeeds, the relayed candidate is just another entry in the ICE candidate list and pairs against the peer’s candidates by normal ICE rules. Whether ICE picks the relayed candidate or some other (cheaper) candidate depends on the connectivity-check outcome — TURN is the path of last resort.

Trickle ICE (Using Trickle ICE) and TURN TCP/TLS combine well — the TURN allocation handshake is the largest single source of pre-trickle ICE setup latency, so the speedup from trickling is most visible on calls that actually use the relayed candidate.

PJSUA-LIB equivalents

PJSUA2

PJSUA-LIB

pj::AccountNatConfig::turnEnabled

pjsua_turn_config::enable_turn

pj::AccountNatConfig::turnServer

pjsua_turn_config::turn_server

pj::AccountNatConfig::turnConnType (UDP/TCP only)

pjsua_turn_config::turn_conn_type (UDP/TCP/TLS)

pj::AccountNatConfig::turnUserName / turnPassword / turnPasswordType

pjsua_turn_config::turn_auth_cred

(not exposed)

pjsua_turn_config::turn_tls_setting (pj_turn_sock_tls_cfg)

References