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