1
%%%-------------------------------------------------------------------
2
%%% File : ejabberd_stun.erl
3
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
4
%%% Description : RFC5389 implementation.
5
%%% Currently only Binding usage is supported.
7
%%% Created : 8 Aug 2009 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
10
%%% ejabberd, Copyright (C) 2002-2009 ProcessOne
12
%%% This program is free software; you can redistribute it and/or
13
%%% modify it under the terms of the GNU General Public License as
14
%%% published by the Free Software Foundation; either version 2 of the
15
%%% License, or (at your option) any later version.
17
%%% This program is distributed in the hope that it will be useful,
18
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
19
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20
%%% General Public License for more details.
22
%%% You should have received a copy of the GNU General Public License
23
%%% along with this program; if not, write to the Free Software
24
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27
%%%-------------------------------------------------------------------
28
-module(ejabberd_stun).
33
-export([start_link/2,
47
-export([wait_for_tls/2,
48
session_established/2]).
50
-include("ejabberd.hrl").
53
-define(MAX_BUF_SIZE, 64*1024). %% 64kb
54
-define(TIMEOUT, 10000). %% 10 sec
63
%%====================================================================
65
%%====================================================================
66
start({gen_tcp, Sock}, Opts) ->
67
supervisor:start_child(ejabberd_stun_sup, [Sock, Opts]).
69
start_link(Sock, Opts) ->
70
gen_fsm:start_link(?MODULE, [Sock, Opts], []).
75
udp_recv(Sock, Addr, Port, Data, _Opts) ->
76
case stun_codec:decode(Data) of
78
?DEBUG("got:~n~p", [Msg]),
79
case process(Addr, Port, Msg) of
80
RespMsg when is_record(RespMsg, stun) ->
81
?DEBUG("sent:~n~p", [RespMsg]),
82
Data1 = stun_codec:encode(RespMsg),
83
gen_udp:send(Sock, Addr, Port, Data1);
91
%%====================================================================
93
%%====================================================================
95
case inet:peername(Sock) of
97
inet:setopts(Sock, [{active, once}]),
98
TRef = erlang:start_timer(?TIMEOUT, self(), stop),
99
State = #state{sock = Sock, peer = Addr, tref = TRef},
100
case proplists:get_value(certfile, Opts) of
102
{ok, session_established, State};
104
{ok, wait_for_tls, State#state{certfile = CertFile}}
110
wait_for_tls(Event, State) ->
111
?INFO_MSG("unexpected event in wait_for_tls: ~p", [Event]),
112
{next_state, wait_for_tls, State}.
114
session_established(Msg, State) when is_record(Msg, stun) ->
115
?DEBUG("got:~n~p", [Msg]),
116
{Addr, Port} = State#state.peer,
117
case process(Addr, Port, Msg) of
118
Resp when is_record(Resp, stun) ->
119
?DEBUG("sent:~n~p", [Resp]),
120
Data = stun_codec:encode(Resp),
121
(State#state.sock_mod):send(State#state.sock, Data);
125
{next_state, session_established, State};
126
session_established(Event, State) ->
127
?INFO_MSG("unexpected event in session_established: ~p", [Event]),
128
{next_state, session_established, State}.
130
handle_event(_Event, StateName, State) ->
131
{next_state, StateName, State}.
133
handle_sync_event(_Event, _From, StateName, State) ->
134
{reply, {error, badarg}, StateName, State}.
136
handle_info({tcp, Sock, TLSData}, wait_for_tls, State) ->
137
Buf = <<(State#state.buf)/binary, TLSData/binary>>,
138
%% Check if the initial message is a TLS handshake
140
_ when size(Buf) < 3 ->
141
{next_state, wait_for_tls,
142
update_state(State#state{buf = Buf})};
143
<<_:16, 1, _/binary>> ->
144
TLSOpts = [{certfile, State#state.certfile}],
145
{ok, TLSSock} = tls:tcp_to_tls(Sock, TLSOpts),
146
NewState = State#state{sock = TLSSock,
149
case tls:recv_data(TLSSock, Buf) of
151
process_data(session_established, NewState, Data);
153
{stop, normal, NewState}
156
process_data(session_established, State, TLSData)
158
handle_info({tcp, _Sock, TLSData}, StateName,
159
#state{sock_mod = tls} = State) ->
160
case tls:recv_data(State#state.sock, TLSData) of
162
process_data(StateName, State, Data);
164
{stop, normal, State}
166
handle_info({tcp, _Sock, Data}, StateName, State) ->
167
process_data(StateName, State, Data);
168
handle_info({tcp_closed, _Sock}, _StateName, State) ->
169
?DEBUG("connection reset by peer", []),
170
{stop, normal, State};
171
handle_info({tcp_error, _Sock, Reason}, _StateName, State) ->
172
?DEBUG("connection error: ~p", [Reason]),
173
{stop, normal, State};
174
handle_info({timeout, TRef, stop}, _StateName,
175
#state{tref = TRef} = State) ->
176
{stop, normal, State};
177
handle_info(Info, StateName, State) ->
178
?INFO_MSG("unexpected info: ~p", [Info]),
179
{next_state, StateName, State}.
181
terminate(_Reason, _StateName, State) ->
182
catch (State#state.sock_mod):close(State#state.sock),
185
code_change(_OldVsn, StateName, State, _Extra) ->
186
{ok, StateName, State}.
188
%%--------------------------------------------------------------------
189
%%% Internal functions
190
%%--------------------------------------------------------------------
191
process(Addr, Port, #stun{class = request, unsupported = []} = Msg) ->
192
Resp = prepare_response(Msg),
193
if Msg#stun.method == ?STUN_METHOD_BINDING ->
194
case stun_codec:version(Msg) of
196
Resp#stun{class = response,
197
'MAPPED-ADDRESS' = {Addr, Port}};
199
Resp#stun{class = response,
200
'XOR-MAPPED-ADDRESS' = {Addr, Port}}
203
Resp#stun{class = error,
204
'ERROR-CODE' = {405, <<"Method Not Allowed">>}}
206
process(_Addr, _Port, #stun{class = request} = Msg) ->
207
Resp = prepare_response(Msg),
208
Resp#stun{class = error,
209
'UNKNOWN-ATTRIBUTES' = Msg#stun.unsupported,
210
'ERROR-CODE' = {420, stun_codec:reason(420)}};
211
process(_Addr, _Port, _Msg) ->
214
prepare_response(Msg) ->
215
Version = list_to_binary("ejabberd " ++ ?VERSION),
216
#stun{method = Msg#stun.method,
217
magic = Msg#stun.magic,
218
trid = Msg#stun.trid,
219
'SOFTWARE' = Version}.
221
process_data(NextStateName, #state{buf = Buf} = State, Data) ->
222
NewBuf = <<Buf/binary, Data/binary>>,
223
case stun_codec:decode(NewBuf) of
225
gen_fsm:send_event(self(), Msg),
226
process_data(NextStateName, State#state{buf = <<>>}, Tail);
228
NewState = State#state{buf = <<>>},
229
{next_state, NextStateName, update_state(NewState)};
230
more when size(NewBuf) < ?MAX_BUF_SIZE ->
231
NewState = State#state{buf = NewBuf},
232
{next_state, NextStateName, update_state(NewState)};
234
{stop, normal, State}
237
update_state(#state{sock = Sock} = State) ->
238
case State#state.sock_mod of
240
inet:setopts(Sock, [{active, once}]);
242
SockMod:setopts(Sock, [{active, once}])
244
cancel_timer(State#state.tref),
245
TRef = erlang:start_timer(?TIMEOUT, self(), stop),
246
State#state{tref = TRef}.
248
cancel_timer(TRef) ->
249
case erlang:cancel_timer(TRef) of
252
{timeout, TRef, _} ->