Commit 6f2876a7 authored by Philipp Huebner's avatar Philipp Huebner

Imported Upstream version 0.2014.05.08

parent e4f267c4
This diff is collapsed.
......@@ -12,4 +12,7 @@ clean:
doc:
rebar skip_deps=true doc
.PHONY: clean src all doc
test: all
rebar -v skip_deps=true eunit
.PHONY: clean src all doc rebar
-define(STUN_MAGIC, 554869826).
%%%-------------------------------------------------------------------
%%% File : stun.hrl
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% Description : STUN values
%%% Created : 8 Aug 2009 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2014 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%-------------------------------------------------------------------
-define(STUN_MAGIC, 16#2112a442).
%% I know, this is terrible. Refer to 'STUN Message Structure' of
%% RFC5389 to understand this.
-define(STUN_METHOD(Type),
Type band 15872 bsr 2 bor (Type band 224 bsr 1) bor
Type band 15).
((Type band 16#3e00) bsr 2) bor
((Type band 16#e0) bsr 1) bor (Type band 16#f)).
-define(STUN_CLASS(Type),
Type band 256 bsr 7 bor (Type band 16 bsr 4)).
((Type band 16#100) bsr 7) bor
((Type band 16#10) bsr 4)).
-define(STUN_TYPE(C, M),
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 =< 32767).
-define(STUN_METHOD_BINDING, 1).
-define(STUN_ATTR_MAPPED_ADDRESS, 1).
-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' = []}).
(((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))).
-define(is_required(A), (A =< 16#7fff)).
-define(STUN_METHOD_BINDING, 16#001).
-define(STUN_METHOD_ALLOCATE, 16#003).
-define(STUN_METHOD_REFRESH, 16#004).
-define(STUN_METHOD_SEND, 16#006).
-define(STUN_METHOD_DATA, 16#007).
-define(STUN_METHOD_CREATE_PERMISSION, 16#008).
-define(STUN_METHOD_CHANNEL_BIND, 16#009).
%% 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_CHANNEL_NUMBER, 16#000c).
-define(STUN_ATTR_LIFETIME, 16#000d).
-define(STUN_ATTR_XOR_PEER_ADDRESS, 16#0012).
-define(STUN_ATTR_DATA, 16#0013).
-define(STUN_ATTR_XOR_RELAYED_ADDRESS, 16#0016).
-define(STUN_ATTR_EVEN_PORT, 16#0018).
-define(STUN_ATTR_REQUESTED_TRANSPORT, 16#0019).
-define(STUN_ATTR_DONT_FRAGMENT, 16#001a).
-define(STUN_ATTR_RESERVATION_TOKEN, 16#0022).
%% 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 = request :: request | response | error | indication,
method = ?STUN_METHOD_BINDING :: non_neg_integer(),
magic = ?STUN_MAGIC :: non_neg_integer(),
trid = 0 :: non_neg_integer(),
raw = <<>> :: binary(),
unsupported = [],
'ALTERNATE-SERVER',
'CHANNEL-NUMBER',
'DATA',
'DONT-FRAGMENT' = false,
'ERROR-CODE',
'LIFETIME',
'MAPPED-ADDRESS',
'MESSAGE-INTEGRITY',
'NONCE',
'REALM',
'REQUESTED-TRANSPORT',
'SOFTWARE',
'UNKNOWN-ATTRIBUTES' = [],
'USERNAME',
'XOR-MAPPED-ADDRESS',
'XOR-PEER-ADDRESS' = [],
'XOR-RELAYED-ADDRESS'}).
-record(turn, {channel = 0 :: non_neg_integer(),
data = <<>> :: binary()}).
%% Workarounds.
%%-define(NO_PADDING, true).
This diff is collapsed.
This diff is collapsed.
%%%-------------------------------------------------------------------
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2011, Evgeniy Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 9 Jan 2011 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(stun_listener).
-behaviour(gen_server).
%% API
-export([start_link/0, add_listener/3, del_listener/2, start_listener/4]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(TCP_SEND_TIMEOUT, 10000).
-record(state, {listeners = dict:new() :: dict()}).
%%%===================================================================
%%% API
%%%===================================================================
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
add_listener(Port, Transport, Opts) ->
gen_server:call(?MODULE, {add_listener, Port, Transport, Opts}).
del_listener(Port, Transport) ->
gen_server:call(?MODULE, {del_listener, Port, Transport}).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) ->
{ok, #state{}}.
handle_call({add_listener, Port, Transport, Opts}, _From, State) ->
case dict:find({Port, Transport}, State#state.listeners) of
{ok, _} ->
Err = {error, already_started},
{reply, Err, State};
error ->
{Pid, MRef} = spawn_monitor(?MODULE, start_listener,
[Port, Transport, Opts, self()]),
receive
{'DOWN', MRef, _Type, _Object, Info} ->
Res = {error, Info},
format_listener_error(Port, Transport, Opts, Res),
{reply, Res, State};
{Pid, Reply} ->
case Reply of
{error, _} = Err ->
format_listener_error(Port, Transport, Opts, Err),
{reply, Reply, State};
ok ->
Listeners = dict:store(
{Port, Transport}, {MRef, Pid, Opts},
State#state.listeners),
{reply, ok, State#state{listeners = Listeners}}
end
end
end;
handle_call({del_listener, Port, Transport}, _From, State) ->
case dict:find({Port, Transport}, State#state.listeners) of
{ok, {MRef, Pid, _Opts}} ->
catch erlang:demonitor(MRef, [flush]),
catch exit(Pid, kill),
Listeners = dict:erase({Port, Transport}, State#state.listeners),
{reply, ok, State#state{listeners = Listeners}};
error ->
{reply, {error, notfound}, State}
end;
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({'DOWN', MRef, _Type, _Pid, Info}, State) ->
Listeners = dict:filter(
fun({Port, Transport}, {Ref, _, _}) when Ref == MRef ->
error_logger:error_msg("listener on ~p/~p failed: ~p",
[Port, Transport, Info]),
false;
(_, _) ->
true
end, State#state.listeners),
{noreply, State#state{listeners = Listeners}};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
start_listener(Port, Transport, Opts, Owner)
when Transport == tcp; Transport == tls ->
OptsWithTLS = case Transport of
tls -> [tls|Opts];
tcp -> Opts
end,
case gen_tcp:listen(Port, [binary,
{packet, 0},
{active, false},
{reuseaddr, true},
{nodelay, true},
{keepalive, true},
{send_timeout, ?TCP_SEND_TIMEOUT},
{send_timeout_close, true}]) of
{ok, ListenSocket} ->
Owner ! {self(), ok},
OptsWithTLS1 = stun:tcp_init(ListenSocket, OptsWithTLS),
accept(ListenSocket, OptsWithTLS1);
Err ->
Owner ! {self(), Err}
end;
start_listener(Port, udp, Opts, Owner) ->
case gen_udp:open(Port, [binary,
{active, false},
{reuseaddr, true}]) of
{ok, Socket} ->
Owner ! {self(), ok},
Opts1 = stun:udp_init(Socket, Opts),
udp_recv(Socket, Opts1);
Err ->
Owner ! {self(), Err}
end.
accept(ListenSocket, Opts) ->
case gen_tcp:accept(ListenSocket) of
{ok, Socket} ->
case {inet:peername(Socket),
inet:sockname(Socket)} of
{{ok, {PeerAddr, PeerPort}}, {ok, {Addr, Port}}} ->
error_logger:info_msg("accepted connection: ~s:~p -> ~s:~p",
[inet_parse:ntoa(PeerAddr), PeerPort,
inet_parse:ntoa(Addr), Port]),
case stun:start({gen_tcp, Socket}, Opts) of
{ok, Pid} ->
gen_tcp:controlling_process(Socket, Pid);
Err ->
Err
end;
Err ->
error_logger:error_msg("unable to fetch peername: ~p", [Err]),
Err
end,
accept(ListenSocket, Opts);
Err ->
Err
end.
udp_recv(Socket, Opts) ->
case gen_udp:recv(Socket, 0) of
{ok, {Addr, Port, Packet}} ->
case catch stun:udp_recv(Socket, Addr, Port, Packet, Opts) of
{'EXIT', Reason} ->
error_logger:error_msg("failed to process UDP packet:~n"
"** Source: {~p, ~p}~n"
"** Reason: ~p~n** Packet: ~p",
[Addr, Port, Reason, Packet]),
udp_recv(Socket, Opts);
NewOpts ->
udp_recv(Socket, NewOpts)
end;
{error, Reason} ->
error_logger:error_msg(
"unexpected UDP error: ~s", [inet:format_error(Reason)]),
erlang:error(Reason)
end.
format_listener_error(Port, Transport, Opts, Err) ->
error_logger:error_msg("failed to start listener:~n"
"** Port: ~p~n"
"** Transport: ~p~n"
"** Options: ~p~n"
"** Reason: ~p",
[Port, Transport, Opts, Err]).
%%%----------------------------------------------------------------------
%%% File : stun_shaper.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Functions to control connections traffic
%%% Created : 9 Feb 2003 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% stun, Copyright (C) 2002-2014 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------
-module(stun_shaper).
-author('alexey@process-one.net').
-export([new/1, update/2]).
-record(maxrate, {maxrate = 0 :: integer(),
lastrate = 0.0 :: float(),
lasttime = 0 :: integer()}).
-type shaper() :: none | #maxrate{}.
-export_type([shaper/0]).
%%%===================================================================
%%% API
%%%===================================================================
-spec new(none | integer()) -> shaper().
new(none) -> none;
new(MaxRate) when is_integer(MaxRate) ->
#maxrate{maxrate = MaxRate, lastrate = 0.0,
lasttime = now_to_usec(now())}.
-spec update(shaper(), integer()) -> {shaper(), integer()}.
update(none, _Size) -> {none, 0};
update(#maxrate{} = State, Size) ->
MinInterv = 1000 * Size /
(2 * State#maxrate.maxrate - State#maxrate.lastrate),
Interv = (now_to_usec(now()) - State#maxrate.lasttime) /
1000,
Pause = if MinInterv > Interv ->
1 + trunc(MinInterv - Interv);
true -> 0
end,
NextNow = now_to_usec(now()) + Pause * 1000,
{State#maxrate{lastrate =
(State#maxrate.lastrate +
1000000 * Size / (NextNow - State#maxrate.lasttime))
/ 2,
lasttime = NextNow},
Pause}.
now_to_usec({MSec, Sec, USec}) ->
(MSec * 1000000 + Sec) * 1000000 + USec.
......@@ -21,38 +21,22 @@
%%%===================================================================
%%% API functions
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% Starts the supervisor
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever a supervisor is started using supervisor:start_link/[2,3],
%% this function is called by the new process to find out about
%% restart strategy, maximum restart frequency and child
%% specifications.
%%
%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
{ok, {{simple_one_for_one, 10, 1},
[{undefined, {stun, start_link, []},
temporary, brutal_kill, worker, [stun]}]}}.
StunTmpSup = {stun_tmp_sup, {stun_tmp_sup, start_link, []},
permanent, infinity, supervisor, [stun_tmp_sup]},
TurnTmpSup = {turn_tmp_sup, {turn_tmp_sup, start_link, []},
permanent, infinity, supervisor, [turn_tmp_sup]},
TurnSM = {turn_sm, {turn_sm, start_link, []},
permanent, 2000, worker, [turn_sm]},
StunListen = {stun_listener, {stun_listener, start_link, []},
permanent, 2000, worker, [stun_listener]},
{ok, {{one_for_one, 10, 1}, [TurnSM, StunTmpSup, TurnTmpSup, StunListen]}}.
%%%===================================================================
%%% Internal functions
......
This diff is collapsed.
%%%-------------------------------------------------------------------
%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% @copyright (C) 2013, Evgeniy Khramtsov
%%% @doc
%%%
%%% @end
%%% Created : 3 May 2014 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%-------------------------------------------------------------------
-module(stun_tmp_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
%%%===================================================================
%%% API functions
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%% Starts the supervisor
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Whenever a supervisor is started using supervisor:start_link/[2,3],
%% this function is called by the new process to find out about
%% restart strategy, maximum restart frequency and child
%% specifications.
%%
%% @spec init(Args) -> {ok, {SupFlags, [ChildSpec]}} |
%% ignore |
%% {error, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
{ok, {{simple_one_for_one, 10, 1},
[{undefined, {stun, start_link, []},
temporary, brutal_kill, worker, [stun]}]}}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
%%%----------------------------------------------------------------------
%%% File : stun_treap.erl
%%% Author : Alexey Shchepin <alexey@process-one.net>
%%% Purpose : Treaps implementation
%%% Created : 22 Apr 2008 by Alexey Shchepin <alexey@process-one.net>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License
%%% along with this program; if not, write to the Free Software
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
%%% 02111-1307 USA
%%%
%%%----------------------------------------------------------------------
-module(stun_treap).
-export([empty/0, insert/4, delete/2, delete_root/1,
get_root/1, lookup/2, is_empty/1, fold/3, from_list/1,
to_list/1]).
-type hashkey() :: {non_neg_integer(), any()}.
-type treap() :: {hashkey(), any(), any(), treap(), treap()} | nil.
-export_type([treap/0]).
empty() -> nil.
insert(Key, Priority, Value, Tree) ->
HashKey = {erlang:phash2(Key), Key},
insert1(Tree, HashKey, Priority, Value).
insert1(nil, HashKey, Priority, Value) ->
{HashKey, Priority, Value, nil, nil};
insert1({HashKey1, Priority1, Value1, Left, Right} =
Tree,
HashKey, Priority, Value) ->
if HashKey < HashKey1 ->
heapify({HashKey1, Priority1, Value1,
insert1(Left, HashKey, Priority, Value), Right});
HashKey > HashKey1 ->
heapify({HashKey1, Priority1, Value1, Left,
insert1(Right, HashKey, Priority, Value)});
Priority == Priority1 ->
{HashKey, Priority, Value, Left, Right};
true ->
insert1(delete_root(Tree), HashKey, Priority, Value)
end.
heapify({_HashKey, _Priority, _Value, nil, nil} =
Tree) ->
Tree;
heapify({HashKey, Priority, Value, nil = Left,
{HashKeyR, PriorityR, ValueR, LeftR, RightR}} =
Tree) ->
if PriorityR > Priority ->
{HashKeyR, PriorityR, ValueR,
{HashKey, Priority, Value, Left, LeftR}, RightR};
true -> Tree
end;
heapify({HashKey, Priority, Value,
{HashKeyL, PriorityL, ValueL, LeftL, RightL},
nil = Right} =
Tree) ->
if PriorityL > Priority ->
{HashKeyL, PriorityL, ValueL, LeftL,
{HashKey, Priority, Value, RightL, Right}};
true -> Tree
end;
heapify({HashKey, Priority, Value,
{HashKeyL, PriorityL, ValueL, LeftL, RightL} = Left,
{HashKeyR, PriorityR, ValueR, LeftR, RightR} = Right} =
Tree) ->
if PriorityR > Priority ->
{HashKeyR, PriorityR, ValueR,
{HashKey, Priority, Value, Left, LeftR}, RightR};
PriorityL > Priority ->
{HashKeyL, PriorityL, ValueL, LeftL,
{HashKey, Priority, Value, RightL, Right}};
true -> Tree
end.
delete(Key, Tree) ->
HashKey = {erlang:phash2(Key), Key},
delete1(HashKey, Tree).
delete1(_HashKey, nil) -> nil;
delete1(HashKey,
{HashKey1, Priority1, Value1, Left, Right} = Tree) ->
if HashKey < HashKey1 ->
{HashKey1, Priority1, Value1, delete1(HashKey, Left),
Right};
HashKey > HashKey1 ->
{HashKey1, Priority1, Value1, Left,
delete1(HashKey, Right)};
true -> delete_root(Tree)
end.
delete_root({HashKey, Priority, Value, Left, Right}) ->
case {Left, Right} of
{nil, nil} -> nil;
{_, nil} -> Left;
{nil, _} -> Right;
{{HashKeyL, PriorityL, ValueL, LeftL, RightL},
{HashKeyR, PriorityR, ValueR, LeftR, RightR}} ->
if PriorityL > PriorityR ->
{HashKeyL, PriorityL, ValueL, LeftL,
delete_root({HashKey, Priority, Value, RightL, Right})};
true ->
{HashKeyR, PriorityR, ValueR,
delete_root({HashKey, Priority, Value, Left, LeftR}),
RightR}
end
end.
is_empty(nil) -> true;
is_empty({_HashKey, _Priority, _Value, _Left,
_Right}) ->
false.
get_root({{_Hash, Key}, Priority, Value, _Left,
_Right}) ->
{Key, Priority, Value}.
lookup(Key, Tree) ->
HashKey = {erlang:phash2(Key), Key},
lookup1(Tree, HashKey).
lookup1(nil, _HashKey) -> error;
lookup1({HashKey1, Priority1, Value1, Left, Right},
HashKey) ->
if HashKey < HashKey1 -> lookup1(Left, HashKey);
HashKey > HashKey1 -> lookup1(Right, HashKey);
true -> {ok, Priority1, Value1}
end.
fold(_F, Acc, nil) -> Acc;
fold(F, Acc,
{{_Hash, Key}, Priority, Value, Left, Right}) ->
Acc1 = F({Key, Priority, Value}, Acc),
Acc2 = fold(F, Acc1, Left),
fold(F, Acc2, Right).
to_list(Tree) -> to_list(Tree, []).
to_list(nil, Acc) -> Acc;
to_list(Tree, Acc) ->
Root = get_root(Tree),
to_list(delete_root(Tree), [Root | Acc]).
from_list(List) -> from_list(List, nil).
from_list([{Key, Priority, Value} | Tail], Tree) ->
from_list(Tail, insert(Key, Priority, Value, Tree));
from_list([], Tree) -> Tree.
This diff is collapsed.
%%%-------------------------------------------------------------------
%%% File : turn_sm.erl
%%% Author : Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%% Description : Registers TURN sessions and credentials
%%% Created : 23 Aug 2009 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
%%%