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