Skip to content

Commit

Permalink
Separate white/blacklists for clients and peers
Browse files Browse the repository at this point in the history
Allow for specifying blacklists/whitelists for TURN clients and TURN
peers separately.
  • Loading branch information
weiss committed Sep 26, 2023
1 parent f9f3581 commit 4f02bfa
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 22 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Version 1.2.9

* Allow for specifying white/blacklists for clients and peers separately.

# Version 1.2.8

* Fix expiry of nonces.
Expand Down
74 changes: 62 additions & 12 deletions src/stun.erl
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,10 @@
max_allocs = 10 :: non_neg_integer() | infinity,
shaper = none :: stun_shaper:shaper(),
max_permissions = 10 :: non_neg_integer() | infinity,
blacklist = [] :: turn:accesslist(),
whitelist = [] :: turn:accesslist(),
blacklist_clients = [] :: turn:accesslist(),
whitelist_clients = [] :: turn:accesslist(),
blacklist_peers = [] :: turn:accesslist(),
whitelist_peers = [] :: turn:accesslist(),
auth = user :: anonymous | user,
nonces = treap:empty() :: treap:treap(),
realm = <<"">> :: binary(),
Expand Down Expand Up @@ -354,8 +356,10 @@ process(State, #stun{class = request,
{server_name, State#state.server_name},
{max_allocs, State#state.max_allocs},
{max_permissions, State#state.max_permissions},
{blacklist, State#state.blacklist},
{whitelist, State#state.whitelist},
{blacklist_clients, State#state.blacklist_clients},
{whitelist_clients, State#state.whitelist_clients},
{blacklist_peers, State#state.blacklist_peers},
{whitelist_peers, State#state.whitelist_peers},
{addr, AddrPort},
{relay_ipv4_ip, State#state.relay_ipv4_ip},
{relay_ipv6_ip, State#state.relay_ipv6_ip},
Expand Down Expand Up @@ -550,24 +554,70 @@ prepare_state(Opts, Sock, Peer, SockMod) when is_list(Opts) ->
?LOG_ERROR("Wrong 'turn_max_permissions' value: ~p",
[Wrong]),
State;
({turn_blacklist, B}, State) ->
({turn_blacklist_clients, B},
#state{blacklist_clients = B0} = State) ->
case lists:all(fun is_valid_subnet/1, B) of
true ->
State#state{blacklist = B};
State#state{blacklist_clients = B0 ++ B};
false ->
?LOG_ERROR("Wrong 'turn_blacklist' value: ~p",
[B]),
?LOG_ERROR("Wrong 'turn_blacklist_clients' "
"value: ~p", [B]),
State
end;
({turn_whitelist, B}, State) ->
({turn_whitelist_clients, W},
#state{whitelist_clients = W0} = State) ->
case lists:all(fun is_valid_subnet/1, W) of
true ->
State#state{whitelist_clients = W0 ++ W};
false ->
?LOG_ERROR("Wrong 'turn_whitelist_clients' "
"value: ~p", [W]),
State
end;
({turn_blacklist_peers, B},
#state{blacklist_peers = B0} = State) ->
case lists:all(fun is_valid_subnet/1, B) of
true ->
State#state{whitelist = B};
State#state{blacklist_peers = B0 ++ B};
false ->
?LOG_ERROR("Wrong 'turn_blacklist_peers' "
"value: ~p", [B]),
State
end;
({turn_whitelist_peers, W},
#state{whitelist_peers = W0} = State) ->
case lists:all(fun is_valid_subnet/1, W) of
true ->
State#state{whitelist_peers = W0 ++ W};
false ->
?LOG_ERROR("Wrong 'turn_whitelist' value: ~p",
[B]),
?LOG_ERROR("Wrong 'turn_whitelist_peers' "
"value: ~p", [W]),
State
end;
({turn_blacklist, B}, #state{blacklist_clients = C,
blacklist_peers = P} = State0) ->
case lists:all(fun is_valid_subnet/1, B) of
true ->
State1 = State0#state{blacklist_clients = C ++ B},
State2 = State1#state{blacklist_peers = P ++ B},
State2;
false ->
?LOG_ERROR("Wrong 'turn_blacklist' "
"value: ~p", [B]),
State0
end;
({turn_whitelist, W}, #state{whitelist_clients = C,
whitelist_peers = P} = State0) ->
case lists:all(fun is_valid_subnet/1, W) of
true ->
State1 = State0#state{whitelist_clients = C ++ W},
State2 = State1#state{whitelist_peers = P ++ W},
State2;
false ->
?LOG_ERROR("Wrong 'turn_whitelist' "
"value: ~p", [W]),
State0
end;
({shaper, S}, State)
when S == none orelse (is_integer(S) andalso (S > 0)) ->
State#state{shaper = stun_shaper:new(S)};
Expand Down
33 changes: 23 additions & 10 deletions src/turn.erl
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,10 @@
last_pkt = <<>> :: binary(),
seq = 1 :: non_neg_integer(),
life_timer :: reference() | undefined,
blacklist = [] :: accesslist(),
whitelist = [] :: accesslist(),
blacklist_clients = [] :: accesslist(),
whitelist_clients = [] :: accesslist(),
blacklist_peers = [] :: accesslist(),
whitelist_peers = [] :: accesslist(),
hook_fun :: function() | undefined,
session_id :: binary(),
rcvd_bytes = 0 :: non_neg_integer(),
Expand Down Expand Up @@ -120,8 +122,10 @@ init([Opts]) ->
AddrPort = proplists:get_value(addr, Opts),
SockMod = proplists:get_value(sock_mod, Opts),
HookFun = proplists:get_value(hook_fun, Opts),
Blacklist = proplists:get_value(blacklist, Opts) ++ ?INITIAL_BLACKLIST,
Whitelist = proplists:get_value(whitelist, Opts),
BlacklistClients = proplists:get_value(blacklist_clients, Opts),
WhitelistClients = proplists:get_value(whitelist_clients, Opts),
BlacklistPeers = proplists:get_value(blacklist_peers, Opts),
WhitelistPeers = proplists:get_value(whitelist_peers, Opts),
State = #state{sock_mod = SockMod,
sock = proplists:get_value(sock, Opts),
key = proplists:get_value(key, Opts),
Expand All @@ -133,7 +137,10 @@ init([Opts]) ->
server_name = proplists:get_value(server_name, Opts),
username = Username, realm = Realm, addr = AddrPort,
session_id = ID, owner = Owner, hook_fun = HookFun,
blacklist = Blacklist, whitelist = Whitelist},
blacklist_clients = BlacklistClients ++ ?INITIAL_BLACKLIST,
whitelist_clients = WhitelistClients,
blacklist_peers = BlacklistPeers ++ ?INITIAL_BLACKLIST,
whitelist_peers = WhitelistPeers},
stun_logger:set_metadata(turn, SockMod, ID, AddrPort, Username),
MaxAllocs = proplists:get_value(max_allocs, Opts),
if is_pid(Owner) ->
Expand Down Expand Up @@ -162,7 +169,7 @@ wait_for_allocate(#stun{class = request,
undefined -> inet;
unknown -> unknown
end,
IsBlacklisted = blacklisted(State),
IsBlacklisted = is_blacklisted_client(State),
Resp = prepare_response(State, Msg),
if Msg#stun.'REQUESTED-TRANSPORT' == undefined ->
?LOG_NOTICE("Rejecting allocation request: no transport requested"),
Expand Down Expand Up @@ -498,7 +505,7 @@ update_permissions(#state{permissions = Perms, max_permissions = Max}, Addrs)
when map_size(Perms) + length(Addrs) > Max ->
{error, 508};
update_permissions(#state{relay_addr = {IP, _}} = State, Addrs) ->
case {families_match(IP, Addrs), blacklisted(State, Addrs)} of
case {families_match(IP, Addrs), is_blacklisted_peer(State, Addrs)} of
{true, false} ->
Perms = lists:foldl(
fun(Addr, Acc) ->
Expand Down Expand Up @@ -603,10 +610,16 @@ family_matches({_, _, _, _, _, _, _, _}, {_, _, _, _, _, _, _, _}) ->
family_matches(_Addr1, _Addr2) ->
false.

blacklisted(#state{addr = {IP, _Port}} = State) ->
blacklisted(State, [IP]).
is_blacklisted_client(#state{addr = {IP, _Port},
blacklist_clients = Blacklist,
whitelist_clients = Whitelist}) ->
is_blacklisted(Blacklist, Whitelist, [IP]).

blacklisted(#state{blacklist = Blacklist, whitelist = Whitelist}, IPs) ->
is_blacklisted_peer(#state{blacklist_peers = Blacklist,
whitelist_peers = Whitelist}, IPs) ->
is_blacklisted(Blacklist, Whitelist, IPs).

is_blacklisted(Blacklist, Whitelist, IPs) ->
lists:any(
fun(IP) ->
lists:any(
Expand Down

0 comments on commit 4f02bfa

Please sign in to comment.