1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2007-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%% 22%%---------------------------------------------------------------------- 23%% Purpose: megaco_tcp sequence generator for the megaco test suite 24%%---------------------------------------------------------------------- 25 26-module(megaco_test_tcp_generator). 27 28-behaviour(megaco_test_generator). 29 30-compile({no_auto_import,[error/1]}). 31 32%% API 33-export([ 34 start_link/1, start_link/2, 35 stop/1, 36 exec/2, exec/3 37 ]). 38 39%% genarator behaviour callback exports 40-export([ 41 init/1, 42 handle_parse/2, 43 handle_exec/2, 44 terminate/2 45 ]). 46 47 48-record(state, 49 { 50 listen, % Listen socket 51 connection, % Connection socket 52 encode, % Encode fun 53 decode, % Decode fun 54 result = [] % Accumulated results from exec 55 }). 56 57 58%%---------------------------------------------------------------------- 59%% API 60%%---------------------------------------------------------------------- 61 62start_link(Name) -> 63 megaco_test_generator:start_link(?MODULE, [], Name). 64 65start_link(Name, Node) -> 66 megaco_test_generator:start_link(?MODULE, [], Name, Node). 67 68stop(Server) -> 69 megaco_test_generator:stop(Server). 70 71exec(Server, Instructions) when is_list(Instructions) -> 72 megaco_test_generator:exec(Server, Instructions). 73 74exec(Server, Instructions, Timeout) when is_list(Instructions) -> 75 megaco_test_generator:exec(Server, Instructions, Timeout). 76 77 78%%---------------------------------------------------------------------- 79%% generator callback functions 80%%---------------------------------------------------------------------- 81 82init([]) -> 83 {ok, #state{}}. 84 85 86%% ----- instruction parser ----- 87 88handle_parse({debug, Debug} = Instruction, State) 89 when (Debug == true) orelse (Debug == false) -> 90 {ok, Instruction, State}; 91 92handle_parse({decode, Decode} = Instruction, State) 93 when is_function(Decode) -> 94 {ok, Instruction, State}; 95 96handle_parse({encode, Encode} = Instruction, State) 97 when is_function(Encode) -> 98 {ok, Instruction, State}; 99 100handle_parse(disconnect = Instruction, State) -> 101 {ok, Instruction, State}; 102 103handle_parse({listen, Port} = Instruction, State) 104 when is_integer(Port) andalso (Port >= 0) -> 105 {ok, Instruction, State}; 106 107handle_parse({expect_accept, any} = _Instruction, State) -> 108 {ok, {expect_accept, {any, infinity}}, State}; 109 110handle_parse({expect_accept, {any, To}} = Instruction, State) 111 when (is_integer(To) andalso (To >= 0)) orelse (To == infinity) -> 112 {ok, Instruction, State}; 113 114handle_parse({expect_accept, {Host, To}} = _Instruction, State) 115 when (is_integer(To) andalso (To >= 0)) orelse (To == infinity) -> 116 case inet:getaddr(Host, inet) of 117 {ok, Addr} -> 118 Instruction = {expect_accept, {Addr, To}}, 119 {ok, Instruction, State}; 120 {error, Reason} -> 121 {error, {bad_host, Host, Reason}} 122 end; 123 124handle_parse({expect_accept, Host} = _Instruction, State) -> 125 case inet:getaddr(Host, inet) of 126 {ok, Addr} -> 127 Instruction = {expect_accept, {Addr, infinity}}, 128 {ok, Instruction, State}; 129 {error, Reason} -> 130 {error, {bad_host, Host, Reason}} 131 end; 132 133handle_parse({active, NewState} = Instruction, State) 134 when (NewState == true) orelse 135 (NewState == false) orelse 136 (NewState == once) -> 137 {ok, Instruction, State}; 138 139handle_parse({connect, Port} = _Instruction, State) 140 when is_integer(Port) andalso (Port >= 0) -> 141 Host = 142 case inet:gethostname() of 143 {ok, Hostname} -> 144 Hostname; 145 {error, Reason1} -> 146 error({failed_geting_own_hostname, Reason1}) 147 end, 148 case inet:getaddr(Host, inet) of 149 {ok, Address} -> 150 Instruction = {connect, {Address, Port, infinity}}, 151 {ok, Instruction, State}; 152 {error, Reason2} -> 153 {error, {bad_host, Host, Reason2}} 154 end; 155 156handle_parse({connect, {Port, To}} = _Instruction, State) 157 when (is_integer(Port) andalso 158 (Port >= 0)) andalso ((is_integer(To) andalso (To >= 0)) orelse 159 (To == infinity)) -> 160 Host = 161 case inet:gethostname() of 162 {ok, Hostname} -> 163 Hostname; 164 {error, Reason1} -> 165 error({failed_geting_own_hostname, Reason1}) 166 end, 167 case inet:getaddr(Host, inet) of 168 {ok, Address} -> 169 Instruction = {connect, {Address, Port, To}}, 170 {ok, Instruction, State}; 171 {error, Reason2} -> 172 {error, {bad_host, Host, Reason2}} 173 end; 174 175handle_parse({connect, {Host, Port}} = _Instruction, State) 176 when (is_integer(Port) andalso (Port >= 0)) -> 177 case inet:getaddr(Host, inet) of 178 {ok, Address} -> 179 Instruction = {connect, {Address, Port, infinity}}, 180 {ok, Instruction, State}; 181 {error, Reason} -> 182 {error, {bad_host, Host, Reason}} 183 end; 184 185handle_parse({connect, {Host, Port, To}} = _Instruction, State) 186 when (is_integer(Port) andalso 187 (Port >= 0)) andalso ((is_integer(To) andalso (To >= 0)) orelse 188 (To == infinity)) -> 189 case inet:getaddr(Host, inet) of 190 {ok, Address} -> 191 Instruction = {connect, {Address, Port, To}}, 192 {ok, Instruction, State}; 193 {error, Reason} -> 194 {error, {bad_host, Host, Reason}} 195 end; 196 197handle_parse({sleep, To} = Instruction, State) 198 when is_integer(To) andalso (To > 0) -> 199 {ok, Instruction, State}; 200 201handle_parse({expect_nothing, To} = Instruction, State) 202 when is_integer(To) andalso (To > 0) -> 203 {ok, Instruction, State}; 204 205handle_parse({expect_closed, To} = Instruction, State) 206 when is_integer(To) andalso (To > 0) -> 207 {ok, Instruction, State}; 208 209handle_parse({expect_receive, Desc, Verify} = _Instruction, State) 210 when is_list(Desc) andalso is_function(Verify) -> 211 Instruction = {expect_receive, Desc, {Verify, infinity}}, 212 {ok, Instruction, State}; 213 214handle_parse({expect_receive, Desc, {Verify, To}} = Instruction, State) 215 when is_list(Desc) andalso 216 is_function(Verify) andalso 217 ((is_integer(To) andalso (To >= 0)) orelse (To == infinity)) -> 218 {ok, Instruction, State}; 219 220handle_parse({send, Desc, Msg} = Instruction, State) 221 when is_list(Desc) andalso (is_tuple(Msg) orelse is_binary(Msg)) -> 222 {ok, Instruction, State}; 223 224handle_parse({trigger, Desc, Trigger} = Instruction, State) 225 when is_list(Desc) andalso is_function(Trigger) -> 226 {ok, Instruction, State}; 227 228handle_parse(Instruction, _State) -> 229 {error, {unknown_instruction, Instruction}}. 230 231 232%% ----- instruction exececutor ----- 233 234handle_exec({debug, Debug}, 235 State) -> 236 p("debug: ~p", [Debug]), 237 put(debug, Debug), 238 {ok, State}; 239 240handle_exec({encode, Encode}, 241 State) -> 242 p("encode: ~p", [Encode]), 243 {ok, State#state{encode = Encode}}; 244 245handle_exec({decode, Decode}, 246 State) -> 247 p("Decode: ~p", [Decode]), 248 {ok, State#state{decode = Decode}}; 249 250handle_exec(disconnect, 251 #state{listen = Listen, 252 connection = Sock, 253 result = Res} = State) -> 254 p("disconnect"), 255 (catch gen_tcp:close(Sock)), 256 (catch gen_tcp:close(Listen)), 257 {ok, State#state{listen = undefined, 258 connection = undefined, 259 result = [disconnected|Res]}}; 260 261handle_exec({listen, Port}, #state{result = Res} = State) -> 262 p("listen to ~p", [Port]), 263 Opts = [binary, 264 {packet, tpkt}, 265 {active, false}, 266 {reuseaddr, true}, 267 {nodelay, true}], 268 case (catch gen_tcp:listen(Port, Opts)) of 269 {ok, Listen} -> 270 d("listen -> listen socket created"), 271 {ok, State#state{listen = Listen, result = [listening | Res]}}; 272 {error, Reason} -> 273 e("failed creating listen socket: ~p", [Reason]), 274 {error, {failed_creating_listen_socket, Reason, Res}} 275 end; 276 277handle_exec({expect_accept, {Addr, To}}, 278 #state{listen = Listen, 279 result = Res} = State) -> 280 p("expect_accept from ~p (~p)", [Addr, To]), 281 case (catch gen_tcp:accept(Listen, To)) of 282 {ok, Sock} -> 283 d("expect_accept -> connection accepted"), 284 case (catch inet:peername(Sock)) of 285 {ok, {Addr, _Port}} -> 286 d("expect_accept -> valid address"), 287 NewState = 288 State#state{connection = Sock, 289 result = [{connected, Addr}|Res]}, 290 {ok, NewState}; 291 {ok, {OtherAddr, _Port}} when Addr == any -> 292 d("expect_accept -> valid (~p)", [OtherAddr]), 293 NewState = 294 State#state{connection = Sock, 295 result = [{connected, OtherAddr}|Res]}, 296 {ok, NewState}; 297 {ok, AddrAndPort} -> 298 {error, {invalid_connect, AddrAndPort, Res}}; 299 {error, Reason} -> 300 e("failed getting peername for socket: ~p", [Reason]), 301 (catch gen_tcp:close(Sock)), 302 {error, {failed_getting_peername, Sock, Reason}} 303 end; 304 {error, Reason} -> 305 e("failed accepting connection: ~p", [Reason]), 306 (catch gen_tcp:close(Listen)), 307 {error, {failed_accepting_conection, Reason, Listen}} 308 end; 309 310handle_exec({active, NewState}, 311 #state{connection = Sock, 312 result = Res} = State) -> 313 p("active to ~p", [NewState]), 314 case inet:setopts(Sock, [{active, NewState}]) of 315 ok -> 316 d("active -> state changed"), 317 {ok, State#state{result = [{active, NewState}|Res]}}; 318 {error, Reason} -> 319 e("failed changing active state to ~w: ~p", [NewState, Reason]), 320 {error, {failed_setting_active, Reason}} 321 end; 322 323handle_exec({connect, {Addr, Port, To}}, 324 #state{result = Res} = State) -> 325 p("connect to ~p, ~p", [Addr, Port]), 326 Opts = [binary, {packet, tpkt}, {active, once}, {nodelay, true}], 327 case (catch gen_tcp:connect(Addr, Port, Opts, To)) of 328 {ok, Sock} -> 329 d("connect -> connected"), 330 {ok, State#state{connection = Sock, 331 result = [{connected, Addr, Port}|Res]}}; 332 {error, Reason} -> 333 e("failed connecting: ~p", [Reason]), 334 {error, {failed_connect, Addr, Port, Reason}} 335 end; 336 337%% Already encoded 338handle_exec({send, Desc, Bin}, 339 #state{connection = Sock, 340 result = Res} = State) 341 when is_binary(Bin) -> 342 p("send ~s message", [Desc]), 343 NewBin = add_tpkt_header(Bin), 344 d("send -> tpkt header added [~w], now send", [sz(NewBin)]), 345 case (catch gen_tcp:send(Sock, NewBin)) of 346 ok -> 347 d("send -> message sent"), 348 {ok, State#state{result = [{sent, Desc}|Res]}}; 349 {error, Reason} -> 350 e("send -> send failed: ~n~p",[Reason]), 351 {error, {failed_send, Reason}} 352 end; 353 354handle_exec({send, Desc, Msg}, 355 #state{connection = Sock, 356 encode = Encode, 357 result = Res} = State) -> 358 p("send ~s message", [Desc]), 359 case (catch Encode(Msg)) of 360 {ok, Bin} -> 361 d("send -> message encoded [~w], now add tpkt header: ~n~s", 362 [sz(Bin), binary_to_list(Bin)]), 363 NewBin = add_tpkt_header(Bin), 364 d("send -> tpkt header added [~w], now send", [sz(NewBin)]), 365 case (catch gen_tcp:send(Sock, NewBin)) of 366 ok -> 367 d("send -> message sent"), 368 {ok, State#state{result = [{sent, Desc}|Res]}}; 369 {error, Reason} -> 370 e("send -> send failed: ~n~p", [Reason]), 371 {error, {failed_send, Reason}} 372 end; 373 Error -> 374 e("send -> encode failed: ~n~p", [Error]), 375 {error, {encode_failed, Error}} 376 end; 377 378handle_exec({expect_receive, Desc, {Verify, To}}, 379 #state{connection = Sock, 380 decode = Decode, 381 result = Acc} = State) -> 382 p("expect_receive ~s message", [Desc]), 383 inet:setopts(Sock, [{active, once}]), 384 receive 385 {tcp, Sock, <<3:8, _X:8, Length:16, Msg/binary>>} -> 386 d("expect_receive -> received message: Length = ~p", [Length]), 387 case (catch Decode(Msg)) of 388 {ok, MegaMsg} when is_tuple(MegaMsg) -> 389 d("expect_receive -> decode successfull, now verify"), 390 case (catch Verify(MegaMsg)) of 391 {ok, Res} -> 392 d("expect_receive -> verify successfull"), 393 {ok, State#state{result = [Res|Acc]}}; 394 Else -> 395 e("failed to verify message: ~n~p~n~p", 396 [Else, MegaMsg]), 397 {error, {expect_receive, {verify_failed, Else}}} 398 end; 399 Error -> 400 e("failed decoding message: ~p", [Error]), 401 {error, {expect_receive, Error}} 402 end; 403 Else -> 404 e("received unknown message: ~p", [Else]), 405 {error, {expect_receive, {unexpected_message, Else}}} 406 after To -> 407 e("(expect) receive timeout: ~w", [To]), 408 {error, {expect_receive, timeout}} 409 end; 410 411handle_exec({expect_closed, To}, 412 #state{connection = Sock, 413 result = Acc} = State) -> 414 p("expect_closed ~w", [To]), 415 inet:setopts(Sock, [{active, once}]), 416 d("expect_closed - await closed", []), 417 receive 418 {tcp_closed, Sock} -> 419 p("expect_closed - received closed"), 420 {ok, State#state{connection = undefined, 421 result = [closed|Acc]}} 422 after To -> 423 e("(expect) closed timeout after ~w", [To]), 424 {error, {expect_closed, timeout}} 425 end; 426 427handle_exec({expect_nothing, To}, 428 #state{connection = Sock, 429 result = Acc} = State) -> 430 p("expect_nothing ~w", [To]), 431 inet:setopts(Sock, [{active, once}]), 432 d("expect_nothing - await anything", []), 433 receive 434 Any -> 435 e("expect_nothing - received: ~p", [Any]), 436 {error, {expect_nothing, Any}} 437 after To -> 438 p("got nothing (~w) as expected", [To]), 439 {ok, State#state{result = [{nothing, To}|Acc]}} 440 end; 441 442handle_exec({trigger, Desc, Trigger}, 443 #state{result = Acc} = State) when is_function(Trigger) -> 444 p("trigger: ~s", [Desc]), 445 Trigger(), 446 {ok, State#state{result = [triggered|Acc]}}; 447 448handle_exec({sleep, To}, 449 #state{result = Acc} = State) -> 450 p("sleep ~p", [To]), 451 sleep(To), 452 {ok, State#state{result = [{slept, To}|Acc]}}; 453 454handle_exec(Instruction, _State) -> 455 {error, {unknown_instruction, Instruction}}. 456 457 458%% ----- terminate ----- 459 460terminate(normal, #state{listen = Listen, 461 connection = Sock, 462 result = Result}) -> 463 (catch gen_tcp:close(Sock)), 464 (catch gen_tcp:close(Listen)), 465 {ok, Result}; 466terminate(Reason, #state{listen = Listen, 467 connection = Sock, 468 result = Result}) -> 469 (catch gen_tcp:close(Sock)), 470 (catch gen_tcp:close(Listen)), 471 {error, {Reason, Result}}. 472 473 474%%---------------------------------------------------------------------- 475%% internal utility functions 476%%---------------------------------------------------------------------- 477 478error(Reason) -> 479 throw({error, Reason}). 480 481 482%%% ---------------------------------------------------------------- 483 484add_tpkt_header(Bin) when is_binary(Bin) -> 485 L = size(Bin) + 4, 486 SZ1 = ((L) bsr 8) band 16#ff, 487 SZ2 = (L) band 16#ff, 488 <<3, 0, SZ1, SZ2, Bin/binary>>; 489add_tpkt_header(IOList) when is_list(IOList) -> 490 add_tpkt_header(list_to_binary(IOList)). 491 492sleep(X) -> megaco_test_generator:sleep(X). 493 494sz(X) -> megaco_test_generator:sz(X). 495 496 497%%% ---------------------------------------------------------------- 498 499d(F) -> megaco_test_generator:debug(F). 500d(F, A) -> megaco_test_generator:debug(F, A). 501 502e(F, A) -> megaco_test_generator:error(F, A). 503 504p(F ) -> p("", F, []). 505p(F, A) -> p("", F, A). 506p(P, F, A) -> megaco_test_generator:print(P, F, A). 507 508 509