%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2019-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% -module(tls_api_SUITE). %% Note: This directive should only be used in test suites. -compile(export_all). -include_lib("common_test/include/ct.hrl"). -include_lib("ssl/src/ssl_record.hrl"). -include_lib("ssl/src/ssl_internal.hrl"). -include_lib("ssl/src/ssl_api.hrl"). -include_lib("ssl/src/tls_handshake.hrl"). -define(SLEEP, 500). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- all() -> [ {group, 'tlsv1.3'}, {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, {group, 'sslv3'} ]. groups() -> [ {'tlsv1.3', [], api_tests() -- [sockname]}, {'tlsv1.2', [], api_tests()}, {'tlsv1.1', [], api_tests()}, {'tlsv1', [], api_tests()}, {'sslv3', [], api_tests() ++ [ssl3_cipher_suite_limitation]} ]. api_tests() -> [ tls_upgrade, tls_upgrade_with_timeout, tls_downgrade, tls_shutdown, tls_shutdown_write, tls_shutdown_both, tls_shutdown_error, tls_client_closes_socket, tls_closed_in_active_once, tls_tcp_msg, tls_tcp_msg_big, tls_dont_crash_on_handshake_garbage, tls_tcp_error_propagation_in_active_mode, peername, sockname, tls_server_handshake_timeout, transport_close, emulated_options, accept_pool, reuseaddr ]. init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> ssl_test_lib:clean_start(), ssl_test_lib:make_rsa_cert(Config0) catch _:_ -> {skip, "Crypto did not start"} end. end_per_suite(_Config) -> ssl:stop(), application:unload(ssl), application:stop(crypto). init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of true -> case ssl_test_lib:sufficient_crypto_support(GroupName) of true -> ssl_test_lib:init_tls_version(GroupName, Config); false -> {skip, "Missing crypto support"} end; _ -> ssl:start(), Config end. end_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of true -> ssl_test_lib:clean_tls_version(Config); false -> Config end. init_per_testcase(_TestCase, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 10}), Config. end_per_testcase(_TestCase, Config) -> Config. %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- tls_upgrade() -> [{doc,"Test that you can upgrade an tcp connection to an ssl connection"}]. tls_upgrade(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, upgrade_result, []}}, {tcp_options, [{active, false} | TcpOpts]}, {ssl_options, [{verify, verify_peer} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, upgrade_result, []}}, {tcp_options, [binary]}, {ssl_options, [{verify, verify_peer}, {server_name_indication, Hostname} | ClientOpts]}]), ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- tls_upgrade_with_timeout() -> [{doc,"Test ssl_accept/3"}]. tls_upgrade_with_timeout(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, {from, self()}, {timeout, 5000}, {mfa, {?MODULE, upgrade_result, []}}, {tcp_options, [{active, false} | TcpOpts]}, {ssl_options, [{verify, verify_peer} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, upgrade_result, []}}, {tcp_options, TcpOpts}, {ssl_options, [{verify, verify_peer}, {server_name_indication, Hostname} | ClientOpts]}]), ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- tls_downgrade() -> [{doc,"Test that you can downgarde an ssl connection to an tcp connection"}]. tls_downgrade(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, tls_downgrade_result, [self()]}}, {options, [{active, false}, {verify, verify_peer} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, tls_downgrade_result, [self()]}}, {options, [{active, false}, {verify, verify_peer} | ClientOpts]}]), ssl_test_lib:check_result(Server, ready, Client, ready), Server ! go, Client ! go, ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- tls_shutdown() -> [{doc,"Test API function ssl:shutdown/2"}]. tls_shutdown(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, tls_shutdown_result, [server]}}, {options, [{exit_on_close, false}, {active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, tls_shutdown_result, [client]}}, {options, [{exit_on_close, false}, {active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- tls_shutdown_write() -> [{doc,"Test API function ssl:shutdown/2 with option write."}]. tls_shutdown_write(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, tls_shutdown_write_result, [server]}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, tls_shutdown_write_result, [client]}}, {options, [{active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, {error, closed}). %%-------------------------------------------------------------------- tls_shutdown_both() -> [{doc,"Test API function ssl:shutdown/2 with option both."}]. tls_shutdown_both(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, tls_shutdown_both_result, [server]}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, tls_shutdown_both_result, [client]}}, {options, [{active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, {error, closed}). %%-------------------------------------------------------------------- tls_shutdown_error() -> [{doc,"Test ssl:shutdown/2 error handling"}]. tls_shutdown_error(Config) when is_list(Config) -> ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), Port = ssl_test_lib:inet_port(node()), {ok, Listen} = ssl:listen(Port, ServerOpts), {error, enotconn} = ssl:shutdown(Listen, read_write), ok = ssl:close(Listen), {error, closed} = ssl:shutdown(Listen, read_write). %%-------------------------------------------------------------------- tls_client_closes_socket() -> [{doc,"Test what happens when client closes socket before handshake is compleated"}]. tls_client_closes_socket(Config) when is_list(Config) -> ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, {from, self()}, {tcp_options, TcpOpts}, {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Connect = fun() -> {ok, _Socket} = rpc:call(ClientNode, gen_tcp, connect, [Hostname, Port, [binary]]), %% Make sure that ssl_accept is called before %% client process ends and closes socket. ct:sleep(?SLEEP) end, _Client = spawn_link(Connect), ssl_test_lib:check_result(Server, {error,closed}). %%-------------------------------------------------------------------- tls_closed_in_active_once() -> [{doc, "Test that ssl_closed is delivered in active once with non-empty buffer, check ERL-420."}]. tls_closed_in_active_once(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {_ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], Port = ssl_test_lib:inet_port(node()), Server = fun() -> {ok, Listen} = gen_tcp:listen(Port, TcpOpts), {ok, TcpServerSocket} = gen_tcp:accept(Listen), {ok, ServerSocket} = ssl:handshake(TcpServerSocket, ServerOpts), lists:foreach( fun(_) -> ssl:send(ServerSocket, "some random message\r\n") end, lists:seq(1, 20)), %% Close TCP instead of SSL socket to trigger the bug: gen_tcp:close(TcpServerSocket), gen_tcp:close(Listen) end, spawn_link(Server), {ok, Socket} = ssl:connect(Hostname, Port, [{active, false} | ClientOpts]), Result = tls_closed_in_active_once_loop(Socket), ssl:close(Socket), case Result of ok -> ok; _ -> ct:fail(Result) end. %%-------------------------------------------------------------------- tls_tcp_msg() -> [{doc,"Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"}]. tls_tcp_msg(Config) when is_list(Config) -> ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}, {active, false}], Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, {from, self()}, {timeout, 5000}, {mfa, {?MODULE, dummy, []}}, {tcp_options, TcpOpts}, {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), gen_tcp:send(Socket, ""), receive {tcp_closed, Socket} -> receive {Server, {error, Error}} -> ct:log("Error ~p", [Error]) end end. %%-------------------------------------------------------------------- tls_tcp_msg_big() -> [{doc,"Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"}]. tls_tcp_msg_big(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], Rand = crypto:strong_rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1), Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, {from, self()}, {timeout, 5000}, {mfa, {?MODULE, dummy, []}}, {tcp_options, TcpOpts}, {ssl_options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), gen_tcp:send(Socket, <>), receive {tcp_closed, Socket} -> receive {Server, {error, timeout}} -> ct:fail("hangs"); {Server, {error, Error}} -> ct:log("Error ~p", [Error]); {'EXIT', Server, _} -> ok end end. %%-------------------------------------------------------------------- tls_dont_crash_on_handshake_garbage() -> [{doc, "Ensure SSL server worker thows an alert on garbage during handshake " "instead of crashing and exposing state to user code"}]. tls_dont_crash_on_handshake_garbage(Config) -> ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ServerOpts}]), unlink(Server), monitor(process, Server), Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]), % Send hello and garbage record ok = gen_tcp:send(Socket, [<<22, 3,3, 49:16, 1, 45:24, 3,3, % client_hello 16#deadbeef:256, % 32 'random' bytes = 256 bits 0, 6:16, 0,255, 0,61, 0,57, 1, 0 >>, % some hello values <<22, 3,3, 5:16, 92,64,37,228,209>> % garbage ]), % Send unexpected change_cipher_spec ok = gen_tcp:send(Socket, <<20, 3,3, 12:16, 111,40,244,7,137,224,16,109,197,110,249,152>>), % Ensure we receive an alert, not sudden disconnect {ok, <<21, _/binary>>} = drop_handshakes(Socket, 1000). %%-------------------------------------------------------------------- tls_tcp_error_propagation_in_active_mode() -> [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"}]. tls_tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {Client, #sslsocket{pid=[Pid|_]} = SslSocket} = ssl_test_lib:start_client([return_socket, {node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, receive_msg, []}}, {options, ClientOpts}]), {status, _, _, StatusInfo} = sys:get_status(Pid), [_, _,_, _, Prop] = StatusInfo, State = ssl_test_lib:state(Prop), StaticEnv = element(2, State), Socket = element(11, StaticEnv), %% Fake tcp error Pid ! {tcp_error, Socket, etimedout}, ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}). %%-------------------------------------------------------------------- peername() -> [{doc,"Test API function peername/1"}]. peername(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, peername_result, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, peername_result, []}}, {options, [{port, 0} | ClientOpts]}]), ClientPort = ssl_test_lib:inet_port(Client), ServerIp = ssl_test_lib:node_to_hostip(ServerNode, server), ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client), ServerMsg = {ok, {ClientIp, ClientPort}}, ClientMsg = {ok, {ServerIp, Port}}, ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- sockname() -> [{doc,"Test API function sockname/1"}]. sockname(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, sockname_result, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, sockname_result, []}}, {options, [{port, 0} | ClientOpts]}]), ClientPort = ssl_test_lib:inet_port(Client), ServerIp = ssl_test_lib:node_to_hostip(ServerNode, server), ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client), ServerMsg = {ok, {ServerIp, Port}}, ClientMsg = {ok, {ClientIp, ClientPort}}, ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- tls_server_handshake_timeout() -> [{doc,"Test server handshake timeout"}]. tls_server_handshake_timeout(Config) -> process_flag(trap_exit, true), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {timeout, 5000}, {mfa, {ssl_test_lib, no_result_msg, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {ok, CSocket} = gen_tcp:connect(Hostname, Port, [binary, {active, true}]), receive {tcp_closed, CSocket} -> ssl_test_lib:check_result(Server, {error, timeout}), receive {'EXIT', Server, _} -> %% Make sure supervisor had time to react on process exit %% Could we come up with a better solution to this? ct:sleep(500), [] = supervisor:which_children(tls_connection_sup) end end. transport_close() -> [{doc, "Test what happens if socket is closed on TCP level after a while of normal operation"}]. transport_close(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), {ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect, [Hostname,Port,[binary, {active, false}]]), {ok, SslS} = rpc:call(ClientNode, ssl, connect, [TcpS,[{active, false}|ClientOpts]]), ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), self(), Server]), ok = ssl:send(SslS, "Hello world"), {ok,<<"Hello world">>} = ssl:recv(SslS, 11), gen_tcp:close(TcpS), {error, _} = ssl:send(SslS, "Hello world"). %%-------------------------------------------------------------------- ssl3_cipher_suite_limitation() -> [{doc,"Test a SSLv3 client cannot negotiate a TLSv* cipher suite."}]. ssl3_cipher_suite_limitation(Config) when is_list(Config) -> {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]), ok = gen_tcp:send(Socket, <<22, 3,0, 49:16, % handshake, SSL 3.0, length 1, 45:24, % client_hello, length 3,0, % SSL 3.0 16#deadbeef:256, % 32 'random' bytes = 256 bits 0, % no session ID %% three cipher suites -- null, one with sha256 hash and one with sha hash 6:16, 0,255, 0,61, 0,57, 1, 0 % no compression >>), {ok, <<22, RecMajor:8, RecMinor:8, _RecLen:16, 2, HelloLen:24>>} = gen_tcp:recv(Socket, 9, 10000), {ok, <>} = gen_tcp:recv(Socket, HelloLen, 5000), ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin), case ServerHello of #server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} -> ok; _ -> ct:fail({unexpected_server_hello, ServerHello}) end. %%-------------------------------------------------------------------- emulated_options() -> [{doc,"Test API function getopts/2 and setopts/2"}]. emulated_options(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Values = [{mode, list}, {packet, 0}, {header, 0}, {active, true}], %% Shall be the reverse order of Values! Options = [active, header, packet, mode], NewValues = [{mode, binary}, {active, once}], %% Shall be the reverse order of NewValues! NewOptions = [active, mode], Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, tls_socket_options_result, [Options, Values, NewOptions, NewValues]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {?MODULE, tls_socket_options_result, [Options, Values, NewOptions, NewValues]}}, {options, ClientOpts}]), ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), {ok, Listen} = ssl:listen(0, ServerOpts), {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]), ok = ssl:setopts(Listen, [{mode, binary}]), {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]), {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]), ssl:close(Listen). accept_pool() -> [{doc,"Test having an accept pool."}]. accept_pool(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server0 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {accepters, 3}, {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server0), [Server1, Server2] = ssl_test_lib:accepters(2), Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ClientOpts} ]), Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ClientOpts} ]), Client2 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, send_recv_result_active, []}}, {options, ClientOpts} ]), ssl_test_lib:check_ok([Server0, Server1, Server2, Client0, Client1, Client2]), ssl_test_lib:close(Server0), ssl_test_lib:close(Server1), ssl_test_lib:close(Server2), ssl_test_lib:close(Client0), ssl_test_lib:close(Client1), ssl_test_lib:close(Client2). %%-------------------------------------------------------------------- reuseaddr() -> [{doc,"Test reuseaddr option"}]. reuseaddr(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, [{active, false}, {reuseaddr, true}| ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, [{active, false} | ClientOpts]}]), ssl_test_lib:close(Server), ssl_test_lib:close(Client), Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, {from, self()}, {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false}, {reuseaddr, true} | ServerOpts]}]), Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {mfa, {ssl_test_lib, send_recv_result, []}}, {options, [{active, false} | ClientOpts]}]), ssl_test_lib:check_result(Server1, ok, Client1, ok), ssl_test_lib:close(Server1), ssl_test_lib:close(Client1). %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- upgrade_result(Socket) -> ssl:setopts(Socket, [{active, true}]), ok = ssl:send(Socket, "Hello world"), %% Make sure binary is inherited from tcp socket and that we do %% not get the list default! receive {ssl, _, <<"H">>} -> receive {ssl, _, <<"ello world">>} -> ok end; {ssl, _, <<"Hello world">>} -> ok end. tls_downgrade_result(Socket, Pid) -> ok = ssl_test_lib:send_recv_result(Socket), Pid ! {self(), ready}, receive go -> ok end, case ssl:close(Socket, {self(), 10000}) of {ok, TCPSocket} -> inet:setopts(TCPSocket, [{active, true}]), gen_tcp:send(TCPSocket, "Downgraded"), receive {tcp, TCPSocket, <<"Downgraded">>} -> ok; {tcp_closed, TCPSocket} -> ct:fail("Peer timed out, downgrade aborted"), ok; Other -> {error, Other} end; {error, timeout} -> ct:fail("Timed out, downgrade aborted"), ok; Fail -> {error, Fail} end. tls_shutdown_result(Socket, server) -> ssl:send(Socket, "Hej"), ok = ssl:shutdown(Socket, write), {ok, "Hej hopp"} = ssl:recv(Socket, 8), ok; tls_shutdown_result(Socket, client) -> ssl:send(Socket, "Hej hopp"), ok = ssl:shutdown(Socket, write), {ok, "Hej"} = ssl:recv(Socket, 3), ok. tls_shutdown_write_result(Socket, server) -> ct:sleep(?SLEEP), ssl:shutdown(Socket, write); tls_shutdown_write_result(Socket, client) -> ssl:recv(Socket, 0). tls_shutdown_both_result(Socket, server) -> ct:sleep(?SLEEP), ssl:shutdown(Socket, read_write); tls_shutdown_both_result(Socket, client) -> ssl:recv(Socket, 0). tls_closed_in_active_once_loop(Socket) -> case ssl:setopts(Socket, [{active, once}]) of ok -> receive {ssl, Socket, _} -> tls_closed_in_active_once_loop(Socket); {ssl_closed, Socket} -> ok after 5000 -> no_ssl_closed_received end; {error, closed} -> ok end. drop_handshakes(Socket, Timeout) -> {ok, <> = Header} = gen_tcp:recv(Socket, 5, Timeout), {ok, <>} = gen_tcp:recv(Socket, RecLen, Timeout), case RecType of 22 -> drop_handshakes(Socket, Timeout); _ -> {ok, <
>} end. receive_msg(_) -> receive Msg -> Msg end. sockname_result(S) -> ssl:sockname(S). peername_result(S) -> ssl:peername(S). tls_socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> %% Test get/set emulated opts {ok, DefaultValues} = ssl:getopts(Socket, Options), ssl:setopts(Socket, NewValues), {ok, NewValues} = ssl:getopts(Socket, NewOptions), %% Test get/set inet opts {ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]), ssl:setopts(Socket, [{nodelay, true}]), {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]), {ok, All} = ssl:getopts(Socket, []), ct:log("All opts ~p~n", [All]), ok.