%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2012-2014. 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(eldap_connections_SUITE). -compile(export_all). -include_lib("common_test/include/ct.hrl"). %-include_lib("eldap/include/eldap.hrl"). all() -> [ {group, v4}, {group, v6} ]. init_per_group(v4, Config) -> [{listen_opts, []}, {listen_host, "localhost"}, {connect_opts, []} | Config]; init_per_group(v6, Config) -> {ok, Hostname} = inet:gethostname(), case lists:member(list_to_atom(Hostname), ct:get_config(ipv6_hosts,[])) of true -> [{listen_opts, [inet6]}, {listen_host, "::"}, {connect_opts, [{tcpopts,[inet6]}]} | Config]; false -> {skip, io_lib:format("~p is not an ipv6_host",[Hostname])} end. end_per_group(_GroupName, Config) -> Config. groups() -> [{v4, [], tests()}, {v6, [], tests()} ]. tests() -> [tcp_connection, tcp_connection_option, ssl_connection, client_side_start_tls_timeout, client_side_bind_timeout, client_side_add_timeout, client_side_search_timeout ]. init_per_suite(Config) -> HasSSL = init_ssl_certs_et_al(Config), [{has_ssl,HasSSL} | Config]. end_per_suite(_Config) -> ok. init_per_testcase(ssl_connection, Config) -> case ?config(has_ssl,Config) of true -> SSL_Port = 9999, CertFile = filename:join(?config(data_dir,Config), "certs/server/cert.pem"), KeyFile = filename:join(?config(data_dir,Config), "certs/server/key.pem"), Parent = self(), Listener = spawn_link( fun() -> case ssl:listen(SSL_Port, [{certfile, CertFile}, {keyfile, KeyFile}, {reuseaddr, true}]) of {ok,SSL_LSock} -> Parent ! {ok,self()}, (fun L() -> ct:log("ssl server waiting for connections...",[]), {ok, S} = ssl:transport_accept(SSL_LSock), ct:log("ssl:transport_accept/1 ok",[]), ok = ssl:ssl_accept(S), ct:log("ssl:ssl_accept/1 ok",[]), L() end)(); Other -> Parent ! {not_ok,Other,self()} end end), receive {ok,Listener} -> ct:log("SSL listening to port ~p (process ~p)",[SSL_Port, Listener]), [{ssl_listener,Listener}, {ssl_listen_port,SSL_Port}, {ssl_connect_opts,[]} | Config]; {no_ok,SSL_Other,Listener} -> ct:log("ssl:listen on port ~p failed: ~p",[SSL_Port,SSL_Other]), {fail, "ssl:listen/2 failed"} after 5000 -> {fail, "Waiting for ssl:listen timeout"} end; false -> {skip, "ssl not available"} end; init_per_testcase(_, Config) -> case gen_tcp:listen(0, proplists:get_value(listen_opts,Config)) of {ok,LSock} -> {ok,{_,Port}} = inet:sockname(LSock), [{listen_socket,LSock}, {listen_port,Port} | Config]; Other -> {fail, Other} end. end_per_testcase(_TestCase, Config) -> catch gen_tcp:close( proplists:get_value(listen_socket, Config) ). %%%================================================================ %%% %%% Test cases %%% %%%---------------------------------------------------------------- tcp_connection(Config) -> Host = proplists:get_value(listen_host, Config), Port = proplists:get_value(listen_port, Config), Opts = proplists:get_value(connect_opts, Config), case eldap:open([Host], [{port,Port}|Opts]) of {ok,_H} -> Sl = proplists:get_value(listen_socket, Config), case gen_tcp:accept(Sl,1000) of {ok,_S} -> ok; {error,timeout} -> ct:fail("server side accept timeout",[]) end; Other -> ct:fail("eldap:open failed: ~p",[Other]) end. %%%---------------------------------------------------------------- ssl_connection(Config) -> Host = proplists:get_value(listen_host, Config), Port = proplists:get_value(ssl_listen_port, Config), Opts = proplists:get_value(connect_opts, Config), SSLOpts = proplists:get_value(ssl_connect_opts, Config), case eldap:open([Host], [{port,Port},{ssl,true}, {timeout,5000}, {sslopts,SSLOpts}|Opts]) of {ok,_H} -> ok; Other -> ct:fail("eldap:open failed: ~p",[Other]) end. %%%---------------------------------------------------------------- client_side_add_timeout(Config) -> client_timeout( fun(H) -> eldap:add(H, "cn=Foo Bar,dc=host,dc=ericsson,dc=se", [{"objectclass", ["person"]}, {"cn", ["Foo Bar"]}, {"sn", ["Bar"]}, {"telephoneNumber", ["555-1232", "555-5432"]}]) end, Config). %%%---------------------------------------------------------------- client_side_bind_timeout(Config) -> client_timeout( fun(H) -> eldap:simple_bind(H, anon, anon) end, Config). %%%---------------------------------------------------------------- client_side_search_timeout(Config) -> client_timeout( fun(H) -> eldap:search(H, [{base,"dc=host,dc=ericsson,dc=se"}, {filter, eldap:present("objectclass")}, {scope, eldap:wholeSubtree()}]) end, Config). %%%---------------------------------------------------------------- client_side_start_tls_timeout(Config) -> client_timeout( fun(H) -> eldap:start_tls(H, []) end, Config). %%%---------------------------------------------------------------- tcp_connection_option(Config) -> Host = proplists:get_value(listen_host, Config), Port = proplists:get_value(listen_port, Config), Opts = proplists:get_value(connect_opts, Config), Sl = proplists:get_value(listen_socket, Config), %% Make an option value to test. The option must be implemented on all %% platforms that we test on. Must check what the default value is %% so we don't happen to choose that particular value. {ok,[{linger,DefaultLinger}]} = inet:getopts(Sl, [linger]), TestLinger = case DefaultLinger of {false,_} -> {true,5}; {true,_} -> {false,0} end, case catch eldap:open([Host], [{port,Port},{tcpopts,[{linger,TestLinger}]}|Opts]) of {ok,H} -> case gen_tcp:accept(Sl,1000) of {ok,_} -> case eldap:getopts(H, [{tcpopts,[linger]}]) of {ok,[{tcpopts,[{linger,ActualLinger}]}]} -> case ActualLinger of TestLinger -> ok; DefaultLinger -> ct:fail("eldap:getopts: 'linger' didn't change," " got ~p (=default) expected ~p", [ActualLinger,TestLinger]); _ -> ct:fail("eldap:getopts: bad 'linger', got ~p expected ~p", [ActualLinger,TestLinger]) end; Other -> ct:fail("eldap:getopts: bad result ~p",[Other]) end; {error,timeout} -> ct:fail("server side accept timeout",[]) end; Other -> ct:fail("eldap:open failed: ~p",[Other]) end. %%%================================================================ %%% %%% Private %%% client_timeout(Fun, Config) -> Host = proplists:get_value(listen_host, Config), Port = proplists:get_value(listen_port, Config), Opts = proplists:get_value(connect_opts, Config), T = 1000, case eldap:open([Host], [{timeout,T},{port,Port}|Opts]) of {ok,H} -> T0 = now(), {error,{gen_tcp_error,timeout}} = Fun(H), T_op = diff(T0,now()), ct:log("Time = ~p, Timeout spec = ~p",[T_op,T]), if T_op < T -> {fail, "Timeout too early"}; true -> ok end; Other -> ct:fail("eldap:open failed: ~p",[Other]) end. diff({M1,S1,U1},{M2,S2,U2}) -> ( ((M2-M1)*1000 + (S2-S1))*1000 + (U2-U1) ). %%%---------------------------------------------------------------- init_ssl_certs_et_al(Config) -> try ssl:start() of R when R==ok ; R=={error,{already_started,ssl}} -> try make_certs:all("/dev/null", filename:join(?config(data_dir,Config), "certs")) of {ok,_} -> true; Other -> ct:comment("make_certs failed"), ct:log("make_certs failed ~p", [Other]), false catch C:E -> ct:comment("make_certs crashed"), ct:log("make_certs failed ~p:~p", [C,E]), false end; _ -> false catch Error:Reason -> ct:comment("ssl failed to start"), ct:log("init_per_suite failed to start ssl Error=~p Reason=~p", [Error, Reason]), false end.