1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2017-2020. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(ssl_dist_test_lib). 22 23-behaviour(ct_suite). 24 25-include_lib("common_test/include/ct.hrl"). 26-include_lib("public_key/include/public_key.hrl"). 27-include("ssl_dist_test_lib.hrl"). 28 29-export([tstsrvr_format/2, send_to_tstcntrl/1]). 30-export([apply_on_ssl_node/4, apply_on_ssl_node/2]). 31-export([stop_ssl_node/1, start_ssl_node/2]). 32%% 33-export([cnct2tstsrvr/1]). 34 35-define(AWAIT_SSL_NODE_UP_TIMEOUT, 30000). 36 37 38 39%% ssl_node side api 40%% 41 42tstsrvr_format(Fmt, ArgList) -> 43 send_to_tstsrvr({format, Fmt, ArgList}). 44 45send_to_tstcntrl(Message) -> 46 send_to_tstsrvr({message, Message}). 47 48 49%% 50%% test_server side api 51%% 52 53apply_on_ssl_node( 54 #node_handle{connection_handler = Hndlr} = Node, 55 M, F, A) when is_atom(M), is_atom(F), is_list(A) -> 56 Ref = erlang:monitor(process, Hndlr), 57 apply_on_ssl_node(Node, Ref, {apply, self(), Ref, M, F, A}). 58 59apply_on_ssl_node( 60 #node_handle{connection_handler = Hndlr} = Node, 61 Fun) when is_function(Fun, 0) -> 62 Ref = erlang:monitor(process, Hndlr), 63 apply_on_ssl_node(Node, Ref, {apply, self(), Ref, Fun}). 64 65apply_on_ssl_node(Node, Ref, Msg) -> 66 send_to_ssl_node(Node, Msg), 67 receive 68 {'DOWN', Ref, process, Hndlr, Reason} -> 69 exit({handler_died, Hndlr, Reason}); 70 {Ref, Result} -> 71 Result 72 end. 73 74stop_ssl_node(#node_handle{connection_handler = Handler, 75 socket = Socket, 76 name = Name}) -> 77 test_server:format("Trying to stop ssl node ~s.~n", [Name]), 78 Mon = erlang:monitor(process, Handler), 79 unlink(Handler), 80 case gen_tcp:send(Socket, term_to_binary(stop)) of 81 ok -> 82 receive 83 {'DOWN', Mon, process, Handler, Reason} -> 84 case Reason of 85 normal -> 86 ok; 87 _ -> 88 ct:pal( 89 "stop_ssl_node/1 ~s Down ~p ~n", 90 [Name,Reason]) 91 end 92 end; 93 Error -> 94 erlang:demonitor(Mon, [flush]), 95 ct:pal("stop_ssl_node/1 ~s Warning ~p ~n", [Name,Error]) 96 end. 97 98start_ssl_node(Name, Args) -> 99 {ok, LSock} = gen_tcp:listen(0, 100 [binary, {packet, 4}, {active, false}]), 101 {ok, ListenPort} = inet:port(LSock), 102 CmdLine = mk_node_cmdline(ListenPort, Name, Args), 103 test_server:format("Attempting to start ssl node ~ts: ~ts~n", [Name, CmdLine]), 104 case open_port({spawn, CmdLine}, []) of 105 Port when is_port(Port) -> 106 unlink(Port), 107 catch erlang:port_close(Port), 108 case await_ssl_node_up(Name, LSock) of 109 #node_handle{} = NodeHandle -> 110 test_server:format("Ssl node ~s started.~n", [Name]), 111 NodeName = list_to_atom(Name ++ "@" ++ host_name()), 112 NodeHandle#node_handle{nodename = NodeName}; 113 Error -> 114 exit({failed_to_start_node, Name, Error}) 115 end; 116 Error -> 117 exit({failed_to_start_node, Name, Error}) 118 end. 119 120host_name() -> 121 [_, Host] = string:split(atom_to_list(node()), "@"), 122 %% [$@ | Host] = lists:dropwhile(fun ($@) -> false; (_) -> true end, 123 %% atom_to_list(node())), 124 Host. 125 126mk_node_cmdline(ListenPort, Name, Args) -> 127 Static = "-detached -noinput", 128 Pa = filename:dirname(code:which(?MODULE)), 129 Prog = case catch init:get_argument(progname) of 130 {ok,[[P]]} -> P; 131 _ -> exit(no_progname_argument_found) 132 end, 133 NameSw = case net_kernel:longnames() of 134 false -> "-sname "; 135 _ -> "-name " 136 end, 137 {ok, Pwd} = file:get_cwd(), 138 "\"" ++ Prog ++ "\" " 139 ++ Static ++ " " 140 ++ NameSw ++ " " ++ Name ++ " " 141 ++ "-pa " ++ Pa ++ " " 142 ++ "-run application start crypto -run application start public_key " 143 ++ "-eval 'net_kernel:verbose(1)' " 144 ++ "-run " ++ atom_to_list(?MODULE) ++ " cnct2tstsrvr " 145 ++ host_name() ++ " " 146 ++ integer_to_list(ListenPort) ++ " " 147 ++ Args ++ " " 148 ++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ Name ++ " " 149 ++ "-kernel inet_dist_connect_options \"[{recbuf,12582912},{sndbuf,12582912},{high_watermark,8388608},{low_watermark,4194304}]\" " 150 ++ "-kernel inet_dist_listen_options \"[{recbuf,12582912},{sndbuf,12582912},{high_watermark,8388608},{low_watermark,4194304}]\" " 151 ++ "-kernel error_logger \"{file,\\\"" ++ Pwd ++ "/error_log." ++ Name ++ "\\\"}\" " 152 ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()). 153 154%% 155%% Connection handler test_server side 156%% 157 158await_ssl_node_up(Name, LSock) -> 159 case gen_tcp:accept(LSock, ?AWAIT_SSL_NODE_UP_TIMEOUT) of 160 {ok, Socket} -> 161 gen_tcp:close(LSock), 162 case gen_tcp:recv(Socket, 0) of 163 {ok, Bin} -> 164 check_ssl_node_up(Socket, Name, Bin); 165 {error, closed} -> 166 gen_tcp:close(Socket), 167 exit({lost_connection_with_ssl_node_before_up, Name}) 168 end; 169 {error, Error} -> 170 gen_tcp:close(LSock), 171 test_server:format("Accept failed for ssl node ~s: ~p~n", [Name,Error]), 172 exit({accept_failed, Error}) 173 end. 174 175check_ssl_node_up(Socket, Name, Bin) -> 176 case catch binary_to_term(Bin) of 177 {'EXIT', _} -> 178 gen_tcp:close(Socket), 179 exit({bad_data_received_from_ssl_node, Name, Bin}); 180 {ssl_node_up, NodeName} -> 181 case list_to_atom(Name++"@"++host_name()) of 182 NodeName -> 183 Parent = self(), 184 Go = make_ref(), 185 %% Spawn connection handler on test server side 186 Pid = spawn( 187 fun () -> 188 link(group_leader()), 189 receive Go -> ok end, 190 process_flag(trap_exit, true), 191 tstsrvr_con_loop(Name, Socket, Parent) 192 end), 193 ok = gen_tcp:controlling_process(Socket, Pid), 194 Pid ! Go, 195 #node_handle{connection_handler = Pid, 196 socket = Socket, 197 name = Name}; 198 _ -> 199 exit({unexpected_ssl_node_connected, NodeName}) 200 end; 201 Msg -> 202 exit({unexpected_msg_instead_of_ssl_node_up, Name, Msg}) 203 end. 204 205send_to_ssl_node(#node_handle{connection_handler = Hndlr}, Term) -> 206 Hndlr ! {relay_to_ssl_node, term_to_binary(Term)}, 207 ok. 208 209tstsrvr_con_loop(Name, Socket, Parent) -> 210 ok = inet:setopts(Socket,[{active,once}]), 211 receive 212 {relay_to_ssl_node, Data} when is_binary(Data) -> 213 case gen_tcp:send(Socket, Data) of 214 ok -> 215 ok; 216 _Error -> 217 gen_tcp:close(Socket), 218 exit({failed_to_relay_data_to_ssl_node, Name, Data}) 219 end; 220 {tcp, Socket, Bin} -> 221 try binary_to_term(Bin) of 222 {format, FmtStr, ArgList} -> 223 test_server:format(FmtStr, ArgList); 224 {message, Msg} -> 225 test_server:format("Got message ~p", [Msg]), 226 Parent ! Msg; 227 {apply_res, To, Ref, Res} -> 228 To ! {Ref, Res}; 229 bye -> 230 {error, closed} = gen_tcp:recv(Socket, 0), 231 test_server:format("Ssl node ~s stopped.~n", [Name]), 232 gen_tcp:close(Socket), 233 exit(normal); 234 Unknown -> 235 exit({unexpected_message_from_ssl_node, Name, Unknown}) 236 catch 237 error : _ -> 238 gen_tcp:close(Socket), 239 exit({bad_data_received_from_ssl_node, Name, Bin}) 240 end; 241 {tcp_closed, Socket} -> 242 gen_tcp:close(Socket), 243 exit({lost_connection_with_ssl_node, Name}); 244 {'EXIT', Parent, Reason} -> 245 exit({'EXIT', parent, Reason}); 246 Unknown -> 247 exit({unknown, Unknown}) 248 end, 249 tstsrvr_con_loop(Name, Socket, Parent). 250 251%% 252%% Connection handler ssl_node side 253%% 254 255% cnct2tstsrvr() is called via command line arg -run ... 256cnct2tstsrvr([Host, Port]) when is_list(Host), is_list(Port) -> 257 %% Spawn connection handler on ssl node side 258 ConnHandler 259 = spawn(fun () -> 260 case catch gen_tcp:connect(Host, 261 list_to_integer(Port), 262 [binary, 263 {packet, 4}, 264 {active, false}]) of 265 {ok, Socket} -> 266 notify_ssl_node_up(Socket), 267 ets:new(test_server_info, 268 [set, 269 public, 270 named_table, 271 {keypos, 1}]), 272 ets:insert(test_server_info, 273 {test_server_handler, self()}), 274 ssl_node_con_loop(Socket); 275 Error -> 276 halt("Failed to connect to test server " ++ 277 lists:flatten(io_lib:format("Host:~p ~n Port:~p~n Error:~p~n", 278 [Host, Port, Error]))) 279 end 280 end), 281 spawn(fun () -> 282 Mon = erlang:monitor(process, ConnHandler), 283 receive 284 {'DOWN', Mon, process, ConnHandler, Reason} -> 285 receive after 1000 -> ok end, 286 halt("test server connection handler terminated: " ++ 287 lists:flatten(io_lib:format("~p", [Reason]))) 288 end 289 end). 290 291notify_ssl_node_up(Socket) -> 292 case catch gen_tcp:send(Socket, 293 term_to_binary({ssl_node_up, node()})) of 294 ok -> ok; 295 _ -> halt("Failed to notify test server that I'm up") 296 end. 297 298send_to_tstsrvr(Term) -> 299 case catch ets:lookup_element(test_server_info, test_server_handler, 2) of 300 Hndlr when is_pid(Hndlr) -> 301 Hndlr ! {relay_to_test_server, term_to_binary(Term)}, ok; 302 _ -> 303 receive after 200 -> ok end, 304 send_to_tstsrvr(Term) 305 end. 306 307ssl_node_con_loop(Socket) -> 308 inet:setopts(Socket,[{active,once}]), 309 receive 310 {relay_to_test_server, Data} when is_binary(Data) -> 311 case gen_tcp:send(Socket, Data) of 312 ok -> 313 ok; 314 _Error -> 315 gen_tcp:close(Socket), 316 halt("Failed to relay data to test server") 317 end; 318 {tcp, Socket, Bin} -> 319 case catch binary_to_term(Bin) of 320 {'EXIT', _} -> 321 gen_tcp:close(Socket), 322 halt("test server sent me bad data"); 323 {apply, From, Ref, M, F, A} -> 324 spawn_link( 325 fun () -> 326 send_to_tstsrvr({apply_res, 327 From, 328 Ref, 329 (catch apply(M, F, A))}) 330 end); 331 {apply, From, Ref, Fun} -> 332 spawn_link(fun () -> 333 send_to_tstsrvr({apply_res, 334 From, 335 Ref, 336 (catch Fun())}) 337 end); 338 stop -> 339 gen_tcp:send(Socket, term_to_binary(bye)), 340 init:stop(), 341 receive after infinity -> ok end; 342 _Unknown -> 343 halt("test server sent me an unexpected message") 344 end; 345 {tcp_closed, Socket} -> 346 halt("Lost connection to test server") 347 end, 348 ssl_node_con_loop(Socket). 349