aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/dtls_udp_listener.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/dtls_udp_listener.erl')
-rw-r--r--lib/ssl/src/dtls_udp_listener.erl66
1 files changed, 62 insertions, 4 deletions
diff --git a/lib/ssl/src/dtls_udp_listener.erl b/lib/ssl/src/dtls_udp_listener.erl
index f0ace2d887..12e54a0e51 100644
--- a/lib/ssl/src/dtls_udp_listener.erl
+++ b/lib/ssl/src/dtls_udp_listener.erl
@@ -23,9 +23,11 @@
-behaviour(gen_server).
+-include("ssl_internal.hrl").
+
%% API
-export([start_link/4, active_once/3, accept/2, sockname/1, close/1,
- get_all_opts/1]).
+ get_all_opts/1, get_sock_opts/2, set_sock_opts/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -61,8 +63,12 @@ sockname(UDPConnection) ->
call(UDPConnection, sockname).
close(UDPConnection) ->
call(UDPConnection, close).
+get_sock_opts(UDPConnection, SplitSockOpts) ->
+ call(UDPConnection, {get_sock_opts, SplitSockOpts}).
get_all_opts(UDPConnection) ->
call(UDPConnection, get_all_opts).
+set_sock_opts(UDPConnection, Opts) ->
+ call(UDPConnection, {set_sock_opts, Opts}).
%%%===================================================================
%%% gen_server callbacks
@@ -78,7 +84,7 @@ init([Port, EmOpts, InetOptions, DTLSOptions]) ->
listner = Socket,
close = false}}
catch _:_ ->
- {error, closed}
+ {stop, {shutdown, {error, closed}}}
end.
handle_call({accept, _}, _, #state{close = true} = State) ->
{reply, {error, closed}, State};
@@ -108,9 +114,21 @@ handle_call(close, _, #state{dtls_processes = Processes,
end, queue:to_list(Accepters)),
{reply, ok, State#state{close = true, accepters = queue:new()}}
end;
+handle_call({get_sock_opts, {SocketOptNames, EmOptNames}}, _, #state{listner = Socket,
+ emulated_options = EmOpts} = State) ->
+ case get_socket_opts(Socket, SocketOptNames) of
+ {ok, Opts} ->
+ {reply, {ok, emulated_opts_list(EmOpts, EmOptNames, []) ++ Opts}, State};
+ {error, Reason} ->
+ {reply, {error, Reason}, State}
+ end;
handle_call(get_all_opts, _, #state{dtls_options = DTLSOptions,
emulated_options = EmOpts} = State) ->
- {reply, {ok, EmOpts, DTLSOptions}, State}.
+ {reply, {ok, EmOpts, DTLSOptions}, State};
+handle_call({set_sock_opts, {SocketOpts, NewEmOpts}}, _, #state{listner = Socket, emulated_options = EmOpts0} = State) ->
+ set_socket_opts(Socket, SocketOpts),
+ EmOpts = do_set_emulated_opts(NewEmOpts, EmOpts0),
+ {reply, ok, State#state{emulated_options = EmOpts}}.
handle_cast({active_once, Client, Pid}, State0) ->
State = handle_active_once(Client, Pid, State0),
@@ -121,17 +139,32 @@ handle_info({udp, Socket, IP, InPortNo, _} = Msg, #state{listner = Socket} = Sta
next_datagram(Socket),
{noreply, State};
+%% UDP socket does not have a connection and should not receive an econnreset
+%% This does however happens on on some windows versions. Just ignoring it
+%% appears to make things work as expected!
+handle_info({udp_error, Socket, econnreset = Error}, #state{listner = Socket} = State) ->
+ Report = io_lib:format("Ignore SSL UDP Listener: Socket error: ~p ~n", [Error]),
+ error_logger:info_report(Report),
+ {noreply, State};
+handle_info({udp_error, Socket, Error}, #state{listner = Socket} = State) ->
+ Report = io_lib:format("SSL UDP Listener shutdown: Socket error: ~p ~n", [Error]),
+ error_logger:info_report(Report),
+ {noreply, State#state{close=true}};
+
handle_info({'DOWN', _, process, Pid, _}, #state{clients = Clients,
dtls_processes = Processes0,
+ dtls_msq_queues = MsgQueues0,
close = ListenClosed} = State) ->
Client = kv_get(Pid, Processes0),
Processes = kv_delete(Pid, Processes0),
+ MsgQueues = kv_delete(Client, MsgQueues0),
case ListenClosed andalso kv_empty(Processes) of
true ->
{stop, normal, State};
false ->
{noreply, State#state{clients = set_delete(Client, Clients),
- dtls_processes = Processes}}
+ dtls_processes = Processes,
+ dtls_msq_queues = MsgQueues}}
end.
terminate(_Reason, _State) ->
@@ -247,3 +280,28 @@ call(Server, Msg) ->
exit:{{shutdown, _},_} ->
{error, closed}
end.
+
+set_socket_opts(_, []) ->
+ ok;
+set_socket_opts(Socket, SocketOpts) ->
+ inet:setopts(Socket, SocketOpts).
+
+get_socket_opts(_, []) ->
+ {ok, []};
+get_socket_opts(Socket, SocketOpts) ->
+ inet:getopts(Socket, SocketOpts).
+
+do_set_emulated_opts([], Opts) ->
+ Opts;
+do_set_emulated_opts([{mode, Value} | Rest], Opts) ->
+ do_set_emulated_opts(Rest, Opts#socket_options{mode = Value});
+do_set_emulated_opts([{active, Value} | Rest], Opts) ->
+ do_set_emulated_opts(Rest, Opts#socket_options{active = Value}).
+
+emulated_opts_list(_,[], Acc) ->
+ Acc;
+emulated_opts_list( Opts, [mode | Rest], Acc) ->
+ emulated_opts_list(Opts, Rest, [{mode, Opts#socket_options.mode} | Acc]);
+emulated_opts_list(Opts, [active | Rest], Acc) ->
+ emulated_opts_list(Opts, Rest, [{active, Opts#socket_options.active} | Acc]).
+