From: Evgeniy Khramtsov Date: Thu, 8 May 2014 12:08:07 +0000 (+0400) Subject: TURN support (EJAB-1017) X-Git-Tag: 14.05~5 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=90a5c054d4471caff13915ac347bf4ccac7f462b;p=ejabberd TURN support (EJAB-1017) --- diff --git a/doc/guide.tex b/doc/guide.tex index 5f8d13de9..0919c689a 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -398,7 +398,7 @@ Some options that you may be interested in modifying: Enable Stream Compression (XEP-0138) using zlib. \titem{--enable-stun} - Enable STUN support (see section \ref{stun}). + Enable STUN/TURN support (see section \ref{stun}). \titem{--enable-iconv} Enable iconv support. This is needed for \term{mod\_irc} (see seciont \ref{modirc}). @@ -891,9 +891,13 @@ The available modules, their purpose and the options allowed by each one are: \footahref{http://tools.ietf.org/html/rfc3261}{RFC 3261}.\\ Options: \texttt{certfile}, \texttt{tls} \titem{\texttt{ejabberd\_stun}} - Handles STUN Binding requests as defined in - \footahref{http://tools.ietf.org/html/rfc5389}{RFC 5389}.\\ - Options: \texttt{certfile} + Handles STUN/TURN requests as defined in + \footahref{http://tools.ietf.org/html/rfc5389}{RFC 5389} and + \footahref{http://tools.ietf.org/html/rfc5766}{RFC 5766}.\\ + Options: \texttt{certfile}, \texttt{tls}, \texttt{use\_turn}, \texttt{turn\_ip}, + \texttt{turn\_port\_range}, \texttt{turn\_max\_allocations}, + \texttt{turn\_max\_permissions}, \texttt{shaper}, \texttt{server\_name}, + \texttt{auth\_realm}, \texttt{auth\_type} \titem{\texttt{ejabberd\_http}} Handles incoming HTTP connections.\\ Options: \texttt{captcha}, \texttt{certfile}, \texttt{default\_host}, \texttt{http\_bind}, \texttt{http\_poll}, @@ -1991,22 +1995,57 @@ listen: ... \end{verbatim} -\makesubsection{stun}{STUN} +\makesubsection{stun}{STUN and TURN} \ind{options!stun}\ind{stun} -\ejabberd{} is able to act as a stand-alone STUN server -(\footahref{http://tools.ietf.org/html/rfc5389}{RFC 5389}). Currently only Binding usage -is supported. In that role \ejabberd{} helps clients with ICE (\footahref{http://tools.ietf.org/html/rfc5245}{RFC 5245}) or Jingle ICE (\xepref{0176}) support to discover their external addresses and ports. +\ejabberd{} is able to act as a stand-alone STUN/TURN server +(\footahref{http://tools.ietf.org/html/rfc5389}{RFC 5389}/\footahref{http://tools.ietf.org/html/rfc5766}{RFC 5766}). In that role \ejabberd{} helps clients with ICE (\footahref{http://tools.ietf.org/html/rfc5245}{RFC 5245}) or Jingle ICE (\xepref{0176}) support to discover their external addresses and ports and to relay media traffic when it is impossible to establish direct +peer-to-peer connection. You should configure \term{ejabberd\_stun} listening module as described in \ref{listened} section. -If \option{certfile} option is defined, \ejabberd{} multiplexes TCP and -TLS over TCP connections on the same port. Obviously, \option{certfile} option -is defined for \term{tcp} only. Note however that TCP or TLS over TCP -support is not required for Binding usage and is reserved for -\footahref{http://tools.ietf.org/html/draft-ietf-behave-turn-16}{TURN} -functionality. Feel free to configure \term{udp} transport only. +The specific configurable options are: +\begin{description} + \titem{tls: true|false} + If enabled, \option{certfile} option must be set, otherwise \ejabberd{} + will not be able to accept TLS connections. Obviously, this option + makes sense for \term{tcp} transport only. The default is \term{false}. + \titem{certfile: Path} + Path to the certificate file. Only makes sense when \option{tls} is set. + \titem{use\_turn: true|false} + Enables/disables TURN (media relay) functionality. The default is \term{false}. + \titem{turn\_ip: String} + The IPv4 address advertised by your TURN server. The address should not be NAT'ed + or firewalled. There is not default, so you should set this option explicitly. + Implies \term{use\_turn}. + \titem{turn\_min\_port: Integer} + Together with \option{turn\_max\_port} forms port range to allocate from. + The default is 49152. Implies \term{use\_turn}. + \titem{turn\_max\_port: Integer} + Together with \option{turn\_min\_port} forms port range to allocate from. + The default is 65535. Implies \term{use\_turn}. + \titem{turn\_max\_allocations: Integer|unlimited} + Maximum number of TURN allocations available from the particular IP address. + The default value is 10. Implies \term{use\_turn}. + \titem{turn\_max\_permissions: Integer|unlimited} + Maximum number of TURN permissions available from the particular IP address. + The default value is 10. Implies \term{use\_turn}. + \titem{auth\_type: user|anonymous} + Which authentication type to use for TURN allocation requests. When type \term{user} + is set, ejabberd authentication backend is used. For \term{anonymous} type + no authentication is performed (not recommended for public services). + The default is \term{user}. Implies \term{use\_turn}. + \titem{auth\_realm: String} + When \option{auth\_type} is set to \term{user} and you have several virtual + hosts configured you should set this option explicitly to the virtual host + you want to serve on this particular listening port. Implies \term{use\_turn}. + \titem{shaper: Atom} + For \term{tcp} transports defines shaper to use. The default is \term{none}. + \titem{server\_name: String} + Defines software version to return with every response. The default is the + STUN library version. +\end{description} -Example configuration: +Example configuration with disabled TURN functionality (STUN only): \begin{verbatim} listen: ... @@ -2024,18 +2063,41 @@ listen: ... \end{verbatim} +Example configuration with TURN functionality. Note that STUN is always +enabled if TURN is enabled. Here, only UDP section is shown: +\begin{verbatim} +listen: + ... + - + port: 3478 + transport: udp + use_turn: true + turn_ip: 10.20.30.1 + module: ejabberd_stun + ... +\end{verbatim} + You also need to configure DNS SRV records properly so clients can easily discover a -STUN server serving your XMPP domain. Refer to section +STUN/TURN server serving your XMPP domain. Refer to section \footahref{http://tools.ietf.org/html/rfc5389\#section-9}{DNS Discovery of a Server} -of \footahref{http://tools.ietf.org/html/rfc5389}{RFC 5389} for details. +of \footahref{http://tools.ietf.org/html/rfc5389}{RFC 5389} and section +\footahref{http://tools.ietf.org/html/rfc5766\#section-6}{Creating an Allocation} +of \footahref{http://tools.ietf.org/html/rfc5766}{RFC 5766} for details. -Example DNS SRV configuration: +Example DNS SRV configuration for STUN only: \begin{verbatim} _stun._udp IN SRV 0 0 3478 stun.example.com. _stun._tcp IN SRV 0 0 3478 stun.example.com. _stuns._tcp IN SRV 0 0 5349 stun.example.com. \end{verbatim} +And you should also add these in the case if TURN is enabled: +\begin{verbatim} +_turn._udp IN SRV 0 0 3478 turn.example.com. +_turn._tcp IN SRV 0 0 3478 turn.example.com. +_turns._tcp IN SRV 0 0 5349 turn.example.com. +\end{verbatim} + \makesubsection{sip}{SIP} \ind{options!sip}\ind{sip} diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index 844080a04..02a2f3fbd 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -366,7 +366,6 @@ start_listener2(Port, Module, Opts) -> %% It is only required to start the supervisor in some cases. %% But it doesn't hurt to attempt to start it for any listener. %% So, it's normal (and harmless) that in most cases this call returns: {error, {already_started, pid()}} - maybe_start_stun(Module), maybe_start_sip(Module), start_module_sup(Port, Module), start_listener_sup(Port, Module, Opts). @@ -482,13 +481,6 @@ is_frontend(_) -> false. strip_frontend({frontend, Module}) -> Module; strip_frontend(Module) when is_atom(Module) -> Module. --spec maybe_start_stun(module()) -> any(). - -maybe_start_stun(ejabberd_stun) -> - ejabberd:start_app(p1_stun); -maybe_start_stun(_) -> - ok. - maybe_start_sip(esip_socket) -> ejabberd:start_app(esip); maybe_start_sip(_) -> @@ -671,12 +663,8 @@ prepare_ip(IP) when is_list(IP) -> prepare_ip(IP) when is_binary(IP) -> prepare_ip(binary_to_list(IP)). -prepare_mod(ejabberd_stun) -> - prepare_mod(stun); prepare_mod(ejabberd_sip) -> prepare_mod(sip); -prepare_mod(stun) -> - stun; prepare_mod(sip) -> esip_socket; prepare_mod(Mod) when is_atom(Mod) -> diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl new file mode 100644 index 000000000..89cbebf84 --- /dev/null +++ b/src/ejabberd_stun.erl @@ -0,0 +1,83 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2014, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 8 May 2014 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(ejabberd_stun). + +%% API +-export([tcp_init/2, udp_init/2, udp_recv/5, start/2, socket_type/0]). + +-include("ejabberd.hrl"). +-include("logger.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +tcp_init(Socket, Opts) -> + ejabberd:start_app(p1_stun), + stun:tcp_init(Socket, prepare_turn_opts(Opts)). + +udp_init(Socket, Opts) -> + ejabberd:start_app(p1_stun), + stun:udp_init(Socket, prepare_turn_opts(Opts)). + +udp_recv(Socket, Addr, Port, Packet, Opts) -> + stun:udp_recv(Socket, Addr, Port, Packet, Opts). + +start(Opaque, Opts) -> + stun:start(Opaque, Opts). + +socket_type() -> + raw. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +prepare_turn_opts(Opts) -> + UseTurn = proplists:get_bool(use_turn, Opts), + prepare_turn_opts(Opts, UseTurn). + +prepare_turn_opts(Opts, _UseTurn = false) -> + Opts; +prepare_turn_opts(Opts, _UseTurn = true) -> + NumberOfMyHosts = length(?MYHOSTS), + case proplists:get_value(turn_ip, Opts) of + undefined -> + ?WARNING_MSG("option 'turn_ip' is undefined, " + "more likely the TURN relay won't be working " + "properly", []); + _ -> + ok + end, + AuthFun = fun ejabberd_auth:get_password_s/2, + Shaper = gen_mod:get_opt(shaper, Opts, + fun(S) when is_atom(S) -> S end, + none), + AuthType = gen_mod:get_opt(auth_type, Opts, + fun(anonymous) -> anonymous; + (user) -> user + end, user), + Realm = case gen_mod:get_opt(auth_realm, Opts, fun iolist_to_binary/1) of + undefined when AuthType == user -> + if NumberOfMyHosts > 1 -> + ?WARNING_MSG("you have several virtual " + "hosts configured, but option " + "'auth_realm' is undefined and " + "'auth_type' is set to 'user', " + "more likely the TURN relay won't " + "be working properly. Using ~s as " + "a fallback", [?MYNAME]); + true -> + ok + end, + [{auth_realm, ?MYNAME}]; + _ -> + [] + end, + MaxRate = shaper:get_max_rate(Shaper), + Realm ++ [{auth_fun, AuthFun},{shaper, MaxRate} | + lists:keydelete(shaper, 1, Opts)]. diff --git a/src/shaper.erl b/src/shaper.erl index a31ec5560..4904d391e 100644 --- a/src/shaper.erl +++ b/src/shaper.erl @@ -27,7 +27,7 @@ -author('alexey@process-one.net'). --export([start/0, new/1, new1/1, update/2, +-export([start/0, new/1, new1/1, update/2, get_max_rate/1, transform_options/1, load_from_config/0]). -include("ejabberd.hrl"). @@ -74,6 +74,18 @@ load_from_config() -> {error, Err} end. +-spec get_max_rate(atom()) -> none | non_neg_integer(). + +get_max_rate(none) -> + none; +get_max_rate(Name) -> + case ets:lookup(shaper, {Name, global}) of + [#shaper{maxrate = R}] -> + R; + [] -> + none + end. + -spec new(atom()) -> shaper(). new(none) ->