diff options
Diffstat (limited to 'lib/ssl/test/ssl_basic_SUITE.erl')
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 2075 |
1 files changed, 2075 insertions, 0 deletions
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl new file mode 100644 index 0000000000..22a0fb1ee1 --- /dev/null +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -0,0 +1,2075 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(ssl_basic_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("test_server.hrl"). +-include("test_server_line.hrl"). + +-define('24H_in_sec', 86400). +-define(TIMEOUT, 60000). +-define(EXPIRE, 10). + +-behaviour(ssl_session_cache_api). + +%% For the session cache tests +-export([init/0, terminate/1, lookup/2, update/3, + delete/2, foldl/3, select_session/2]). + +%% Test server callback functions +%%-------------------------------------------------------------------- +%% Function: init_per_suite(Config) -> Config +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Initialization before the whole suite +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + crypto:start(), + ssl:start(), + Result = + (catch make_certs:all(?config(data_dir, Config), + ?config(priv_dir, Config))), + test_server:format("Make certs ~p~n", [Result]), + ssl_test_lib:cert_options(Config). + +%%-------------------------------------------------------------------- +%% Function: end_per_suite(Config) -> _ +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after the whole suite +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ssl:stop(), + crypto:stop(). + +%%-------------------------------------------------------------------- +%% Function: init_per_testcase(TestCase, Config) -> Config +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% +%% Description: Initialization before each test case +%% +%% Note: This function is free to add any key/value pairs to the Config +%% variable, but should NOT alter/remove any existing entries. +%% Description: Initialization before each test case +%%-------------------------------------------------------------------- +init_per_testcase(session_cache_process_list, Config) -> + init_customized_session_cache(Config); + +init_per_testcase(session_cache_process_mnesia, Config) -> + mnesia:start(), + init_customized_session_cache(Config); + +init_per_testcase(reuse_session_expired, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_lifetime, ?EXPIRE), + ssl:start(), + [{watchdog, Dog} | Config]; + +init_per_testcase(_TestCase, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + [{watchdog, Dog} | Config]. + +init_customized_session_cache(Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = test_server:timetrap(?TIMEOUT), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_cb, ?MODULE), + ssl:start(), + [{watchdog, Dog} | Config]. + +%%-------------------------------------------------------------------- +%% Function: end_per_testcase(TestCase, Config) -> _ +%% Case - atom() +%% Name of the test case that is about to be run. +%% Config - [tuple()] +%% A list of key/value pairs, holding the test case configuration. +%% Description: Cleanup after each test case +%%-------------------------------------------------------------------- +end_per_testcase(session_cache_process_list, Config) -> + application:unset_env(ssl, session_cb), + end_per_testcase(default_action, Config); +end_per_testcase(session_cache_process_mnesia, Config) -> + application:unset_env(ssl, session_cb), + mnesia:stop(), + end_per_testcase(default_action, Config); +end_per_testcase(reuse_session_expired, Config) -> + application:unset_env(ssl, session_lifetime), + end_per_testcase(default_action, Config); +end_per_testcase(_TestCase, Config) -> + Dog = ?config(watchdog, Config), + case Dog of + undefined -> + ok; + _ -> + test_server:timetrap_cancel(Dog) + end. + +%%-------------------------------------------------------------------- +%% Function: all(Clause) -> TestCases +%% Clause - atom() - suite | doc +%% TestCases - [Case] +%% Case - atom() +%% Name of a test case. +%% Description: Returns a list of all test cases in this test suite +%%-------------------------------------------------------------------- +all(doc) -> + ["Test the basic ssl functionality"]; + +all(suite) -> + [app, connection_info, controlling_process, controller_dies, + peercert, connect_dist, + peername, sockname, socket_options, versions, cipher_suites, upgrade, + upgrade_with_timeout, + ipv6, ekeyfile, ecertfile, ecacertfile, eoptions, shutdown, + shutdown_write, shutdown_both, shutdown_error, ciphers, + send_close, + server_verify_peer_passive, + server_verify_peer_active, server_verify_peer_active_once, + server_verify_none_passive, server_verify_none_active, + server_verify_none_active_once, + server_verify_no_cacerts, client_verify_none_passive, + client_verify_none_active, client_verify_none_active_once + %%, session_cache_process_list, session_cache_process_mnesia + ,reuse_session, reuse_session_expired, server_does_not_want_to_reuse_session + ]. + +%% Test cases starts here. +%%-------------------------------------------------------------------- +app(doc) -> + "Test that the ssl app file is ok"; +app(suite) -> + []; +app(Config) when is_list(Config) -> + ok = test_server:app_test(ssl). + +connection_info(doc) -> + ["Test the API function ssl:connection_info/1"]; +connection_info(suite) -> + []; +connection_info(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, connection_info_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, connection_info_result, []}}, + {options, + [{ciphers,[{rsa,rc4_128,sha,no_export}]} | + ClientOpts]}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + ServerMsg = ClientMsg = {ok, {Version, {rsa,rc4_128,sha,no_export}}}, + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +connection_info_result(Socket) -> + ssl:connection_info(Socket). + +%%-------------------------------------------------------------------- + +controlling_process(doc) -> + ["Test API function controlling_process/2"]; + +controlling_process(suite) -> + []; + +controlling_process(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ClientMsg = "Hello server", + ServerMsg = "Hello client", + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + controlling_process_result, [self(), + ServerMsg]}}, + {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, + controlling_process_result, [self(), + ClientMsg]}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + receive + {ssl, _, ServerMsg} -> + receive + {ssl, _, ClientMsg} -> + ok + end; + {ssl, _, ClientMsg} -> + receive + {ssl, _, ServerMsg} -> + ok + end; + Unexpected -> + test_server:fail(Unexpected) + end, + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +controlling_process_result(Socket, Pid, Msg) -> + ok = ssl:controlling_process(Socket, Pid), + %% Make sure other side has evaluated controlling_process + %% before message is sent + test_server:sleep(100), + ssl:send(Socket, Msg), + no_result_msg. + + +controller_dies(doc) -> + ["Test that the socket is closed after controlling process dies"]; +controller_dies(suite) -> []; +controller_dies(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ClientMsg = "Hello server", + ServerMsg = "Hello client", + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + controller_dies_result, [self(), + ServerMsg]}}, + {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, + controller_dies_result, [self(), + ClientMsg]}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), + timer:sleep(200), %% so that they are connected + + process_flag(trap_exit, true), + + %% Test that clients die + exit(Client, killed), + get_close(Client, ?LINE), + + %% Test that clients die when process disappear + Server ! listen, timer:sleep(200), + Tester = self(), + Connect = fun(Pid) -> + {ok, Socket} = ssl:connect(Hostname, Port, + [{reuseaddr,true},{ssl_imp,new}]), + Pid ! {self(), connected, Socket}, + receive die_nice -> normal end + end, + Client2 = spawn_link(fun() -> Connect(Tester) end), + receive {Client2, connected, _Socket} -> Client2 ! die_nice end, + + get_close(Client2, ?LINE), + + %% Test that clients die when the controlling process have changed + Server ! listen, timer:sleep(200), + + Client3 = spawn_link(fun() -> Connect(Tester) end), + Controller = spawn_link(fun() -> receive die_nice -> normal end end), + receive + {Client3, connected, Socket} -> + ok = ssl:controlling_process(Socket, Controller), + Client3 ! die_nice + end, + + test_server:format("Wating on exit ~p~n",[Client3]), + receive {'EXIT', Client3, normal} -> ok end, + + receive %% Client3 is dead but that doesn't matter, socket should not be closed. + Unexpected -> + test_server:format("Unexpected ~p~n",[Unexpected]), + test_server:fail({line, ?LINE-1}) + after 1000 -> + ok + end, + Controller ! die_nice, + get_close(Controller, ?LINE), + + %% Test that servers die + Server ! listen, timer:sleep(200), + LastClient = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + controller_dies_result, [self(), + ClientMsg]}}, + {options, [{reuseaddr,true}|ClientOpts]}]), + timer:sleep(200), %% so that they are connected + + exit(Server, killed), + get_close(Server, ?LINE), + process_flag(trap_exit, false), + ssl_test_lib:close(LastClient). + +controller_dies_result(_Socket, _Pid, _Msg) -> + receive Result -> Result end. + +get_close(Pid, Where) -> + receive + {'EXIT', Pid, _Reason} -> + receive + {_, {ssl_closed, Socket}} -> + test_server:format("Socket closed ~p~n",[Socket]); + Unexpected -> + test_server:format("Unexpected ~p~n",[Unexpected]), + test_server:fail({line, ?LINE-1}) + after 5000 -> + test_server:fail({timeout, {line, ?LINE, Where}}) + end; + Unexpected -> + test_server:format("Unexpected ~p~n",[Unexpected]), + test_server:fail({line, ?LINE-1}) + after 5000 -> + test_server:fail({timeout, {line, ?LINE, Where}}) + end. + +%%-------------------------------------------------------------------- +peercert(doc) -> + [""]; + +peercert(suite) -> + []; + +peercert(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, ClientOpts}]), + + CertFile = proplists:get_value(certfile, ServerOpts), + {ok, [{cert, BinCert, _}]} = public_key:pem_to_der(CertFile), + {ok, ErlCert} = public_key:pkix_decode_cert(BinCert, otp), + + ServerMsg = {{error, no_peercert}, {error, no_peercert}}, + ClientMsg = {{ok, BinCert}, {ok, ErlCert}}, + + test_server:format("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). + +peercert_result(Socket) -> + Result1 = ssl:peercert(Socket), + Result2 = ssl:peercert(Socket, [ssl]), + {Result1, Result2}. + +%%-------------------------------------------------------------------- +connect_dist(doc) -> + ["Test a simple connect as is used by distribution"]; + +connect_dist(suite) -> + []; + +connect_dist(Config) when is_list(Config) -> + ClientOpts0 = ?config(client_kc_opts, Config), + ClientOpts = [{ssl_imp, new},{active, false}, {packet,4}|ClientOpts0], + ServerOpts0 = ?config(server_kc_opts, Config), + ServerOpts = [{ssl_imp, new},{active, false}, {packet,4}|ServerOpts0], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, connect_dist_s, []}}, + {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, connect_dist_c, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +connect_dist_s(S) -> + Msg = term_to_binary({erlang,term}), + ok = ssl:send(S, Msg). + +connect_dist_c(S) -> + Test = binary_to_list(term_to_binary({erlang,term})), + {ok, Test} = ssl:recv(S, 0, 10000), + ok. + + +%%-------------------------------------------------------------------- +peername(doc) -> + ["Test API function peername/1"]; + +peername(suite) -> + []; + +peername(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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), + ClientIp = ssl_test_lib:node_to_hostip(ClientNode), + ServerMsg = {ok, {ClientIp, ClientPort}}, + ClientMsg = {ok, {ServerIp, Port}}, + + test_server:format("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). + +peername_result(S) -> + ssl:peername(S). + +%%-------------------------------------------------------------------- +sockname(doc) -> + ["Test API function sockname/1"]; + +sockname(suite) -> + []; + +sockname(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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), + ClientIp = ssl_test_lib:node_to_hostip(ClientNode), + ServerMsg = {ok, {ServerIp, Port}}, + ClientMsg = {ok, {ClientIp, ClientPort}}, + + test_server:format("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_result(S) -> + ssl:sockname(S). + +%%-------------------------------------------------------------------- +cipher_suites(doc) -> + ["Test API function cipher_suites/0"]; + +cipher_suites(suite) -> + []; + +cipher_suites(Config) when is_list(Config) -> + MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha,no_export}, + [_|_] = Suites = ssl:cipher_suites(), + true = lists:member(MandatoryCipherSuite, Suites). +%%-------------------------------------------------------------------- +socket_options(doc) -> + ["Test API function getopts/2 and setopts/2"]; + +socket_options(suite) -> + []; + +socket_options(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, 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, socket_options_result, + [Options, Values, NewOptions, NewValues]}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +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]), + ok. + +%%-------------------------------------------------------------------- +versions(doc) -> + ["Test API function versions/0"]; + +versions(suite) -> + []; + +versions(Config) when is_list(Config) -> + [_|_] = Versions = ssl:versions(), + test_server:format("~p~n", [Versions]). + +%%-------------------------------------------------------------------- +send_recv(doc) -> + [""]; + +send_recv(suite) -> + []; + +send_recv(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, send_recv_result, []}}, + {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, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + test_server:format("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). + +send_close(doc) -> + [""]; + +send_close(suite) -> + []; + +send_close(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, 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}, {reuseaddr, true}]]), + {ok, SslS} = rpc:call(ClientNode, ssl, connect, + [TcpS,[{active, false}|ClientOpts]]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), self(), Server]), + ok = ssl:send(SslS, "HejHopp"), + {ok,<<"Hejhopp">>} = ssl:recv(SslS, 7), + gen_tcp:close(TcpS), + {error, _} = ssl:send(SslS, "HejHopp"), + ssl_test_lib:close(Server). + +%%-------------------------------------------------------------------- +upgrade(doc) -> + ["Test that you can upgrade an tcp connection to an ssl connection"]; + +upgrade(suite) -> + []; + +upgrade(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, TcpOpts}, + {ssl_options, 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, ClientOpts}]), + + test_server:format("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). + +upgrade_result(Socket) -> + ok = ssl:send(Socket, "Hejhopp"), + %% Make sure binary is inherited from tcp socket and that we do + %% not get the list default! + receive + {ssl, _, <<"Hejhopp">>} -> + ok + end. + +%%-------------------------------------------------------------------- +upgrade_with_timeout(doc) -> + ["Test ssl_accept/3"]; + +upgrade_with_timeout(suite) -> + []; + +upgrade_with_timeout(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, TcpOpts}, + {ssl_options, 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, ClientOpts}]), + + test_server:format("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). +%%-------------------------------------------------------------------- +ipv6(doc) -> + ["Test ipv6."]; +ipv6(suite) -> + []; +ipv6(Config) when is_list(Config) -> + {ok, Hostname0} = inet:gethostname(), + + case lists:member(list_to_atom(Hostname0), ?config(ipv6_hosts, Config)) of + true -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = + ssl_test_lib:run_where(Config, ipv6), + Server = ssl_test_lib:start_server([{node, ServerNode}, + {port, 0}, {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, + [inet6, {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, send_recv_result, []}}, + {options, + [inet6, {active, false} | ClientOpts]}]), + + test_server:format("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); + false -> + {skip, "Host does not support IPv6"} + end. + +%%-------------------------------------------------------------------- + +ekeyfile(doc) -> + ["Test what happens with an invalid key file"]; + +ekeyfile(suite) -> + []; + +ekeyfile(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + BadOpts = ?config(server_bad_key, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Port = ssl_test_lib:inet_port(ServerNode), + + Server = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, BadOpts}]), + Client = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, {error, ekeyfile}, Client, + {error, closed}). + +%%-------------------------------------------------------------------- + +ecertfile(doc) -> + ["Test what happens with an invalid cert file"]; + +ecertfile(suite) -> + []; + +ecertfile(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerBadOpts = ?config(server_bad_cert, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Port = ssl_test_lib:inet_port(ServerNode), + + Server0 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, ServerBadOpts}]), + Client0 = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server0, {error, ecertfile}, Client0, + {error, closed}). + + +%%-------------------------------------------------------------------- +ecacertfile(doc) -> + ["Test what happens with an invalid cacert file"]; + +ecacertfile(suite) -> + []; + +ecacertfile(Config) when is_list(Config) -> + ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)], + ServerBadOpts = [{reuseaddr, true}|?config(server_bad_ca, Config)], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Port = ssl_test_lib:inet_port(ServerNode), + + Server0 = + ssl_test_lib:start_server_error([{node, ServerNode}, + {port, Port}, {from, self()}, + {options, ServerBadOpts}]), + Client0 = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server0, {error, ecacertfile}, + Client0, {error, closed}), + + File0 = proplists:get_value(cacertfile, ServerBadOpts), + File = File0 ++ "do_not_exit.pem", + ServerBadOpts1 = [{cacertfile, File}|proplists:delete(cacertfile, ServerBadOpts)], + + Server1 = + ssl_test_lib:start_server_error([{node, ServerNode}, + {port, Port}, {from, self()}, + {options, ServerBadOpts1}]), + Client1 = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server1, {error, ecacertfile}, + Client1, {error, closed}), + ok. + + + +%%-------------------------------------------------------------------- +eoptions(doc) -> + ["Test what happens when we give invalid options"]; + +eoptions(suite) -> + []; + +eoptions(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Port = ssl_test_lib:inet_port(ServerNode), + + %% Emulated opts + Server0 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{active, trice} | ServerOpts]}]), + Client0 = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, + {options, [{active, trice} | ClientOpts]}]), + ssl_test_lib:check_result(Server0, {error, {eoptions, {active,trice}}}, + Client0, {error, {eoptions, {active,trice}}}), + + test_server:sleep(500), + + Server1 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{header, a} | ServerOpts]}]), + Client1 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{header, a} | ClientOpts]}]), + ssl_test_lib:check_result(Server1, {error, {eoptions, {header, a}}}, + Client1, {error, {eoptions, {header, a}}}), + + test_server:sleep(500), + + + Server2 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{mode, a} | ServerOpts]}]), + + Client2 = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, + {options, [{mode, a} | ClientOpts]}]), + ssl_test_lib:check_result(Server2, {error, {eoptions, {mode, a}}}, + Client2, {error, {eoptions, {mode, a}}}), + + + test_server:sleep(500), + + Server3 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{packet, 8.0} | ServerOpts]}]), + Client3 = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, + {options, [{packet, 8.0} | ClientOpts]}]), + ssl_test_lib:check_result(Server3, {error, {eoptions, {packet, 8.0}}}, + Client3, {error, {eoptions, {packet, 8.0}}}), + + test_server:sleep(500), + + %% ssl + Server4 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{verify, 4} | ServerOpts]}]), + Client4 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{verify, 4} | ClientOpts]}]), + ssl_test_lib:check_result(Server4, {error, {eoptions, {verify, 4}}}, + Client4, {error, {eoptions, {verify, 4}}}), + + test_server:sleep(500), + + Server5 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{depth, four} | ServerOpts]}]), + Client5 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{depth, four} | ClientOpts]}]), + ssl_test_lib:check_result(Server5, {error, {eoptions, {depth, four}}}, + Client5, {error, {eoptions, {depth, four}}}), + + test_server:sleep(500), + + Server6 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{cacertfile, ""} | ServerOpts]}]), + Client6 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{cacertfile, ""} | ClientOpts]}]), + ssl_test_lib:check_result(Server6, {error, {eoptions, {cacertfile, ""}}}, + Client6, {error, {eoptions, {cacertfile, ""}}}), + + + test_server:sleep(500), + + Server7 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{certfile, 'cert.pem'} | ServerOpts]}]), + Client7 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{certfile, 'cert.pem'} | ClientOpts]}]), + ssl_test_lib:check_result(Server7, + {error, {eoptions, {certfile, 'cert.pem'}}}, + Client7, {error, {eoptions, {certfile, 'cert.pem'}}}), + + test_server:sleep(500), + + Server8 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{keyfile,'key.pem' } | ServerOpts]}]), + Client8 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, {options, [{keyfile, 'key.pem'} + | ClientOpts]}]), + ssl_test_lib:check_result(Server8, + {error, {eoptions, {keyfile, 'key.pem'}}}, + Client8, {error, {eoptions, {keyfile, 'key.pem'}}}), + + test_server:sleep(500), + + Server9 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{key, 'key.pem' } | ServerOpts]}]), + Client9 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, {options, [{key, 'key.pem'} + | ClientOpts]}]), + ssl_test_lib:check_result(Server9, {error, {eoptions, {key, 'key.pem'}}}, + Client9, {error, {eoptions, {key, 'key.pem'}}}), + + + test_server:sleep(500), + + Server10 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{password, foo} | ServerOpts]}]), + Client10 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{password, foo} | ClientOpts]}]), + ssl_test_lib:check_result(Server10, {error, {eoptions, {password, foo}}}, + Client10, {error, {eoptions, {password, foo}}}), + + test_server:sleep(500), + + %% Misc + Server11 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{ssl_imp, cool} | ServerOpts]}]), + Client11 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{ssl_imp, cool} | ClientOpts]}]), + ssl_test_lib:check_result(Server11, {error, {eoptions, {ssl_imp, cool}}}, + Client11, {error, {eoptions, {ssl_imp, cool}}}), + + + test_server:sleep(500), + + Server12 = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + {from, self()}, + {options, [{debug, cool} | ServerOpts]}]), + Client12 = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{debug, cool} | ClientOpts]}]), + ssl_test_lib:check_result(Server12, {error, {eoptions, {debug, cool}}}, + Client12, {error, {eoptions, {debug, cool}}}). + +%%-------------------------------------------------------------------- +shutdown(doc) -> + [""]; + +shutdown(suite) -> + []; + +shutdown(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, 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, 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). + +shutdown_result(Socket, server) -> + ssl:send(Socket, "Hej"), + ssl:shutdown(Socket, write), + {ok, "Hej hopp"} = ssl:recv(Socket, 8), + ok; + +shutdown_result(Socket, client) -> + {ok, "Hej"} = ssl:recv(Socket, 3), + ssl:send(Socket, "Hej hopp"), + ssl:shutdown(Socket, write), + ok. + +%%-------------------------------------------------------------------- +shutdown_write(doc) -> + [""]; + +shutdown_write(suite) -> + []; + +shutdown_write(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, 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, shutdown_write_result, [client]}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, {error, closed}). + +shutdown_write_result(Socket, server) -> + test_server:sleep(500), + ssl:shutdown(Socket, write); +shutdown_write_result(Socket, client) -> + ssl:recv(Socket, 0). + +%%-------------------------------------------------------------------- +shutdown_both(doc) -> + [""]; + +shutdown_both(suite) -> + []; + +shutdown_both(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, 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, shutdown_both_result, [client]}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, {error, closed}). + +shutdown_both_result(Socket, server) -> + test_server:sleep(500), + ssl:shutdown(Socket, read_write); +shutdown_both_result(Socket, client) -> + ssl:recv(Socket, 0). + +%%-------------------------------------------------------------------- +shutdown_error(doc) -> + [""]; + +shutdown_error(suite) -> + []; + +shutdown_error(Config) when is_list(Config) -> + ServerOpts = ?config(server_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). + +%%-------------------------------------------------------------------- +ciphers(doc) -> + [""]; + +ciphers(suite) -> + []; + +ciphers(Config) when is_list(Config) -> + Version = + ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Ciphers = ssl:cipher_suites(), + Result = lists:map(fun(Cipher) -> + cipher(Cipher, Version, Config) end, + Ciphers), + case lists:flatten(Result) of + [] -> + ok; + Error -> + test_server:format("Cipher suite errors: ~p~n", [Error]), + test_server:fail(cipher_suite_failed_see_test_case_log) + end. + +cipher(CipherSuite, Version, Config) -> + process_flag(trap_exit, true), + test_server:format("Testing CipherSuite ~p~n", [CipherSuite]), + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, connection_info_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, connection_info_result, []}}, + {options, + [{ciphers,[CipherSuite]} | + ClientOpts]}]), + + ServerMsg = ClientMsg = {ok, {Version, CipherSuite}}, + + Result = ssl_test_lib:wait_for_result(Server, ServerMsg, + Client, ClientMsg), + ssl_test_lib:close(Server), + receive + {'EXIT', Server, normal} -> + ok + end, + ssl_test_lib:close(Client), + receive + {'EXIT', Client, normal} -> + ok + end, + process_flag(trap_exit, false), + case Result of + ok -> + []; + Error -> + [{CipherSuite, Error}] + end. + +%%-------------------------------------------------------------------- +reuse_session(doc) -> + ["Test reuse of sessions (short handshake)"]; + +reuse_session(suite) -> + []; + +reuse_session(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, session_info_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + Server ! listen, + + %% Make sure session is registered + test_server:sleep(500), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + ok; + {Client1, Other} -> + test_server:format("Expected: ~p, Unexpected: ~p~n", + [SessionInfo, Other]), + test_server:fail(session_not_reused) + end, + + Server ! listen, + + Client2 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, [{reuse_sessions, false} + | ClientOpts]}]), + receive + {Client2, SessionInfo} -> + test_server:fail( + session_reused_when_session_reuse_disabled_by_client); + {Client2, _} -> + ok + end, + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + ssl_test_lib:close(Client2), + + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, session_info_result, []}}, + {options, [{reuse_sessions, false} | ServerOpts]}]), + + Port1 = ssl_test_lib:inet_port(Server1), + Client3 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + + SessionInfo1 = + receive + {Server1, Info1} -> + Info1 + end, + + Server1 ! listen, + + %% Make sure session is registered + test_server:sleep(500), + + Client4 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + + receive + {Client4, SessionInfo1} -> + test_server:fail( + session_reused_when_session_reuse_disabled_by_server); + {Client4, _Other} -> + ok + end, + + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client3), + ssl_test_lib:close(Client4), + process_flag(trap_exit, false). + + +session_info_result(Socket) -> + ssl:session_info(Socket). + +%%-------------------------------------------------------------------- +reuse_session_expired(doc) -> + ["Test sessions is not reused when it has expired"]; + +reuse_session_expired(suite) -> + []; + +reuse_session_expired(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, session_info_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + Server ! listen, + + %% Make sure session is registered + test_server:sleep(500), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + ok; + {Client1, Other} -> + test_server:format("Expected: ~p, Unexpected: ~p~n", + [SessionInfo, Other]), + test_server:fail(session_not_reused) + end, + + Server ! listen, + + %% Make sure session is unregistered due to expiration + test_server:sleep((?EXPIRE+1) * 1000), + + Client2 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client2, SessionInfo} -> + test_server:fail(session_reused_when_session_expired); + {Client2, _} -> + ok + end, + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + ssl_test_lib:close(Client2), + process_flag(trap_exit, false). +%%-------------------------------------------------------------------- +server_does_not_want_to_reuse_session(doc) -> + ["Test reuse of sessions (short handshake)"]; + +server_does_not_want_to_reuse_session(suite) -> + []; + +server_does_not_want_to_reuse_session(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, session_info_result, []}}, + {options, [{reuse_session, fun(_,_,_,_) -> + false + end} | + ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + Server ! listen, + + %% Make sure session is registered + test_server:sleep(500), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + test_server:fail(session_reused_when_server_does_not_want_to); + {Client1, _Other} -> + ok + end, + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + process_flag(trap_exit, false). + +%%-------------------------------------------------------------------- + +server_verify_peer_passive(doc) -> + ["Test server option verify_peer"]; + +server_verify_peer_passive(suite) -> + []; + +server_verify_peer_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_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, send_recv_result, []}}, + {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, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +server_verify_peer_active(doc) -> + ["Test server option verify_peer"]; + +server_verify_peer_active(suite) -> + []; + +server_verify_peer_active(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_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, send_recv_result_active, []}}, + {options, [{active, true}, {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, send_recv_result_active, []}}, + {options, [{active, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +server_verify_peer_active_once(doc) -> + ["Test server option verify_peer"]; + +server_verify_peer_active_once(suite) -> + []; + +server_verify_peer_active_once(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_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, send_recv_result_active_once, []}}, + {options, [{active, once}, {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, send_recv_result_active_once, []}}, + {options, [{active, once} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +server_verify_none_passive(doc) -> + ["Test server option verify_none"]; + +server_verify_none_passive(suite) -> + []; + +server_verify_none_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, send_recv_result, []}}, + {options, [{active, false}, {verify, verify_none} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +server_verify_none_active(doc) -> + ["Test server option verify_none"]; + +server_verify_none_active(suite) -> + []; + +server_verify_none_active(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, send_recv_result_active, []}}, + {options, [{active, true}, {verify, verify_none} | + ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_active, []}}, + {options, [{active, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +server_verify_none_active_once(doc) -> + ["Test server option verify_none"]; + +server_verify_none_active_once(suite) -> + []; + +server_verify_none_active_once(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, send_recv_result_active_once, []}}, + {options, [{active, once}, {verify, verify_none} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_active_once, []}}, + {options, [{active, once} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +server_verify_no_cacerts(doc) -> + ["Test server must have cacerts if it wants to verify client"]; + +server_verify_no_cacerts(suite) -> + []; + +server_verify_no_cacerts(Config) when is_list(Config) -> + ServerOpts = ServerOpts = ?config(server_opts, Config), + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{verify, verify_peer} + | ServerOpts]}]), + + ssl_test_lib:check_result(Server, {error, {eoptions, {cacertfile, ""}}}). + +%%-------------------------------------------------------------------- + +client_verify_none_passive(doc) -> + ["Test client option verify_none"]; + +client_verify_none_passive(suite) -> + []; + +client_verify_none_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, send_recv_result, []}}, + {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, send_recv_result, []}}, + {options, [{active, false}, + {verify, verify_none} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +client_verify_none_active(doc) -> + ["Test client option verify_none"]; + +client_verify_none_active(suite) -> + []; + +client_verify_none_active(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, + send_recv_result_active, []}}, + {options, [{active, true} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active, []}}, + {options, [{active, true}, + {verify, verify_none} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +client_verify_none_active_once(doc) -> + ["Test client option verify_none"]; + +client_verify_none_active_once(suite) -> + []; + +client_verify_none_active_once(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, send_recv_result_active_once, []}}, + {options, [{active, once} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + %% TODO: send message to test process to make sure + %% verifyfun has beeen run as it has the same behavior as + %% the default fun + VerifyFun = fun([{bad_cert, unknown_ca}]) -> + true; + (_) -> + false + end, + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_active_once, + []}}, + {options, [{active, once}, + {verify, verify_none}, + {verify_fun, VerifyFun} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +send_recv_result(Socket) -> + ssl:send(Socket, "Hejhopp"), + test_server:sleep(100), + {ok,"Hejhopp"} = ssl:recv(Socket, 7), + ok. + +send_recv_result_active(Socket) -> + ssl:send(Socket, "Hejhopp"), + test_server:sleep(100), + receive + {ssl, Socket, "Hejhopp"} -> + ok + end. + +send_recv_result_active_once(Socket) -> + ssl:send(Socket, "Hejhopp"), + test_server:sleep(100), + receive + {ssl, Socket, "Hejhopp"} -> + ok + end. + +session_cache_process_list(doc) -> + ["Test reuse of sessions (short handshake)"]; + +session_cache_process_list(suite) -> + []; +session_cache_process_list(Config) when is_list(Config) -> + session_cache_process(list,Config). + +session_cache_process_mnesia(doc) -> + ["Test reuse of sessions (short handshake)"]; + +session_cache_process_mnesia(suite) -> + []; +session_cache_process_mnesia(Config) when is_list(Config) -> + session_cache_process(mnesia,Config). + +session_cache_process(Type,Config) when is_list(Config) -> + process_flag(trap_exit, true), + setup_session_cb(Type), + + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, session_info_result, []}}, + {options, + [{session_cache_cb, ?MODULE}| + ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + Server ! listen, + + %% Make sure session is registered + test_server:sleep(500), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + ok; + {Client1, Other} -> + test_server:format("Expected: ~p, Unexpected: ~p~n", + [SessionInfo, Other]), + test_server:fail(session_not_reused) + end, + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, session_info_result, []}}, + {options, + [{reuse_sessions, false} | ServerOpts]}]), + Port1 = ssl_test_lib:inet_port(Server1), + + Client3 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + + SessionInfo1 = + receive + {Server1, Info1} -> + Info1 + end, + + Server1 ! listen, + + %% Make sure session is registered + test_server:sleep(500), + + Client4 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {?MODULE, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + + receive + {Client4, SessionInfo1} -> + test_server:fail( + session_reused_when_session_reuse_disabled_by_server); + {Client4, _Other} -> + ok + end, + + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client3), + ssl_test_lib:close(Client4), + process_flag(trap_exit, false). + +setup_session_cb(Type) -> + ssl_test = ets:new(ssl_test,[named_table, set,public]), + ets:insert(ssl_test, {type,Type}). + +session_cb() -> + [{type,Type}] = ets:lookup(ssl_test, type), + Type. + +init() -> + io:format("~p~n",[?LINE]), + case session_cb() of + list -> + spawn(fun() -> session_loop([]) end); + mnesia -> + mnesia:start(), + {atomic,ok} = mnesia:create_table(sess_cache, []) + end. + +terminate(Cache) -> + io:format("~p~n",[?LINE]), + case session_cb() of + list -> + Cache ! terminate; + mnesia -> + {atomic,ok} = mnesia:delete_table(sess_cache, []) + end. + +lookup(Cache, Key) -> + io:format("~p~n",[?LINE]), + case session_cb() of + list -> + Cache ! {self(), lookup, Key}, + receive {Cache, Res} -> Res end; + mnesia -> + case mnesia:transaction(fun() -> + mnesia:read(sess_cache, + Key, read) + end) of + {atomic, [Session]} -> Session; + _ -> undefined + end + end. + +update(Cache, Key, Value) -> + io:format("~p~n",[?LINE]), + case session_cb() of + list -> + Cache ! {update, Key, Value}; + mnesia -> + {atomic, ok} = + mnesia:transaction(fun() -> + mnesia:write(sess_cache, + Key, Value) + end) + end. + +delete(Cache, Key) -> + io:format("~p~n",[?LINE]), + case session_cb() of + list -> + Cache ! {delete, Key}; + mnesia -> + {atomic, ok} = + mnesia:transaction(fun() -> + mnesia:delete(sess_cache, Key) + end) + end. + +foldl(Fun, Acc, Cache) -> + io:format("~p~n",[?LINE]), + case session_cb() of + list -> + Cache ! {self(),foldl,Fun,Acc}, + receive {Cache, Res} -> Res end; + mnesia -> + Foldl = fun() -> + mnesia:foldl(Fun, Acc, sess_cache) + end, + {atomic, Res} = mnesia:transaction(Foldl), + Res + end. + +select_session(Cache, PartialKey) -> + io:format("~p~n",[?LINE]), + case session_cb() of + list -> + Cache ! {self(),select_session, PartialKey}, + receive {Cache, Res} -> Res end; + mnesia -> + Sel = fun() -> + mnesia:select(Cache, + [{{{PartialKey,'$1'}, '$2'}, + [],['$$']}]) + end, + {atomic, Res} = mnesia:transaction(Sel), + Res + end. + +session_loop(Sess) -> + receive + terminate -> + ok; + {Pid, lookup, Key} -> + case lists:keysearch(Key,1,Sess) of + {value, {Key,Value}} -> + Pid ! {self(), Value}; + _ -> + Pid ! {self(), undefined} + end, + session_loop(Sess); + {update, Key, Value} -> + session_loop([{Key,Value}|Sess]); + {delete, Key} -> + session_loop(lists:keydelete(Key,1,Sess)); + {Pid,foldl,Fun,Acc} -> + Res = lists:foldl(Fun, Acc,Sess), + Pid ! {self(), Res}, + session_loop(Sess); + {Pid,select_session,PKey} -> + Sel = fun({{Head, _},Session}, Acc) when Head =:= PKey -> + [Session|Acc]; + (_,Acc) -> + Acc + end, + Pid ! {self(), lists:foldl(Sel, [], Sess)}, + session_loop(Sess) + end. + |