diff options
Diffstat (limited to 'src/stun')
-rw-r--r-- | src/stun/Makefile.in | 2 | ||||
-rw-r--r-- | src/stun/ejabberd_stun.erl | 224 | ||||
-rw-r--r-- | src/stun/stun.hrl | 88 | ||||
-rw-r--r-- | src/stun/stun_codec.erl | 282 |
4 files changed, 274 insertions, 322 deletions
diff --git a/src/stun/Makefile.in b/src/stun/Makefile.in index b6e28d953..e77da8452 100644 --- a/src/stun/Makefile.in +++ b/src/stun/Makefile.in @@ -14,7 +14,7 @@ EFLAGS += -pz .. # make debug=true to compile Erlang module with debug informations. ifdef debug - EFLAGS+=+debug_info +export_all + EFLAGS+=+debug_info endif OUTDIR = .. diff --git a/src/stun/ejabberd_stun.erl b/src/stun/ejabberd_stun.erl index dbc681cb7..1046fff11 100644 --- a/src/stun/ejabberd_stun.erl +++ b/src/stun/ejabberd_stun.erl @@ -30,35 +30,31 @@ -behaviour(gen_fsm). %% API --export([start_link/2, - start/2, - socket_type/0, +-export([start_link/2, start/2, socket_type/0, udp_recv/5]). %% gen_fsm callbacks --export([init/1, - handle_event/3, - handle_sync_event/4, - handle_info/3, - terminate/3, - code_change/4]). +-export([init/1, handle_event/3, handle_sync_event/4, + handle_info/3, terminate/3, code_change/4]). %% gen_fsm states --export([wait_for_tls/2, - session_established/2]). +-export([wait_for_tls/2, session_established/2]). -include("ejabberd.hrl"). + -include("stun.hrl"). --define(MAX_BUF_SIZE, 64*1024). %% 64kb --define(TIMEOUT, 10000). %% 10 sec +-define(MAX_BUF_SIZE, 64 * 1024). + +-define(TIMEOUT, 10000). --record(state, {sock, - sock_mod = gen_tcp, - certfile, - peer, - tref, - buf = <<>>}). +-record(state, + {sock :: inet:socket() | tls:tls_socket(), + sock_mod = gen_tcp :: gen_udp | gen_tcp | tls, + certfile :: binary(), + peer = {{0,0,0,0}, 0} :: {inet:ip_address(), inet:port_number()}, + tref = make_ref() :: reference(), + buf = <<>> :: binary()}). %%==================================================================== %% API @@ -69,23 +65,20 @@ start({gen_tcp, Sock}, Opts) -> start_link(Sock, Opts) -> gen_fsm:start_link(?MODULE, [Sock, Opts], []). -socket_type() -> - raw. +socket_type() -> raw. udp_recv(Sock, Addr, Port, Data, _Opts) -> case stun_codec:decode(Data) of - {ok, Msg, <<>>} -> - ?DEBUG("got:~n~p", [Msg]), - case process(Addr, Port, Msg) of - RespMsg when is_record(RespMsg, stun) -> - ?DEBUG("sent:~n~p", [RespMsg]), - Data1 = stun_codec:encode(RespMsg), - gen_udp:send(Sock, Addr, Port, Data1); - _ -> - ok - end; - _ -> - ok + {ok, Msg, <<>>} -> + ?DEBUG("got:~n~p", [Msg]), + case process(Addr, Port, Msg) of + RespMsg when is_record(RespMsg, stun) -> + ?DEBUG("sent:~n~p", [RespMsg]), + Data1 = stun_codec:encode(RespMsg), + gen_udp:send(Sock, Addr, Port, Data1); + _ -> ok + end; + _ -> ok end. %%==================================================================== @@ -93,38 +86,38 @@ udp_recv(Sock, Addr, Port, Data, _Opts) -> %%==================================================================== init([Sock, Opts]) -> case inet:peername(Sock) of - {ok, Addr} -> - inet:setopts(Sock, [{active, once}]), - TRef = erlang:start_timer(?TIMEOUT, self(), stop), - State = #state{sock = Sock, peer = Addr, tref = TRef}, - case proplists:get_value(certfile, Opts) of - undefined -> - {ok, session_established, State}; - CertFile -> - {ok, wait_for_tls, State#state{certfile = CertFile}} - end; - Err -> - Err + {ok, Addr} -> + inet:setopts(Sock, [{active, once}]), + TRef = erlang:start_timer(?TIMEOUT, self(), stop), + State = #state{sock = Sock, peer = Addr, tref = TRef}, + case proplists:get_value(certfile, Opts) of + undefined -> {ok, session_established, State}; + CertFile -> + {ok, wait_for_tls, State#state{certfile = CertFile}} + end; + Err -> Err end. wait_for_tls(Event, State) -> - ?INFO_MSG("unexpected event in wait_for_tls: ~p", [Event]), + ?INFO_MSG("unexpected event in wait_for_tls: ~p", + [Event]), {next_state, wait_for_tls, State}. -session_established(Msg, State) when is_record(Msg, stun) -> +session_established(Msg, State) + when is_record(Msg, stun) -> ?DEBUG("got:~n~p", [Msg]), {Addr, Port} = State#state.peer, case process(Addr, Port, Msg) of - Resp when is_record(Resp, stun) -> - ?DEBUG("sent:~n~p", [Resp]), - Data = stun_codec:encode(Resp), - (State#state.sock_mod):send(State#state.sock, Data); - _ -> - ok + Resp when is_record(Resp, stun) -> + ?DEBUG("sent:~n~p", [Resp]), + Data = stun_codec:encode(Resp), + (State#state.sock_mod):send(State#state.sock, Data); + _ -> ok end, {next_state, session_established, State}; session_established(Event, State) -> - ?INFO_MSG("unexpected event in session_established: ~p", [Event]), + ?INFO_MSG("unexpected event in session_established: ~p", + [Event]), {next_state, session_established, State}. handle_event(_Event, StateName, State) -> @@ -133,42 +126,38 @@ handle_event(_Event, StateName, State) -> handle_sync_event(_Event, _From, StateName, State) -> {reply, {error, badarg}, StateName, State}. -handle_info({tcp, Sock, TLSData}, wait_for_tls, State) -> +handle_info({tcp, Sock, TLSData}, wait_for_tls, + State) -> Buf = <<(State#state.buf)/binary, TLSData/binary>>, - %% Check if the initial message is a TLS handshake case Buf of - _ when size(Buf) < 3 -> - {next_state, wait_for_tls, - update_state(State#state{buf = Buf})}; - <<_:16, 1, _/binary>> -> - TLSOpts = [{certfile, State#state.certfile}], - {ok, TLSSock} = tls:tcp_to_tls(Sock, TLSOpts), - NewState = State#state{sock = TLSSock, - buf = <<>>, - sock_mod = tls}, - case tls:recv_data(TLSSock, Buf) of - {ok, Data} -> - process_data(session_established, NewState, Data); - _Err -> - {stop, normal, NewState} - end; - _ -> - process_data(session_established, State, TLSData) + _ when byte_size(Buf) < 3 -> + {next_state, wait_for_tls, + update_state(State#state{buf = Buf})}; + <<_:16, 1, _/binary>> -> + TLSOpts = [{certfile, State#state.certfile}], + {ok, TLSSock} = tls:tcp_to_tls(Sock, TLSOpts), + NewState = State#state{sock = TLSSock, buf = <<>>, + sock_mod = tls}, + case tls:recv_data(TLSSock, Buf) of + {ok, Data} -> + process_data(session_established, NewState, Data); + _Err -> {stop, normal, NewState} + end; + _ -> process_data(session_established, State, TLSData) end; handle_info({tcp, _Sock, TLSData}, StateName, #state{sock_mod = tls} = State) -> case tls:recv_data(State#state.sock, TLSData) of - {ok, Data} -> - process_data(StateName, State, Data); - _Err -> - {stop, normal, State} + {ok, Data} -> process_data(StateName, State, Data); + _Err -> {stop, normal, State} end; handle_info({tcp, _Sock, Data}, StateName, State) -> process_data(StateName, State, Data); handle_info({tcp_closed, _Sock}, _StateName, State) -> ?DEBUG("connection reset by peer", []), {stop, normal, State}; -handle_info({tcp_error, _Sock, Reason}, _StateName, State) -> +handle_info({tcp_error, _Sock, Reason}, _StateName, + State) -> ?DEBUG("connection error: ~p", [Reason]), {stop, normal, State}; handle_info({timeout, TRef, stop}, _StateName, @@ -188,58 +177,55 @@ code_change(_OldVsn, StateName, State, _Extra) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -process(Addr, Port, #stun{class = request, unsupported = []} = Msg) -> +process(Addr, Port, + #stun{class = request, unsupported = []} = Msg) -> Resp = prepare_response(Msg), - if Msg#stun.method == ?STUN_METHOD_BINDING -> - case stun_codec:version(Msg) of - old -> - Resp#stun{class = response, - 'MAPPED-ADDRESS' = {Addr, Port}}; - new -> - Resp#stun{class = response, - 'XOR-MAPPED-ADDRESS' = {Addr, Port}} - end; + if Msg#stun.method == (?STUN_METHOD_BINDING) -> + case stun_codec:version(Msg) of + old -> + Resp#stun{class = response, + 'MAPPED-ADDRESS' = {Addr, Port}}; + new -> + Resp#stun{class = response, + 'XOR-MAPPED-ADDRESS' = {Addr, Port}} + end; true -> - Resp#stun{class = error, - 'ERROR-CODE' = {405, <<"Method Not Allowed">>}} + Resp#stun{class = error, + 'ERROR-CODE' = {405, <<"Method Not Allowed">>}} end; process(_Addr, _Port, #stun{class = request} = Msg) -> Resp = prepare_response(Msg), Resp#stun{class = error, 'UNKNOWN-ATTRIBUTES' = Msg#stun.unsupported, 'ERROR-CODE' = {420, stun_codec:reason(420)}}; -process(_Addr, _Port, _Msg) -> - pass. +process(_Addr, _Port, _Msg) -> pass. prepare_response(Msg) -> - Version = list_to_binary("ejabberd " ++ ?VERSION), - #stun{method = Msg#stun.method, - magic = Msg#stun.magic, - trid = Msg#stun.trid, - 'SOFTWARE' = Version}. + Version = <<"ejabberd ", (iolist_to_binary(?VERSION))/binary>>, + #stun{method = Msg#stun.method, magic = Msg#stun.magic, + trid = Msg#stun.trid, 'SOFTWARE' = Version}. -process_data(NextStateName, #state{buf = Buf} = State, Data) -> +process_data(NextStateName, #state{buf = Buf} = State, + Data) -> NewBuf = <<Buf/binary, Data/binary>>, case stun_codec:decode(NewBuf) of - {ok, Msg, Tail} -> - gen_fsm:send_event(self(), Msg), - process_data(NextStateName, State#state{buf = <<>>}, Tail); - empty -> - NewState = State#state{buf = <<>>}, - {next_state, NextStateName, update_state(NewState)}; - more when size(NewBuf) < ?MAX_BUF_SIZE -> - NewState = State#state{buf = NewBuf}, - {next_state, NextStateName, update_state(NewState)}; - _ -> - {stop, normal, State} + {ok, Msg, Tail} -> + gen_fsm:send_event(self(), Msg), + process_data(NextStateName, State#state{buf = <<>>}, + Tail); + empty -> + NewState = State#state{buf = <<>>}, + {next_state, NextStateName, update_state(NewState)}; + more when byte_size(NewBuf) < (?MAX_BUF_SIZE) -> + NewState = State#state{buf = NewBuf}, + {next_state, NextStateName, update_state(NewState)}; + _ -> {stop, normal, State} end. update_state(#state{sock = Sock} = State) -> case State#state.sock_mod of - gen_tcp -> - inet:setopts(Sock, [{active, once}]); - SockMod -> - SockMod:setopts(Sock, [{active, once}]) + gen_tcp -> inet:setopts(Sock, [{active, once}]); + SockMod -> SockMod:setopts(Sock, [{active, once}]) end, cancel_timer(State#state.tref), TRef = erlang:start_timer(?TIMEOUT, self(), stop), @@ -247,13 +233,7 @@ update_state(#state{sock = Sock} = State) -> cancel_timer(TRef) -> case erlang:cancel_timer(TRef) of - false -> - receive - {timeout, TRef, _} -> - ok - after 0 -> - ok - end; - _ -> - ok + false -> + receive {timeout, TRef, _} -> ok after 0 -> ok end; + _ -> ok end. diff --git a/src/stun/stun.hrl b/src/stun/stun.hrl index bd25c67f9..251cf83cc 100644 --- a/src/stun/stun.hrl +++ b/src/stun/stun.hrl @@ -28,51 +28,61 @@ %% I know, this is terrible. Refer to 'STUN Message Structure' of %% RFC5389 to understand this. -define(STUN_METHOD(Type), - ((Type band 16#3e00) bsr 2) bor - ((Type band 16#e0) bsr 1) bor (Type band 16#f)). + Type band 15872 bsr 2 bor (Type band 224 bsr 1) bor + Type band 15). + -define(STUN_CLASS(Type), - ((Type band 16#100) bsr 7) bor - ((Type band 16#10) bsr 4)). + Type band 256 bsr 7 bor (Type band 16 bsr 4)). + -define(STUN_TYPE(C, M), - (((M band 16#f80) bsl 2) - bor ((M band 16#70) bsl 1) - bor (M band 16#f) ) - bor (((C band 16#2) bsl 7) bor ((C band 16#1) bsl 4))). +%% Comprehension-required range (0x0000-0x7FFF) +%% Comprehension-optional range (0x8000-0xFFFF) + M band 3968 bsl 2 bor (M band 112 bsl 1) bor M band 15 + bor (C band 2 bsl 7 bor (C band 1 bsl 4))). --define(is_required(A), (A =< 16#7fff)). +-define(is_required(A), A =< 32767). --define(STUN_METHOD_BINDING, 16#001). +-define(STUN_METHOD_BINDING, 1). -%% Comprehension-required range (0x0000-0x7FFF) --define(STUN_ATTR_MAPPED_ADDRESS, 16#0001). --define(STUN_ATTR_USERNAME, 16#0006). --define(STUN_ATTR_MESSAGE_INTEGRITY, 16#0008). --define(STUN_ATTR_ERROR_CODE, 16#0009). --define(STUN_ATTR_UNKNOWN_ATTRIBUTES, 16#000a). --define(STUN_ATTR_REALM, 16#0014). --define(STUN_ATTR_NONCE, 16#0015). --define(STUN_ATTR_XOR_MAPPED_ADDRESS, 16#0020). +-define(STUN_ATTR_MAPPED_ADDRESS, 1). -%% Comprehension-optional range (0x8000-0xFFFF) --define(STUN_ATTR_SOFTWARE, 16#8022). --define(STUN_ATTR_ALTERNATE_SERVER, 16#8023). --define(STUN_ATTR_FINGERPRINT, 16#8028). - --record(stun, {class, - method, - magic = ?STUN_MAGIC, - trid, - unsupported = [], - 'SOFTWARE', - 'ALTERNATE-SERVER', - 'MAPPED-ADDRESS', - 'XOR-MAPPED-ADDRESS', - 'USERNAME', - 'REALM', - 'NONCE', - 'MESSAGE-INTEGRITY', - 'ERROR-CODE', - 'UNKNOWN-ATTRIBUTES' = []}). +-define(STUN_ATTR_USERNAME, 6). + +-define(STUN_ATTR_MESSAGE_INTEGRITY, 8). + +-define(STUN_ATTR_ERROR_CODE, 9). + +-define(STUN_ATTR_UNKNOWN_ATTRIBUTES, 10). + +-define(STUN_ATTR_REALM, 20). + +-define(STUN_ATTR_NONCE, 21). + +-define(STUN_ATTR_XOR_MAPPED_ADDRESS, 32). + +-define(STUN_ATTR_SOFTWARE, 32802). + +-define(STUN_ATTR_ALTERNATE_SERVER, 32803). + +-define(STUN_ATTR_FINGERPRINT, 32808). + +-record(stun, + {class = request :: request | response | error | indication, + method = ?STUN_METHOD_BINDING :: non_neg_integer(), + magic = ?STUN_MAGIC :: non_neg_integer(), + trid = 0 :: non_neg_integer() , + unsupported = [] :: [non_neg_integer()], + 'SOFTWARE', + 'ALTERNATE-SERVER', + 'MAPPED-ADDRESS', + 'XOR-MAPPED-ADDRESS', + 'USERNAME', + 'REALM', + 'NONCE', + 'MESSAGE-INTEGRITY', + 'ERROR-CODE', + 'UNKNOWN-ATTRIBUTES' = []}). %% Workarounds. %%-define(NO_PADDING, true). + diff --git a/src/stun/stun_codec.erl b/src/stun/stun_codec.erl index 2f4723dca..4d489e070 100644 --- a/src/stun/stun_codec.erl +++ b/src/stun/stun_codec.erl @@ -26,16 +26,11 @@ -module(stun_codec). %% API --export([decode/1, - encode/1, - version/1, - reason/1, +-export([decode/1, encode/1, version/1, reason/1, pp/1]). %% Tests --export([test_udp/2, - test_tcp/2, - test_tls/2, +-export([test_udp/2, test_tcp/2, test_tls/2, test_public/0]). -include("stun.hrl"). @@ -44,42 +39,34 @@ %% API %%==================================================================== decode(<<0:2, Type:14, Len:16, Magic:32, TrID:96, - Body:Len/binary, Tail/binary>>) -> + Body:Len/binary, Tail/binary>>) -> case catch decode(Type, Magic, TrID, Body) of - {'EXIT', _} -> - {error, unparsed}; - Res -> - {ok, Res, Tail} + {'EXIT', _} -> {error, unparsed}; + Res -> {ok, Res, Tail} end; -decode(<<0:2, _/binary>>) -> - more; -decode(<<>>) -> - empty; -decode(_) -> - {error, unparsed}. - -encode(#stun{class = Class, - method = Method, - magic = Magic, - trid = TrID} = Msg) -> +decode(<<0:2, _/binary>>) -> more; +decode(<<>>) -> empty; +decode(_) -> {error, unparsed}. + +encode(#stun{class = Class, method = Method, + magic = Magic, trid = TrID} = + Msg) -> ClassCode = case Class of - request -> 0; - indication -> 1; - response -> 2; - error -> 3 + request -> 0; + indication -> 1; + response -> 2; + error -> 3 end, - Type = ?STUN_TYPE(ClassCode, Method), + Type = (?STUN_TYPE(ClassCode, Method)), Attrs = enc_attrs(Msg), - Len = size(Attrs), - <<0:2, Type:14, Len:16, Magic:32, TrID:96, Attrs/binary>>. + Len = byte_size(Attrs), + <<0:2, Type:14, Len:16, Magic:32, TrID:96, + Attrs/binary>>. -pp(Term) -> - io_lib_pretty:print(Term, fun pp/2). +pp(Term) -> io_lib_pretty:print(Term, fun pp/2). -version(#stun{magic = ?STUN_MAGIC}) -> - new; -version(#stun{}) -> - old. +version(#stun{magic = ?STUN_MAGIC}) -> new; +version(#stun{}) -> old. reason(300) -> <<"Try Alternate">>; reason(400) -> <<"Bad Request">>; @@ -93,43 +80,41 @@ reason(_) -> <<"Undefined Error">>. %% Internal functions %%==================================================================== decode(Type, Magic, TrID, Body) -> - Method = ?STUN_METHOD(Type), + Method = (?STUN_METHOD(Type)), Class = case ?STUN_CLASS(Type) of - 0 -> request; - 1 -> indication; - 2 -> response; - 3 -> error + 0 -> request; + 1 -> indication; + 2 -> response; + 3 -> error end, - dec_attrs(Body, #stun{class = Class, - method = Method, - magic = Magic, - trid = TrID}). + dec_attrs(Body, + #stun{class = Class, method = Method, magic = Magic, + trid = TrID}). dec_attrs(<<Type:16, Len:16, Rest/binary>>, Msg) -> PaddLen = padd_len(Len), <<Val:Len/binary, _:PaddLen, Tail/binary>> = Rest, NewMsg = dec_attr(Type, Val, Msg), - if Type == ?STUN_ATTR_MESSAGE_INTEGRITY -> - NewMsg; - true -> - dec_attrs(Tail, NewMsg) + if Type == (?STUN_ATTR_MESSAGE_INTEGRITY) -> NewMsg; + true -> dec_attrs(Tail, NewMsg) end; -dec_attrs(<<>>, Msg) -> - Msg. +dec_attrs(<<>>, Msg) -> Msg. enc_attrs(Msg) -> - list_to_binary( - [enc_attr(?STUN_ATTR_SOFTWARE, Msg#stun.'SOFTWARE'), - enc_addr(?STUN_ATTR_MAPPED_ADDRESS, Msg#stun.'MAPPED-ADDRESS'), - enc_xor_addr(?STUN_ATTR_XOR_MAPPED_ADDRESS, - Msg#stun.magic, Msg#stun.trid, - Msg#stun.'XOR-MAPPED-ADDRESS'), - enc_addr(?STUN_ATTR_ALTERNATE_SERVER, Msg#stun.'ALTERNATE-SERVER'), - enc_attr(?STUN_ATTR_USERNAME, Msg#stun.'USERNAME'), - enc_attr(?STUN_ATTR_REALM, Msg#stun.'REALM'), - enc_attr(?STUN_ATTR_NONCE, Msg#stun.'NONCE'), - enc_error_code(Msg#stun.'ERROR-CODE'), - enc_unknown_attrs(Msg#stun.'UNKNOWN-ATTRIBUTES')]). + iolist_to_binary([enc_attr(?STUN_ATTR_SOFTWARE, + Msg#stun.'SOFTWARE'), + enc_addr(?STUN_ATTR_MAPPED_ADDRESS, + Msg#stun.'MAPPED-ADDRESS'), + enc_xor_addr(?STUN_ATTR_XOR_MAPPED_ADDRESS, + Msg#stun.magic, Msg#stun.trid, + Msg#stun.'XOR-MAPPED-ADDRESS'), + enc_addr(?STUN_ATTR_ALTERNATE_SERVER, + Msg#stun.'ALTERNATE-SERVER'), + enc_attr(?STUN_ATTR_USERNAME, Msg#stun.'USERNAME'), + enc_attr(?STUN_ATTR_REALM, Msg#stun.'REALM'), + enc_attr(?STUN_ATTR_NONCE, Msg#stun.'NONCE'), + enc_error_code(Msg#stun.'ERROR-CODE'), + enc_unknown_attrs(Msg#stun.'UNKNOWN-ATTRIBUTES')]). dec_attr(?STUN_ATTR_MAPPED_ADDRESS, Val, Msg) -> <<_, Family, Port:16, AddrBin/binary>> = Val, @@ -139,7 +124,8 @@ dec_attr(?STUN_ATTR_XOR_MAPPED_ADDRESS, Val, Msg) -> <<_, Family, XPort:16, XAddr/binary>> = Val, Magic = Msg#stun.magic, Port = XPort bxor (Magic bsr 16), - Addr = dec_xor_addr(Family, Magic, Msg#stun.trid, XAddr), + Addr = dec_xor_addr(Family, Magic, Msg#stun.trid, + XAddr), Msg#stun{'XOR-MAPPED-ADDRESS' = {Addr, Port}}; dec_attr(?STUN_ATTR_SOFTWARE, Val, Msg) -> Msg#stun{'SOFTWARE' = Val}; @@ -157,55 +143,52 @@ dec_attr(?STUN_ATTR_ALTERNATE_SERVER, Val, Msg) -> Msg#stun{'ALTERNATE-SERVER' = {IP, Port}}; dec_attr(?STUN_ATTR_ERROR_CODE, Val, Msg) -> <<_:21, Class:3, Number:8, Reason/binary>> = Val, - if Class >=3, Class =< 6, Number >=0, Number =< 99 -> - Code = Class * 100 + Number, - Msg#stun{'ERROR-CODE' = {Code, Reason}} + if Class >= 3, Class =< 6, Number >= 0, Number =< 99 -> + Code = Class * 100 + Number, + Msg#stun{'ERROR-CODE' = {Code, Reason}} end; dec_attr(?STUN_ATTR_UNKNOWN_ATTRIBUTES, Val, Msg) -> Attrs = dec_unknown_attrs(Val, []), Msg#stun{'UNKNOWN-ATTRIBUTES' = Attrs}; dec_attr(Attr, _Val, #stun{unsupported = Attrs} = Msg) - when Attr =< 16#7fff -> - Msg#stun{unsupported = [Attr|Attrs]}; -dec_attr(_Attr, _Val, Msg) -> - Msg. - -dec_addr(1, <<A1, A2, A3, A4>>) -> - {A1, A2, A3, A4}; -dec_addr(2, <<A1:16, A2:16, A3:16, A4:16, - A5:16, A6:16, A7:16, A8:16>>) -> + when Attr =< 32767 -> + Msg#stun{unsupported = [Attr | Attrs]}; +dec_attr(_Attr, _Val, Msg) -> Msg. + +dec_addr(1, <<A1, A2, A3, A4>>) -> {A1, A2, A3, A4}; +dec_addr(2, + <<A1:16, A2:16, A3:16, A4:16, A5:16, A6:16, A7:16, + A8:16>>) -> {A1, A2, A3, A4, A5, A6, A7, A8}. dec_xor_addr(1, Magic, _TrID, <<XAddr:32>>) -> - Addr = XAddr bxor Magic, - dec_addr(1, <<Addr:32>>); + Addr = XAddr bxor Magic, dec_addr(1, <<Addr:32>>); dec_xor_addr(2, Magic, TrID, <<XAddr:128>>) -> - Addr = XAddr bxor ((Magic bsl 96) bor TrID), + Addr = XAddr bxor (Magic bsl 96 bor TrID), dec_addr(2, <<Addr:128>>). dec_unknown_attrs(<<Attr:16, Tail/binary>>, Acc) -> - dec_unknown_attrs(Tail, [Attr|Acc]); -dec_unknown_attrs(<<>>, Acc) -> - lists:reverse(Acc). + dec_unknown_attrs(Tail, [Attr | Acc]); +dec_unknown_attrs(<<>>, Acc) -> lists:reverse(Acc). -enc_attr(_Attr, undefined) -> - <<>>; +enc_attr(_Attr, undefined) -> <<>>; enc_attr(Attr, Val) -> - Len = size(Val), + Len = byte_size(Val), PaddLen = padd_len(Len), <<Attr:16, Len:16, Val/binary, 0:PaddLen>>. -enc_addr(_Type, undefined) -> - <<>>; +enc_addr(_Type, undefined) -> <<>>; enc_addr(Type, {{A1, A2, A3, A4}, Port}) -> enc_attr(Type, <<0, 1, Port:16, A1, A2, A3, A4>>); -enc_addr(Type, {{A1, A2, A3, A4, A5, A6, A7, A8}, Port}) -> - enc_attr(Type, <<0, 2, Port:16, A1:16, A2:16, A3:16, - A4:16, A5:16, A6:16, A7:16, A8:16>>). +enc_addr(Type, + {{A1, A2, A3, A4, A5, A6, A7, A8}, Port}) -> + enc_attr(Type, + <<0, 2, Port:16, A1:16, A2:16, A3:16, A4:16, A5:16, + A6:16, A7:16, A8:16>>). -enc_xor_addr(_Type, _Magic, _TrID, undefined) -> - <<>>; -enc_xor_addr(Type, Magic, _TrID, {{A1, A2, A3, A4}, Port}) -> +enc_xor_addr(_Type, _Magic, _TrID, undefined) -> <<>>; +enc_xor_addr(Type, Magic, _TrID, + {{A1, A2, A3, A4}, Port}) -> XPort = Port bxor (Magic bsr 16), <<Addr:32>> = <<A1, A2, A3, A4>>, XAddr = Addr bxor Magic, @@ -213,51 +196,46 @@ enc_xor_addr(Type, Magic, _TrID, {{A1, A2, A3, A4}, Port}) -> enc_xor_addr(Type, Magic, TrID, {{A1, A2, A3, A4, A5, A6, A7, A8}, Port}) -> XPort = Port bxor (Magic bsr 16), - <<Addr:128>> = <<A1:16, A2:16, A3:16, A4:16, - A5:16, A6:16, A7:16, A8:16>>, - XAddr = Addr bxor ((Magic bsl 96) bor TrID), + <<Addr:128>> = <<A1:16, A2:16, A3:16, A4:16, A5:16, + A6:16, A7:16, A8:16>>, + XAddr = Addr bxor (Magic bsl 96 bor TrID), enc_attr(Type, <<0, 2, XPort:16, XAddr:128>>). -enc_error_code(undefined) -> - <<>>; +enc_error_code(undefined) -> <<>>; enc_error_code({Code, Reason}) -> Class = Code div 100, Number = Code rem 100, enc_attr(?STUN_ATTR_ERROR_CODE, <<0:21, Class:3, Number:8, Reason/binary>>). -enc_unknown_attrs([]) -> - <<>>; +enc_unknown_attrs([]) -> <<>>; enc_unknown_attrs(Attrs) -> enc_attr(?STUN_ATTR_UNKNOWN_ATTRIBUTES, - list_to_binary([<<Attr:16>> || Attr <- Attrs])). - %%==================================================================== %% Auxiliary functions %%==================================================================== -pp(Tag, N) -> - try - pp1(Tag, N) - catch _:_ -> - no - end. + iolist_to_binary([<<Attr:16>> || Attr <- Attrs])). + +pp(Tag, N) -> try pp1(Tag, N) catch _:_ -> no end. pp1(stun, N) -> N = record_info(size, stun) - 1, record_info(fields, stun); -pp1(_, _) -> - no. +pp1(_, _) -> no. %% Workaround for stupid clients. -ifdef(NO_PADDING). -padd_len(_Len) -> - 0. + +padd_len(_Len) -> 0. + -else. + padd_len(Len) -> case Len rem 4 of - 0 -> 0; - N -> 8*(4-N) + 0 -> 0; + N -> 8 * (4 - N) end. + -endif. %%==================================================================== @@ -265,65 +243,49 @@ padd_len(Len) -> %%==================================================================== bind_msg() -> Msg = #stun{method = ?STUN_METHOD_BINDING, - class = request, - trid = random:uniform(1 bsl 96), + class = request, trid = random:uniform(1 bsl 96), 'SOFTWARE' = <<"test">>}, encode(Msg). -test_udp(Addr, Port) -> - test(Addr, Port, gen_udp). +test_udp(Addr, Port) -> test(Addr, Port, gen_udp). -test_tcp(Addr, Port) -> - test(Addr, Port, gen_tcp). +test_tcp(Addr, Port) -> test(Addr, Port, gen_tcp). -test_tls(Addr, Port) -> - test(Addr, Port, ssl). +test_tls(Addr, Port) -> test(Addr, Port, ssl). test(Addr, Port, Mod) -> Res = case Mod of - gen_udp -> - Mod:open(0, [binary, {active, false}]); - _ -> - Mod:connect(Addr, Port, - [binary, {active, false}], 1000) + gen_udp -> Mod:open(0, [binary, {active, false}]); + _ -> + Mod:connect(Addr, Port, [binary, {active, false}], 1000) end, case Res of - {ok, Sock} -> - if Mod == gen_udp -> - Mod:send(Sock, Addr, Port, bind_msg()); - true -> - Mod:send(Sock, bind_msg()) - end, - case Mod:recv(Sock, 0, 1000) of - {ok, {_, _, Data}} -> - try_dec(Data); - {ok, Data} -> - try_dec(Data); - Err -> - io:format("err: ~p~n", [Err]) - end, - Mod:close(Sock); - Err -> - io:format("err: ~p~n", [Err]) + {ok, Sock} -> + if Mod == gen_udp -> + Mod:send(Sock, Addr, Port, bind_msg()); + true -> Mod:send(Sock, bind_msg()) + end, + case Mod:recv(Sock, 0, 1000) of + {ok, {_, _, Data}} -> try_dec(Data); + {ok, Data} -> try_dec(Data); + Err -> io:format("err: ~p~n", [Err]) + end, + Mod:close(Sock); + Err -> io:format("err: ~p~n", [Err]) end. try_dec(Data) -> case decode(Data) of - {ok, Msg, _} -> - io:format("got:~n~s~n", [pp(Msg)]); - Err -> - io:format("err: ~p~n", [Err]) + {ok, Msg, _} -> io:format("got:~n~s~n", [pp(Msg)]); + Err -> io:format("err: ~p~n", [Err]) end. public_servers() -> [{"stun.ekiga.net", 3478, 3478, 5349}, - {"stun.fwdnet.net", 3478, 3478, 5349}, {"stun.ideasip.com", 3478, 3478, 5349}, - {"stun01.sipphone.com", 3478, 3478, 5349}, {"stun.softjoys.com", 3478, 3478, 5349}, {"stun.voipbuster.com", 3478, 3478, 5349}, {"stun.voxgratia.org", 3478, 3478, 5349}, - {"stun.xten.com", 3478, 3478, 5349}, {"stunserver.org", 3478, 3478, 5349}, {"stun.sipgate.net", 10000, 10000, 5349}, {"numb.viagenie.ca", 3478, 3478, 5349}, @@ -332,12 +294,12 @@ public_servers() -> test_public() -> ssl:start(), - lists:foreach( - fun({Addr, UDPPort, TCPPort, TLSPort}) -> - io:format("trying ~s:~p on UDP... ", [Addr, UDPPort]), - test_udp(Addr, UDPPort), - io:format("trying ~s:~p on TCP... ", [Addr, TCPPort]), - test_tcp(Addr, TCPPort), - io:format("trying ~s:~p on TLS... ", [Addr, TLSPort]), - test_tls(Addr, TLSPort) - end, public_servers()). + lists:foreach(fun ({Addr, UDPPort, TCPPort, TLSPort}) -> + io:format("trying ~s:~p on UDP... ", [Addr, UDPPort]), + test_udp(Addr, UDPPort), + io:format("trying ~s:~p on TCP... ", [Addr, TCPPort]), + test_tcp(Addr, TCPPort), + io:format("trying ~s:~p on TLS... ", [Addr, TLSPort]), + test_tls(Addr, TLSPort) + end, + public_servers()). |