1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1998-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(tls_socket).
21
22-behaviour(gen_server).
23
24-include("ssl_internal.hrl").
25-include("ssl_api.hrl").
26
27-export([send/3,
28         listen/3,
29         accept/3,
30         socket/5,
31         connect/4,
32         upgrade/3,
33	 setopts/3,
34         getopts/3,
35         getstat/3,
36         peername/2,
37         sockname/2,
38         port/2,
39         close/2]).
40
41-export([split_options/1,
42         get_socket_opts/3]).
43
44-export([emulated_options/0,
45         emulated_options/1,
46         internal_inet_values/0,
47         default_inet_values/0,
48	 init/1,
49         start_link/3,
50         terminate/2,
51         inherit_tracker/3,
52	 emulated_socket_options/2,
53         get_emulated_opts/1,
54	 set_emulated_opts/2,
55         get_all_opts/1,
56         handle_call/3,
57         handle_cast/2,
58	 handle_info/2,
59         code_change/3]).
60
61-export([update_active_n/2]).
62
63-record(state, {
64	  emulated_opts,
65	  port,
66	  ssl_opts
67	 }).
68
69%%--------------------------------------------------------------------
70%%% Internal API
71%%--------------------------------------------------------------------
72send(Transport, Socket, Data) ->
73    Transport:send(Socket, Data).
74
75listen(Transport, Port, #config{transport_info = {Transport, _, _, _, _},
76				inet_user = Options,
77				ssl = SslOpts, emulated = EmOpts} = Config) ->
78    case Transport:listen(Port, Options ++ internal_inet_values()) of
79	{ok, ListenSocket} ->
80	    {ok, Tracker} = inherit_tracker(ListenSocket, EmOpts, SslOpts),
81            LifeTime = get_ticket_lifetime(),
82            TicketStoreSize = get_ticket_store_size(),
83            {ok, SessionHandler} = session_tickets_tracker(LifeTime, TicketStoreSize, SslOpts),
84            Trackers =  [{option_tracker, Tracker}, {session_tickets_tracker, SessionHandler}],
85            Socket = #sslsocket{pid = {ListenSocket, Config#config{trackers = Trackers}}},
86            check_active_n(EmOpts, Socket),
87	    {ok, Socket};
88	Err = {error, _} ->
89	    Err
90    end.
91
92accept(ListenSocket, #config{transport_info = {Transport,_,_,_,_} = CbInfo,
93			     connection_cb = ConnectionCb,
94			     ssl = SslOpts,
95			     trackers = Trackers}, Timeout) ->
96    case Transport:accept(ListenSocket, Timeout) of
97	{ok, Socket} ->
98            Tracker = proplists:get_value(option_tracker, Trackers),
99            {ok, EmOpts} = get_emulated_opts(Tracker),
100	    {ok, Port} = tls_socket:port(Transport, Socket),
101            {ok, Sender} = tls_sender:start(),
102            ConnArgs = [server, Sender, "localhost", Port, Socket,
103			{SslOpts, emulated_socket_options(EmOpts, #socket_options{}), Trackers}, self(), CbInfo],
104	    case tls_connection_sup:start_child(ConnArgs) of
105		{ok, Pid} ->
106		    ssl_connection:socket_control(ConnectionCb, Socket, [Pid, Sender], Transport, Trackers);
107		{error, Reason} ->
108		    {error, Reason}
109	    end;
110	{error, Reason} ->
111	    {error, Reason}
112    end.
113
114upgrade(Socket, #config{transport_info = {Transport,_,_,_,_}= CbInfo,
115			ssl = SslOptions,
116			emulated = EmOpts, connection_cb = ConnectionCb}, Timeout) ->
117    ok = setopts(Transport, Socket, tls_socket:internal_inet_values()),
118    case peername(Transport, Socket) of
119	{ok, {Address, Port}} ->
120	    ssl_connection:connect(ConnectionCb, Address, Port, Socket,
121				   {SslOptions,
122				    emulated_socket_options(EmOpts, #socket_options{}), undefined},
123				   self(), CbInfo, Timeout);
124	{error, Error} ->
125	    {error, Error}
126    end.
127
128connect(Address, Port,
129	#config{transport_info = CbInfo, inet_user = UserOpts, ssl = SslOpts,
130		emulated = EmOpts, inet_ssl = SocketOpts, connection_cb = ConnetionCb},
131	Timeout) ->
132    {Transport, _, _, _, _} = CbInfo,
133    try Transport:connect(Address, Port,  SocketOpts, Timeout) of
134	{ok, Socket} ->
135	    ssl_connection:connect(ConnetionCb, Address, Port, Socket,
136				   {SslOpts,
137				    emulated_socket_options(EmOpts, #socket_options{}), undefined},
138				   self(), CbInfo, Timeout);
139	{error, Reason} ->
140	    {error, Reason}
141    catch
142	exit:{function_clause, _} ->
143	    {error, {options, {cb_info, CbInfo}}};
144	exit:badarg ->
145	    {error, {options, {socket_options, UserOpts}}};
146	exit:{badarg, _} ->
147	    {error, {options, {socket_options, UserOpts}}}
148    end.
149
150socket(Pids, Transport, Socket, ConnectionCb, Trackers) ->
151    #sslsocket{pid = Pids,
152	       %% "The name "fd" is keept for backwards compatibility
153	       fd = {Transport, Socket, ConnectionCb, Trackers}}.
154setopts(gen_tcp, Socket = #sslsocket{pid = {ListenSocket, #config{trackers = Trackers}}}, Options) ->
155    Tracker = proplists:get_value(option_tracker, Trackers),
156    {SockOpts, EmulatedOpts} = split_options(Options),
157    ok = set_emulated_opts(Tracker, EmulatedOpts),
158    check_active_n(EmulatedOpts, Socket),
159    inet:setopts(ListenSocket, SockOpts);
160setopts(_, Socket = #sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_,_,_},
161                                                            trackers = Trackers}}}, Options) ->
162    Tracker = proplists:get_value(option_tracker, Trackers),
163    {SockOpts, EmulatedOpts} = split_options(Options),
164    ok = set_emulated_opts(Tracker, EmulatedOpts),
165    check_active_n(EmulatedOpts, Socket),
166    Transport:setopts(ListenSocket, SockOpts);
167%%% Following clauses will not be called for emulated options, they are  handled in the connection process
168setopts(gen_tcp, Socket, Options) ->
169    inet:setopts(Socket, Options);
170setopts(Transport, Socket, Options) ->
171    Transport:setopts(Socket, Options).
172
173check_active_n(EmulatedOpts, Socket = #sslsocket{pid = {_, #config{trackers = Trackers}}}) ->
174    Tracker = proplists:get_value(option_tracker, Trackers),
175    %% We check the resulting options to send an ssl_passive message if necessary.
176    case proplists:lookup(active, EmulatedOpts) of
177        %% The provided value is out of bound.
178        {_, N} when is_integer(N), N < -32768 ->
179            throw(einval);
180        {_, N} when is_integer(N), N > 32767 ->
181            throw(einval);
182        {_, N} when is_integer(N) ->
183            case get_emulated_opts(Tracker, [active]) of
184                [{_, false}] ->
185                    self() ! {ssl_passive, Socket},
186                    ok;
187                %% The result of the addition is out of bound.
188                [{_, A}] when is_integer(A), A < -32768 ->
189                    throw(einval);
190                [{_, A}] when is_integer(A), A > 32767 ->
191                    throw(einval);
192                _ ->
193                    ok
194            end;
195        _ ->
196            ok
197    end.
198
199getopts(gen_tcp,  #sslsocket{pid = {ListenSocket, #config{trackers = Trackers}}}, Options) ->
200    Tracker = proplists:get_value(option_tracker, Trackers),
201    {SockOptNames, EmulatedOptNames} = split_options(Options),
202    EmulatedOpts = get_emulated_opts(Tracker, EmulatedOptNames),
203    SocketOpts = get_socket_opts(ListenSocket, SockOptNames, inet),
204    {ok, EmulatedOpts ++ SocketOpts};
205getopts(Transport,  #sslsocket{pid = {ListenSocket, #config{trackers = Trackers}}}, Options) ->
206    Tracker = proplists:get_value(option_tracker, Trackers),
207    {SockOptNames, EmulatedOptNames} = split_options(Options),
208    EmulatedOpts = get_emulated_opts(Tracker, EmulatedOptNames),
209    SocketOpts = get_socket_opts(ListenSocket, SockOptNames, Transport),
210    {ok, EmulatedOpts ++ SocketOpts};
211%%% Following clauses will not be called for emulated options, they are  handled in the connection process
212getopts(gen_tcp, Socket, Options) ->
213    inet:getopts(Socket, Options);
214getopts(Transport, Socket, Options) ->
215    Transport:getopts(Socket, Options).
216
217getstat(gen_tcp, Socket, Options) ->
218	inet:getstat(Socket, Options);
219getstat(Transport, Socket, Options) ->
220	Transport:getstat(Socket, Options).
221
222peername(gen_tcp, Socket) ->
223    inet:peername(Socket);
224peername(Transport, Socket) ->
225    Transport:peername(Socket).
226
227sockname(gen_tcp, Socket) ->
228    inet:sockname(Socket);
229sockname(Transport, Socket) ->
230    Transport:sockname(Socket).
231
232port(gen_tcp, Socket) ->
233    inet:port(Socket);
234port(Transport, Socket) ->
235    Transport:port(Socket).
236
237close(gen_tcp, Socket) ->
238    inet:close(Socket);
239close(Transport, Socket) ->
240    Transport:close(Socket).
241
242emulated_options() ->
243    [mode, packet, active, header, packet_size].
244
245emulated_options(Opts) ->
246      emulated_options(Opts, internal_inet_values(), default_inet_values()).
247
248internal_inet_values() ->
249    [{packet_size,0}, {packet, 0}, {header, 0}, {active, false}, {mode,binary}].
250
251default_inet_values() ->
252    [{packet_size, 0}, {packet,0}, {header, 0}, {active, true}, {mode, list}].
253
254inherit_tracker(ListenSocket, EmOpts, #{erl_dist := false} = SslOpts) ->
255    ssl_listen_tracker_sup:start_child([ListenSocket, EmOpts, SslOpts]);
256inherit_tracker(ListenSocket, EmOpts, #{erl_dist := true} = SslOpts) ->
257    ssl_listen_tracker_sup:start_child_dist([ListenSocket, EmOpts, SslOpts]).
258
259session_tickets_tracker(_, _, #{erl_dist := false,
260                                session_tickets := disabled}) ->
261    {ok, disabled};
262session_tickets_tracker(Lifetime, TicketStoreSize, #{erl_dist := false,
263                                    session_tickets := Mode,
264                                    anti_replay := AntiReplay}) ->
265    tls_server_session_ticket_sup:start_child([Mode, Lifetime, TicketStoreSize, AntiReplay]);
266session_tickets_tracker(Lifetime, TicketStoreSize, #{erl_dist := true,
267                                                     session_tickets := Mode}) ->
268    tls_server_session_ticket_sup:start_child_dist([Mode, Lifetime, TicketStoreSize]).
269
270
271get_emulated_opts(TrackerPid) ->
272    call(TrackerPid, get_emulated_opts).
273set_emulated_opts(TrackerPid, InetValues) ->
274    call(TrackerPid, {set_emulated_opts, InetValues}).
275get_all_opts(TrackerPid) ->
276    call(TrackerPid, get_all_opts).
277
278%%====================================================================
279%% ssl_listen_tracker_sup API
280%%====================================================================
281
282start_link(Port, SockOpts, SslOpts) ->
283    gen_server:start_link(?MODULE, [Port, SockOpts, SslOpts], []).
284
285%%--------------------------------------------------------------------
286-spec init(list()) -> {ok, #state{}}.
287%% Possible return values not used now.
288%% |  {ok, #state{}, timeout()} | ignore | {stop, term()}.
289%%
290%% Description: Initiates the server
291%%--------------------------------------------------------------------
292init([Port, Opts, SslOpts]) ->
293    process_flag(trap_exit, true),
294    true = link(Port),
295    {ok, #state{emulated_opts = do_set_emulated_opts(Opts, []), port = Port, ssl_opts = SslOpts}}.
296
297%%--------------------------------------------------------------------
298-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}}.
299%% Possible return values not used now.
300%%					      {reply, reply(), #state{}, timeout()} |
301%%					      {noreply, #state{}} |
302%%					      {noreply, #state{}, timeout()} |
303%%					      {stop, reason(), reply(), #state{}} |
304%%					      {stop, reason(), #state{}}.
305%%
306%% Description: Handling call messages
307%%--------------------------------------------------------------------
308handle_call({set_emulated_opts, Opts0}, _From,
309	    #state{emulated_opts = Opts1} = State) ->
310    Opts = do_set_emulated_opts(Opts0, Opts1),
311    {reply, ok, State#state{emulated_opts = Opts}};
312handle_call(get_emulated_opts, _From,
313	    #state{emulated_opts = Opts} = State) ->
314    {reply, {ok, Opts}, State};
315handle_call(get_all_opts, _From,
316	    #state{emulated_opts = EmOpts,
317		   ssl_opts = SslOpts} = State) ->
318    {reply, {ok, EmOpts, SslOpts}, State}.
319
320%%--------------------------------------------------------------------
321-spec  handle_cast(msg(), #state{}) -> {noreply, #state{}}.
322%% Possible return values not used now.
323%%				      | {noreply, #state{}, timeout()} |
324%%				       {stop, reason(), #state{}}.
325%%
326%% Description: Handling cast messages
327%%--------------------------------------------------------------------
328handle_cast(_, State)->
329    {noreply, State}.
330
331%%--------------------------------------------------------------------
332-spec handle_info(msg(), #state{}) ->  {stop, reason(), #state{}}.
333%% Possible return values not used now.
334%%			              {noreply, #state{}}.
335%%				      |{noreply, #state{}, timeout()} |
336%%
337%%
338%% Description: Handling all non call/cast messages
339%%-------------------------------------------------------------------
340handle_info({'EXIT', Port, _}, #state{port = Port} = State) ->
341    {stop, normal, State}.
342
343
344%%--------------------------------------------------------------------
345-spec terminate(reason(), #state{}) -> ok.
346%%
347%% Description: This function is called by a gen_server when it is about to
348%% terminate. It should be the opposite of Module:init/1 and do any necessary
349%% cleaning up. When it returns, the gen_server terminates with Reason.
350%% The return value is ignored.
351%%--------------------------------------------------------------------
352terminate(_Reason, _State) ->
353    ok.
354
355%%--------------------------------------------------------------------
356-spec code_change(term(), #state{}, list()) -> {ok, #state{}}.
357%%
358%% Description: Convert process state when code is changed
359%%--------------------------------------------------------------------
360code_change(_OldVsn, State, _Extra) ->
361    {ok, State}.
362
363%%--------------------------------------------------------------------
364%%% Internal functions
365%%--------------------------------------------------------------------
366call(Pid, Msg) ->
367    gen_server:call(Pid, Msg, infinity).
368
369split_options(Opts) ->
370    split_options(Opts, emulated_options(), [], []).
371split_options([], _, SocketOpts, EmuOpts) ->
372    {SocketOpts, EmuOpts};
373split_options([{Name, _} = Opt | Opts], Emu, SocketOpts, EmuOpts) ->
374    case lists:member(Name, Emu) of
375	true ->
376	    split_options(Opts, Emu, SocketOpts, [Opt | EmuOpts]);
377	false ->
378	    split_options(Opts, Emu, [Opt | SocketOpts], EmuOpts)
379    end;
380split_options([Name | Opts], Emu, SocketOptNames, EmuOptNames) ->
381    case lists:member(Name, Emu) of
382	true ->
383	    split_options(Opts, Emu, SocketOptNames, [Name | EmuOptNames]);
384	false ->
385	    split_options(Opts, Emu, [Name | SocketOptNames], EmuOptNames)
386    end.
387
388do_set_emulated_opts([], Opts) ->
389    Opts;
390do_set_emulated_opts([{active, N0} | Rest], Opts) when is_integer(N0) ->
391    N = update_active_n(N0, proplists:get_value(active, Opts, false)),
392    do_set_emulated_opts(Rest, [{active, N} | proplists:delete(active, Opts)]);
393do_set_emulated_opts([{Name,_} = Opt | Rest], Opts) ->
394    do_set_emulated_opts(Rest, [Opt | proplists:delete(Name, Opts)]).
395
396update_active_n(New, Current) ->
397    if
398        is_integer(Current), New + Current =< 0 ->
399            false;
400        is_integer(Current) ->
401            New + Current;
402        New =< 0 ->
403            false;
404        true ->
405            New
406    end.
407
408get_socket_opts(_, [], _) ->
409    [];
410get_socket_opts(ListenSocket, SockOptNames, Cb) ->
411    {ok, Opts} = Cb:getopts(ListenSocket, SockOptNames),
412    Opts.
413
414get_emulated_opts(TrackerPid, EmOptNames) ->
415    {ok, EmOpts} = get_emulated_opts(TrackerPid),
416    lists:map(fun(Name) -> {value, Value} = lists:keysearch(Name, 1, EmOpts),
417			   Value end,
418	      EmOptNames).
419
420emulated_socket_options(InetValues, #socket_options{
421				       mode   = Mode,
422				       header = Header,
423				       active = Active,
424				       packet = Packet,
425				       packet_size = Size}) ->
426    #socket_options{
427       mode   = proplists:get_value(mode, InetValues, Mode),
428       header = proplists:get_value(header, InetValues, Header),
429       active = proplists:get_value(active, InetValues, Active),
430       packet = proplists:get_value(packet, InetValues, Packet),
431       packet_size = proplists:get_value(packet_size, InetValues, Size)
432      }.
433
434emulated_options([{mode, Value} = Opt |Opts], Inet, Emulated) ->
435    validate_inet_option(mode, Value),
436    emulated_options(Opts, Inet, [Opt | proplists:delete(mode, Emulated)]);
437emulated_options([{header, Value} = Opt | Opts], Inet, Emulated) ->
438    validate_inet_option(header, Value),
439    emulated_options(Opts, Inet,  [Opt | proplists:delete(header, Emulated)]);
440emulated_options([{active, Value} = Opt |Opts], Inet, Emulated) ->
441    validate_inet_option(active, Value),
442    emulated_options(Opts, Inet, [Opt | proplists:delete(active, Emulated)]);
443emulated_options([{packet, Value} = Opt |Opts], Inet, Emulated) ->
444    validate_inet_option(packet, Value),
445    emulated_options(Opts, Inet, [Opt | proplists:delete(packet, Emulated)]);
446emulated_options([{packet_size, Value} = Opt | Opts], Inet, Emulated) ->
447    validate_inet_option(packet_size, Value),
448    emulated_options(Opts, Inet, [Opt | proplists:delete(packet_size, Emulated)]);
449emulated_options([Opt|Opts], Inet, Emulated) ->
450    emulated_options(Opts, [Opt|Inet], Emulated);
451emulated_options([], Inet,Emulated) ->
452    {Inet, Emulated}.
453
454validate_inet_option(mode, Value)
455  when Value =/= list, Value =/= binary ->
456    throw({error, {options, {mode,Value}}});
457validate_inet_option(packet, Value)
458  when not (is_atom(Value) orelse is_integer(Value)) ->
459    throw({error, {options, {packet,Value}}});
460validate_inet_option(packet_size, Value)
461  when not is_integer(Value) ->
462    throw({error, {options, {packet_size,Value}}});
463validate_inet_option(header, Value)
464  when not is_integer(Value) ->
465    throw({error, {options, {header,Value}}});
466validate_inet_option(active, Value)
467  when Value >= -32768, Value =< 32767 ->
468    ok;
469validate_inet_option(active, Value)
470  when Value =/= true, Value =/= false, Value =/= once ->
471    throw({error, {options, {active,Value}}});
472validate_inet_option(_, _) ->
473    ok.
474
475get_ticket_lifetime() ->
476    case application:get_env(ssl, server_session_ticket_lifetime) of
477	{ok, Seconds} when is_integer(Seconds) andalso
478                           Seconds =< 604800 ->  %% MUST be less than 7 days
479	    Seconds;
480	_  ->
481	    7200 %% Default 2 hours
482    end.
483
484get_ticket_store_size() ->
485    case application:get_env(ssl, server_session_ticket_store_size) of
486	{ok, Size} when is_integer(Size) ->
487	    Size;
488	_  ->
489	    1000
490    end.
491