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-module(gen_sctp_SUITE).
21
22-include_lib("common_test/include/ct.hrl").
23-include_lib("kernel/include/inet_sctp.hrl").
24-include("kernel_test_lib.hrl").
25
26%%-compile(export_all).
27
28-export([all/0, suite/0,groups/0,
29	 init_per_suite/1,end_per_suite/1,
30	 init_per_group/2,end_per_group/2,
31	 init_per_testcase/2, end_per_testcase/2]).
32-export(
33   [skip_old_solaris/1,
34    basic/1,
35    api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1,
36    xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1,
37    open_multihoming_ipv4_socket/1,
38    open_unihoming_ipv6_socket/1,
39    open_multihoming_ipv6_socket/1,
40    open_multihoming_ipv4_and_ipv6_socket/1,
41    basic_stream/1, xfer_stream_min/1, active_n/1,
42    peeloff_active_once/1, peeloff_active_true/1, peeloff_active_n/1,
43    buffers/1,
44    names_unihoming_ipv4/1, names_unihoming_ipv6/1,
45    names_multihoming_ipv4/1, names_multihoming_ipv6/1,
46    recv_close/1]).
47
48suite() ->
49    [{ct_hooks,[ts_install_cth]},
50     {timetrap,{minutes,1}}].
51
52all() ->
53    G = case is_old_solaris() of
54	    true -> old_solaris;
55	    false -> extensive
56	end,
57    [{group,smoke},
58     {group,G}].
59
60groups() ->
61    [
62     {smoke,       [], smoke_cases()},
63     {old_solaris, [], old_solaris_cases()},
64     {extensive,   [], extensive_cases()}
65    ].
66
67smoke_cases() ->
68    [
69     basic,
70     basic_stream
71    ].
72
73old_solaris_cases() ->
74    [
75     skip_old_solaris
76    ].
77
78extensive_cases() ->
79    [api_open_close, api_listen, api_connect_init,
80     api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6,
81     open_multihoming_ipv4_socket,
82     open_unihoming_ipv6_socket,
83     open_multihoming_ipv6_socket,
84     open_multihoming_ipv4_and_ipv6_socket, active_n,
85     xfer_stream_min, peeloff_active_once,
86     peeloff_active_true, peeloff_active_n, buffers,
87     names_unihoming_ipv4, names_unihoming_ipv6,
88     names_multihoming_ipv4, names_multihoming_ipv6,
89     recv_close].
90
91init_per_suite(_Config) ->
92    case gen_sctp:open() of
93	{ok,Socket} ->
94	    gen_sctp:close(Socket),
95	    [];
96	{error,Error}
97	  when Error =:= eprotonosupport;
98	       Error =:= esocktnosupport ->
99	    {skip,"SCTP not supported on this machine"}
100    end.
101
102end_per_suite(_Config) ->
103    ok.
104
105init_per_group(_GroupName, Config) ->
106    Config.
107
108end_per_group(_GroupName, Config) ->
109    Config.
110
111
112init_per_testcase(_Func, Config) ->
113    Config.
114
115end_per_testcase(_Func, _Config) ->
116    ok.
117
118
119-define(LOGVAR(Var), begin io:format(??Var" = ~p~n", [Var]) end).
120
121is_old_solaris() ->
122    os:type() =:= {unix,sunos} andalso os:version() < {5,12,0}.
123
124skip_old_solaris(_Config) ->
125    {skip,"Unreliable test cases and/or implementation on old Solaris"}.
126
127%% Hello world.
128basic(Config) when is_list(Config) ->
129    {ok,S} = gen_sctp:open(),
130    ok = gen_sctp:close(S),
131    ok.
132
133%% Minimal data transfer.
134xfer_min(Config) when is_list(Config) ->
135    Stream = 0,
136    Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
137    Loopback = {127,0,0,1},
138    StatOpts =
139	[recv_avg,recv_cnt,recv_max,recv_oct,
140	 send_avg,send_cnt,send_max,send_oct],
141    {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
142    {ok,SbStat1} = inet:getstat(Sb, StatOpts),
143    {ok,Pb} = inet:port(Sb),
144    ok = gen_sctp:listen(Sb, true),
145
146    {ok,Sa} = gen_sctp:open(),
147    {ok,Pa} = inet:port(Sa),
148    {ok,#sctp_assoc_change{state=comm_up,
149			   error=0,
150			   outbound_streams=SaOutboundStreams,
151			   inbound_streams=SaInboundStreams,
152			   assoc_id=SaAssocId}=SaAssocChange} =
153	gen_sctp:connect(Sa, Loopback, Pb, []),
154    {SbAssocId,SaOutboundStreams,SaInboundStreams} =
155	case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
156	    {Loopback,Pa,
157	     #sctp_assoc_change{state=comm_up,
158				error=0,
159				outbound_streams=SbOutboundStreams,
160				inbound_streams=SbInboundStreams,
161				assoc_id=AssocId}} ->
162		{AssocId,SbInboundStreams,SbOutboundStreams};
163	    {Loopback,Pa,
164	     #sctp_paddr_change{state=addr_confirmed,
165				addr={Loopback,Pa},
166				error=0,
167				assoc_id=AssocId}} ->
168		{Loopback,Pa,
169		 #sctp_assoc_change{state=comm_up,
170				    error=0,
171				    outbound_streams=SbOutboundStreams,
172				    inbound_streams=SbInboundStreams,
173				    assoc_id=AssocId}} =
174		    recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
175		{AssocId,SbInboundStreams,SbOutboundStreams}
176	end,
177
178    ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
179    case log_ok(gen_sctp:recv(Sb, infinity)) of
180	{Loopback,
181	 Pa,
182	 [#sctp_sndrcvinfo{stream   = Stream,
183			   assoc_id = SbAssocId}],
184	 Data} -> ok;
185	Event1 ->
186	    case recv_event(Event1) of
187		{Loopback,Pa,
188		 #sctp_paddr_change{addr = {Loopback,_},
189				    state = State,
190				    error = 0,
191				    assoc_id = SbAssocId}}
192		  when State =:= addr_available;
193		       State =:= addr_confirmed ->
194		    {Loopback,
195		     Pa,
196		     [#sctp_sndrcvinfo{stream=Stream,
197				       assoc_id=SbAssocId}],
198		     Data} = log_ok(gen_sctp:recv(Sb, infinity))
199	    end
200    end,
201    ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
202    case log_ok(gen_sctp:recv(Sa, infinity)) of
203	{Loopback,Pb,
204	 [#sctp_sndrcvinfo{stream=Stream,
205			   assoc_id=SaAssocId}],
206	 Data} ->
207	    ok;
208	Event2 ->
209	    {Loopback,Pb,
210	     #sctp_paddr_change{addr={_,Pb},
211				state=addr_confirmed,
212				error=0,
213				assoc_id=SaAssocId}} =
214		recv_event(Event2),
215	    {Loopback,
216	     Pb,
217	     [#sctp_sndrcvinfo{stream=Stream,
218			       assoc_id=SaAssocId}],
219	     Data} =
220		log_ok(gen_sctp:recv(Sa, infinity))
221    end,
222    %%
223    ok = gen_sctp:eof(Sa, SaAssocChange),
224    {Loopback,Pa,#sctp_shutdown_event{assoc_id=SbAssocId}} =
225	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
226    {Loopback,Pb,
227     #sctp_assoc_change{state=shutdown_comp,
228			error=0,
229			assoc_id=SaAssocId}} =
230	recv_event(log_ok(gen_sctp:recv(Sa, infinity))),
231    {Loopback,Pa,
232     #sctp_assoc_change{state=shutdown_comp,
233			error=0,
234			assoc_id=SbAssocId}} =
235	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
236    ok = gen_sctp:close(Sa),
237    {ok,SbStat2} = inet:getstat(Sb, StatOpts),
238    [] = filter_stat_eq(SbStat1, SbStat2),
239    ok = gen_sctp:close(Sb),
240
241    receive
242	Msg -> ct:fail({received,Msg})
243    after 17 -> ok
244    end,
245    ok.
246
247filter_stat_eq([], []) ->
248    [];
249filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) ->
250    if
251	Val1 == Val2 ->
252	    [Stat|filter_stat_eq(SbStat1, SbStat2)];
253	true ->
254	    filter_stat_eq(SbStat1, SbStat2)
255    end.
256
257
258
259%% Minimal data transfer in active mode.
260xfer_active(Config) when is_list(Config) ->
261    Timeout = 2000,
262    Stream = 0,
263    Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
264    Loopback = {127,0,0,1},
265    {ok,Sb} = gen_sctp:open([{active,true}]),
266    {ok,Pb} = inet:port(Sb),
267    ok = gen_sctp:listen(Sb, true),
268
269    {ok,Sa} = gen_sctp:open([{active,true}]),
270    {ok,Pa} = inet:port(Sa),
271    ok = gen_sctp:connect_init(Sa, Loopback, Pb, []),
272    #sctp_assoc_change{state=comm_up,
273		       error=0,
274		       outbound_streams=SaOutboundStreams,
275		       inbound_streams=SaInboundStreams,
276		       assoc_id=SaAssocId} = SaAssocChange =
277	recv_assoc_change(Sa, Loopback, Pb, Timeout),
278    io:format("Sa=~p, Pa=~p, Sb=~p, Pb=~p, SaAssocId=~p, "
279	      "SaOutboundStreams=~p, SaInboundStreams=~p~n",
280	      [Sa,Pa,Sb,Pb,SaAssocId,
281	       SaOutboundStreams,SaInboundStreams]),
282    #sctp_assoc_change{state=comm_up,
283		       error=0,
284		       outbound_streams=SbOutboundStreams,
285		       inbound_streams=SbInboundStreams,
286		       assoc_id=SbAssocId} =
287	recv_assoc_change(Sb, Loopback, Pa, Timeout),
288    SbOutboundStreams = SaInboundStreams,
289    SbInboundStreams = SaOutboundStreams,
290    io:format("SbAssocId=~p~n", [SbAssocId]),
291
292    case recv_paddr_change(Sa, Loopback, Pb, 314) of
293	#sctp_paddr_change{state=addr_confirmed,
294			   addr={_,Pb},
295			   error=0,
296			   assoc_id=SaAssocId} -> ok;
297	#sctp_paddr_change{state=addr_available,
298			   addr={_,Pb},
299			   error=0,
300			   assoc_id=SaAssocId} -> ok;
301	timeout -> ok
302    end,
303    case recv_paddr_change(Sb, Loopback, Pa, 314) of
304	#sctp_paddr_change{state=addr_confirmed,
305			   addr={Loopback,Pa},
306			   error=0,
307			   assoc_id=SbAssocId} -> ok;
308	#sctp_paddr_change{state=addr_available,
309			   addr={Loopback,P},
310			   error=0,
311			   assoc_id=SbAssocId} ->
312	    match_unless_solaris(Pa, P);
313	timeout -> ok
314    end,
315    [] = flush(),
316
317    ok =
318	do_from_other_process(
319	  fun () -> gen_sctp:send(Sa, SaAssocId, 0, Data) end),
320    receive
321	{sctp,Sb,Loopback,Pa,
322	 {[#sctp_sndrcvinfo{stream=Stream,
323			    assoc_id=SbAssocId}],
324	  Data}} -> ok
325    after Timeout ->
326	    ct:fail({timeout,flush()})
327    end,
328    ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
329    receive
330	{sctp,Sa,Loopback,Pb,
331	 {[#sctp_sndrcvinfo{stream=Stream,
332			    assoc_id=SaAssocId}],
333	  Data}} -> ok
334    after Timeout ->
335	    ct:fail({timeout,flush()})
336    end,
337    %%
338    ok = gen_sctp:abort(Sa, SaAssocChange),
339    case recv_assoc_change(Sb, Loopback, Pa, Timeout) of
340	#sctp_assoc_change{state=comm_lost,
341			   assoc_id=SbAssocId} -> ok;
342	timeout ->
343	    ct:fail({timeout,flush()})
344    end,
345    ok = gen_sctp:close(Sb),
346    case recv_assoc_change(Sa, Loopback, Pb, Timeout) of
347	#sctp_assoc_change{state=comm_lost,
348			   assoc_id=SaAssocId} -> ok;
349	timeout ->
350	    io:format("timeout waiting for comm_lost on Sa~n"),
351	    match_unless_solaris(ok, {timeout,flush()})
352    end,
353    receive
354	{sctp_error,Sa,enotconn} -> ok % Solaris
355    after 17 -> ok
356    end,
357    ok = gen_sctp:close(Sa),
358    %%
359    receive
360	Msg -> ct:fail({unexpected,[Msg]++flush()})
361    after 17 -> ok
362    end,
363    ok.
364
365recv_assoc_change(S, Addr, Port, Timeout) ->
366    receive
367	{sctp,S,Addr,Port,{[], #sctp_assoc_change{}=AssocChange}} ->
368	    AssocChange;
369	{sctp,S,Addr,Port,
370	 {[#sctp_sndrcvinfo{assoc_id=AssocId}],
371	  #sctp_assoc_change{assoc_id=AssocId}=AssocChange}} ->
372	    AssocChange
373    after Timeout ->
374	    timeout
375    end.
376
377recv_paddr_change(S, Addr, Port, Timeout) ->
378    receive
379	{sctp,S,Addr,Port,{[], #sctp_paddr_change{}=PaddrChange}} ->
380	    PaddrChange;
381	{sctp,S,Addr,Port,
382	 {[#sctp_sndrcvinfo{assoc_id=AssocId}],
383	  #sctp_paddr_change{assoc_id=AssocId}=PaddrChange}} ->
384	    PaddrChange
385    after Timeout ->
386	    timeout
387    end.
388
389%% Test that #sctp_sndrcvinfo{} parameters set on a socket
390%% are used by gen_sctp:send/4.
391def_sndrcvinfo(Config) when is_list(Config) ->
392    Loopback = {127,0,0,1},
393    Data = <<"What goes up, must come down.">>,
394    %%
395    S1 =
396	log_ok(gen_sctp:open(
397		 0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
398    ?LOGVAR(S1),
399    P1 =
400	log_ok(inet:port(S1)),
401    ?LOGVAR(P1),
402    #sctp_sndrcvinfo{ppid=17, context=0, timetolive=0, assoc_id=0} =
403	getopt(S1, sctp_default_send_param),
404    ok =
405	gen_sctp:listen(S1, true),
406    %%
407    S2 =
408	log_ok(gen_sctp:open()),
409    ?LOGVAR(S2),
410    P2 =
411	log_ok(inet:port(S2)),
412    ?LOGVAR(P2),
413    #sctp_sndrcvinfo{ppid=0, context=0, timetolive=0, assoc_id=0} =
414	getopt(S2, sctp_default_send_param),
415    %%
416    #sctp_assoc_change{
417       state=comm_up,
418       error=0,
419       assoc_id=S2AssocId} = S2AssocChange =
420	log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
421    ?LOGVAR(S2AssocChange),
422    S1AssocId =
423	case recv_event(log_ok(gen_sctp:recv(S1))) of
424	    {Loopback,P2,
425	     #sctp_assoc_change{
426		state=comm_up,
427		error=0,
428		assoc_id=AssocId}} ->
429		AssocId;
430	    {Loopback,P2,
431	     #sctp_paddr_change{
432		state=addr_confirmed,
433		error=0,
434		assoc_id=AssocId}} ->
435		{Loopback,P2,
436		 #sctp_assoc_change{
437		    state=comm_up,
438		    error=0,
439		    assoc_id=AssocId}} =
440		    recv_event(log_ok(gen_sctp:recv(S1))),
441		AssocId
442	end,
443    ?LOGVAR(S1AssocId),
444
445    #sctp_sndrcvinfo{
446       ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
447	getopt(
448	  S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}),
449    #sctp_sndrcvinfo{
450       ppid=0, context=0, timetolive=0} = %, assoc_id=S2AssocId} =
451	getopt(
452	  S2, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S2AssocId}),
453    %%
454    ok =
455	gen_sctp:send(S1, S1AssocId, 1, <<"1: ",Data/binary>>),
456    case log_ok(gen_sctp:recv(S2)) of
457	{Loopback,P1,
458	 [#sctp_sndrcvinfo{
459	     stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
460	 <<"1: ",Data/binary>>} -> ok;
461	Event1 ->
462	    {Loopback,P1,
463	     #sctp_paddr_change{state=addr_confirmed,
464				addr={_,P1},
465				error=0,
466				assoc_id=S2AssocId}} =
467		recv_event(Event1),
468	    {Loopback,P1,
469	     [#sctp_sndrcvinfo{
470		 stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
471	     <<"1: ",Data/binary>>} =
472		log_ok(gen_sctp:recv(S2))
473    end,
474    %%
475    ok =
476	setopt(
477	  S1, sctp_default_send_param, #sctp_sndrcvinfo{ppid=18}),
478    ok =
479	setopt(
480	  S1, sctp_default_send_param,
481	  #sctp_sndrcvinfo{ppid=19, assoc_id=S1AssocId}),
482    #sctp_sndrcvinfo{
483       ppid=18, context=0, timetolive=0, assoc_id=0} =
484	getopt(S1, sctp_default_send_param),
485    #sctp_sndrcvinfo{
486       ppid=19, context=0, timetolive=0, assoc_id=S1AssocId} =
487	getopt(
488	  S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}),
489    %%
490    ok =
491	gen_sctp:send(S1, S1AssocId, 0, <<"2: ",Data/binary>>),
492    case log_ok(gen_sctp:recv(S2)) of
493	{Loopback,P1,
494	 [#sctp_sndrcvinfo{
495	     stream=0, ppid=19, context=0, assoc_id=S2AssocId}],
496	 <<"2: ",Data/binary>>} -> ok
497    end,
498    ok =
499	gen_sctp:send(S2, S2AssocChange, 1, <<"3: ",Data/binary>>),
500    case log_ok(gen_sctp:recv(S1)) of
501	{Loopback,P2,
502	 [#sctp_sndrcvinfo{
503	     stream=1, ppid=0, context=0, assoc_id=S1AssocId}],
504	 <<"3: ",Data/binary>>} -> ok;
505	Event2 ->
506	    case recv_event(Event2) of
507		{Loopback,P2,
508		 #sctp_paddr_change{
509		    addr={Loopback,_},
510		    state=State,
511		    error=0, assoc_id=S1AssocId}}
512		  when State =:= addr_available;
513		       State =:= addr_confirmed ->
514		    case log_ok(gen_sctp:recv(S1)) of
515			{Loopback,P2,
516			 [#sctp_sndrcvinfo{
517			     stream=1, ppid=0, context=0,
518			     assoc_id=S1AssocId}],
519			 <<"3: ",Data/binary>>} -> ok
520		    end
521	    end
522    end,
523    ok =
524	do_from_other_process(
525	  fun () ->
526		  gen_sctp:send(
527		    S2,
528		    #sctp_sndrcvinfo{stream=0, ppid=20, assoc_id=S2AssocId},
529		    <<"4: ",Data/binary>>)
530	  end),
531    case log_ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of
532	{Loopback,P2,
533	 [#sctp_sndrcvinfo{
534	     stream=0, ppid=20, context=0, assoc_id=S1AssocId}],
535	 <<"4: ",Data/binary>>} -> ok
536    end,
537    %%
538    ok =
539	gen_sctp:close(S1),
540    ok =
541	gen_sctp:close(S2),
542    receive
543	Msg ->
544	    ct:fail({received,Msg})
545    after 17 -> ok
546    end,
547    ok.
548
549getopt(S, Opt) ->
550    {ok,[{Opt,Val}]} = inet:getopts(S, [Opt]),
551    Val.
552
553getopt(S, Opt, Param) ->
554    {ok,[{Opt,Val}]} = inet:getopts(S, [{Opt,Param}]),
555    Val.
556
557setopt(S, Opt, Val) ->
558    inet:setopts(S, [{Opt,Val}]).
559
560
561flush() ->
562    receive
563	Msg ->
564	    [Msg|flush()]
565    after 17 ->
566	    []
567    end.
568
569%% Test the API function open/1,2 and close/1.
570api_open_close(Config) when is_list(Config) ->
571    {ok,S1} = gen_sctp:open(0),
572    {ok,P}  = inet:port(S1),
573    ok      = gen_sctp:close(S1),
574
575    {ok,S2} = gen_sctp:open(P),
576    {ok,P}  = inet:port(S2),
577    ok      = gen_sctp:close(S2),
578
579    {ok,S3} = gen_sctp:open([{port,P}]),
580    {ok,P}  = inet:port(S3),
581    ok      = gen_sctp:close(S3),
582
583    {ok,S4} = gen_sctp:open(P, []),
584    {ok,P}  = inet:port(S4),
585    ok      = gen_sctp:close(S4),
586
587    {ok,S5} = gen_sctp:open(P, [{ifaddr,any}]),
588    {ok,P}  = inet:port(S5),
589    ok      = gen_sctp:close(S5),
590
591    ok      = gen_sctp:close(S5),
592
593    try gen_sctp:close(0)
594    catch error:badarg -> ok
595    end,
596
597    try gen_sctp:open({})
598    catch error:badarg -> ok
599    end,
600
601    try gen_sctp:open(-1)
602    catch error:badarg -> ok
603    end,
604
605    try gen_sctp:open(65536)
606    catch error:badarg -> ok
607    end,
608
609    try gen_sctp:open(make_ref(), [])
610    catch error:badarg -> ok
611    end,
612
613    try gen_sctp:open(0, {})
614    catch error:badarg -> ok
615    end,
616
617    try gen_sctp:open(0, [make_ref()])
618    catch error:badarg -> ok
619    end,
620
621    try gen_sctp:open([{invalid_option,0}])
622    catch error:badarg -> ok
623    end,
624
625    try gen_sctp:open(0, [{mode,invalid_mode}])
626    catch error:badarg -> ok
627    end,
628    ok.
629
630%% Test the API function listen/2.
631api_listen(Config) when is_list(Config) ->
632    Localhost = {127,0,0,1},
633
634    try gen_sctp:listen(0, true)
635    catch error:badarg -> ok
636    end,
637
638    {ok,S} = gen_sctp:open(),
639    {ok,Pb} = inet:port(S),
640    try gen_sctp:listen(S, not_allowed_for_listen)
641    catch error:badarg -> ok
642    end,
643    ok = gen_sctp:close(S),
644    {error,closed} = gen_sctp:listen(S, true),
645
646    {ok,Sb} = gen_sctp:open(Pb),
647    {ok,Sa} = gen_sctp:open(),
648    case gen_sctp:connect(Sa, localhost, Pb, []) of
649	{error,econnrefused} ->
650	    {ok,{Localhost,
651		 Pb,[],
652		 #sctp_assoc_change{
653		    state=comm_lost}}} =
654		gen_sctp:recv(Sa, infinity);
655	{error,#sctp_assoc_change{state=cant_assoc}} ->
656	    ok%;
657	    %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} ->
658	    %% 	  ok
659    end,
660    ok = gen_sctp:listen(Sb, true),
661    {ok,#sctp_assoc_change{state=comm_up,
662			   error=0}} =
663	gen_sctp:connect(Sa, localhost, Pb, []),
664    ok = gen_sctp:close(Sa),
665    ok = gen_sctp:close(Sb),
666    ok.
667
668%% Test the API function connect_init/4.
669api_connect_init(Config) when is_list(Config) ->
670    Localhost = {127,0,0,1},
671
672    {ok,S} = gen_sctp:open(),
673    {ok,Pb} = inet:port(S),
674    try gen_sctp:connect_init(S, Localhost, not_allowed_for_port, [])
675    catch error:badarg -> ok
676    end,
677    try gen_sctp:connect_init(S, Localhost, 12345, not_allowed_for_opts)
678    catch error:badarg -> ok
679    end,
680    ok = gen_sctp:close(S),
681    {error,closed} = gen_sctp:connect_init(S, Localhost, 12345, []),
682
683    {ok,Sb} = gen_sctp:open(Pb),
684    {ok,Sa} = gen_sctp:open(),
685    case gen_sctp:connect_init(Sa, localhost, Pb, []) of
686	{error,econnrefused} ->
687	    {Localhost,Pb,#sctp_assoc_change{state=comm_lost}} =
688		recv_event(log_ok(gen_sctp:recv(Sa, infinity)));
689	ok ->
690	    {Localhost,Pb,#sctp_assoc_change{state=cant_assoc}} =
691		recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
692    end,
693    ok = gen_sctp:listen(Sb, true),
694    case gen_sctp:connect_init(Sa, localhost, Pb, []) of
695	ok ->
696	    {Localhost,Pb,#sctp_assoc_change{state=comm_up}} =
697		recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
698    end,
699    ok = gen_sctp:close(Sa),
700    ok = gen_sctp:close(Sb),
701    ok.
702
703recv_event({Addr, Port, [], #sctp_assoc_change{} = AssocChange}) ->
704    {Addr, Port, AssocChange};
705recv_event({Addr,Port,
706	    [#sctp_sndrcvinfo{assoc_id  = Assoc}],
707	    #sctp_assoc_change{assoc_id = Assoc} = AssocChange}) ->
708    {Addr, Port, AssocChange};
709recv_event({Addr, Port, [], #sctp_paddr_change{} = PaddrChange}) ->
710    {Addr, Port, PaddrChange};
711recv_event({Addr, Port,
712	    [#sctp_sndrcvinfo{assoc_id  = Assoc}],
713	    #sctp_paddr_change{assoc_id = Assoc} = PaddrChange}) ->
714    {Addr, Port, PaddrChange};
715recv_event({Addr, Port, [], #sctp_shutdown_event{} = ShutdownEvent}) ->
716    {Addr, Port, ShutdownEvent};
717recv_event({Addr, Port,
718	    [#sctp_sndrcvinfo{assoc_id    = Assoc}],
719	    #sctp_shutdown_event{assoc_id = Assoc} = ShutdownEvent}) ->
720    {Addr, Port, ShutdownEvent}.
721
722%% Test socket options.
723api_opts(Config) when is_list(Config) ->
724    Sndbuf = 32768,
725    Recbuf = 65536,
726    {ok,S} = gen_sctp:open(0),
727    OSType = os:type(),
728    case {inet:setopts(S, [{linger,{true,2}}]),OSType} of
729	{ok,_} ->
730	    ok;
731	{{error,einval},{unix,sunos}} ->
732	    ok
733    end,
734    ok = inet:setopts(S, [{sndbuf,Sndbuf}]),
735    ok = inet:setopts(S, [{recbuf,Recbuf}]),
736    case inet:getopts(S, [sndbuf]) of
737	{ok, [{sndbuf,SB}]} when SB >= Sndbuf -> ok
738    end,
739    case inet:getopts(S, [recbuf]) of
740	{ok, [{recbuf, RB}]} when (RB >= Recbuf) -> ok
741    end.
742
743%% What is this *actually* supposed to test?
744implicit_inet6(Config) when is_list(Config) ->
745    ?TC_TRY(implicit_inet6, fun() -> do_implicit_inet6(Config) end).
746
747do_implicit_inet6(_Config) ->
748    ?P("begin"),
749    %% First
750    ?P("try create server socket (1)"),
751    case gen_sctp:open(0, [inet6]) of
752	{ok, S1} ->
753            Loopback = {0,0,0,0,0,0,0,1},
754            ?P("*** ~s: ~p ***", ["Loopback", Loopback]),
755            implicit_inet6(S1, Loopback),
756            ok = gen_sctp:close(S1),
757
758            %% Second
759            ?P("try create server socket (2)"),
760            Localhost =
761                case inet:getaddr("localhost", inet6) of
762                    {ok, LH} ->
763                        LH;
764                    {error, nxdomain = Reason_getaddr} ->
765                        ?SKIPT(Reason_getaddr);
766                    {error, Reason_getaddr} ->
767                        ?line ct:fail({unexpected, Reason_getaddr})
768                end,
769            S2 = case gen_sctp:open(0, [{ip, Localhost}]) of
770                     {ok, S} ->
771                         S;
772                     {error, nxdomain = Reason_open} ->
773                         ?SKIPT(Reason_open);
774                     {error, Reason_open} ->
775                         ?line ct:fail({unexpected, Reason_open})
776                 end,
777
778            ?P("*** ~s: ~p ***", ["localhost", Localhost]),
779            implicit_inet6(S2, Localhost),
780            ok = gen_sctp:close(S2),
781
782            %% Third
783            ?P("try create server socket (3)"),
784            Hostname = log_ok(inet:gethostname()),
785            Addr     = case inet:getaddr(Hostname, inet6) of
786                           {ok, A} ->
787                               A;
788                           {error, eafnosupport} ->
789                               ok = gen_sctp:close(S1),
790                               ?SKIPT("Can not look up IPv6 address")
791                       end,
792            S3 = log_ok(gen_sctp:open(0, [{ifaddr, Addr}])),
793            ?P("*** ~s: ~p ***", [Hostname, Addr]),
794            implicit_inet6(S3, Addr),
795            ok = gen_sctp:close(S1),
796            ?P("done"),
797            ok;
798        {error, eaddrnotavail = Reason} ->
799            ?SKIPT(open_failed_str(Reason));
800        _ ->
801            {skip, "IPv6 not supported"}
802    end.
803
804
805implicit_inet6(S1, Addr) ->
806    ?P("make (server) listen socket"),
807    ok = gen_sctp:listen(S1, true),
808    ServerPortNo = log_ok(inet:port(S1)),
809    ?P("try create (client) socket"),
810    S2 = case gen_sctp:open(0, [inet6, {ifaddr, Addr}]) of
811             {ok, Sock} ->
812                 ?P("client socket created: ~p", [Sock]),
813                 Sock;
814             {error, eaddrnotavail = Reason} ->
815                 ?P("could not create (client) socket with ifaddr: "
816                    "~n   ~p", [Addr]),
817                 ?SKIPT(open_failed_str(Reason))
818    end,
819    {ClientAddr, ClientPortNo} = log_ok(inet:sockname(S2)),
820    ?P("try connect"
821       "~n   from (connector): ~p, ~p (~p)"
822       "~n   to:               ~p, ~p",
823       [ClientAddr, ClientPortNo, S2, Addr, ServerPortNo]),
824    #sctp_assoc_change{state = comm_up} =
825	log_ok(gen_sctp:connect(S2, Addr, ServerPortNo, [])),
826    ?P("connect success: await events"),
827    implicit_inet6_await_ac_comm_up(S1, ClientAddr, ClientPortNo),
828    ?P("verify server sockname"),
829    case log_ok(inet:sockname(S1)) of
830	{Addr,              ServerPortNo} -> ok;
831	{{0,0,0,0,0,0,0,0}, ServerPortNo} -> ok
832    end,
833    ?P("verify client sockname"),
834    case log_ok(inet:sockname(S2)) of
835	{Addr,              ClientPortNo} -> ok;
836	{{0,0,0,0,0,0,0,0}, ClientPortNo} -> ok
837    end,
838    ?P("client client socket"),
839    ok = gen_sctp:close(S2),
840    ?P("verification complete"),
841    ok.
842
843
844implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo) ->
845    {_OsFam, OsName} = os:type(),
846    implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName).
847
848implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName) ->
849    case recv_event(log_ok(gen_sctp:recv(Sock))) of
850	{Addr, PortNo, #sctp_assoc_change{state = comm_up}} ->
851            ?P("received assoc-change:comm-up event => done"),
852	    ok;
853	{Addr, PortNo, #sctp_paddr_change{state = addr_confirmed,
854                                          addr  = {Addr, PortNo},
855                                          error = 0}} ->
856            ?P("received paddr-change:addr-confirmed event - "
857               "try recv assoc-change:comm-up"),
858            implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName);
859
860	{Addr2, PortNo2, #sctp_assoc_change{state = comm_up}}
861          when (OsName =:= freebsd) ->
862            ?P("Expected (assoc-change:comm-up) event from unexpected address: "
863               "~n   Unexpected Address: ~p, ~p"
864               "~n   Expected Address:   ~p, ~p"
865               "~n   => RETRY"
866               "~n", [Addr2, PortNo2, Addr, PortNo]),
867            implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName);
868	{Addr2, PortNo2, #sctp_paddr_change{state = addr_confirmed}}
869          when (OsName =:= freebsd) ->
870            ?P("Expected paddr-change:addr-confirmed event from "
871               "UNEXPECTED ADDRESS: "
872               "~n   UNEXPECTED Address: ~p, ~p"
873               "~n   Expected Address:   ~p, ~p"
874               "~n   => RETRY"
875               "~n", [Addr2, PortNo2, Addr, PortNo]),
876            implicit_inet6_await_ac_comm_up(Sock, Addr, PortNo, OsName);
877
878	{Addr2, PortNo2, #sctp_assoc_change{state = comm_up} = CX} = UNEX ->
879            ?P("Expected (assoc-change:comm-up) event from UNEXPECTED ADDRESS: "
880               "~n   UNEXPECTED Address: ~p, ~p"
881               "~n   Expected Address:   ~p, ~p"
882               "~n   Assoc Change:       ~p"
883               "~n", [Addr2, PortNo2, Addr, PortNo, CX]),
884            exit({unexpected_event, UNEX});
885
886	{AX, PX, #sctp_paddr_change{state = addr_confirmed} = CX} = UNEX ->
887            ?P("Expected paddr-change:addr-confirmed event from "
888               "UNEXPECTED ADDRESS: "
889               "~n   UNEXPECTED Address: ~p, ~p"
890               "~n   Expected Address:   ~p, ~p"
891               "~n   PAddr Change:       ~p"
892               "~n", [AX, PX, Addr, PortNo, CX]),
893            exit({unexpected_event, UNEX});
894
895        {AX, PX, CX} = UNEX ->
896            ?P("UNEXPECTED EVENT: "
897               "~n   ~p"
898               "~n   UNEXPECTED ADDRESS: ~p, ~p"
899               "~n   Expected Address:   ~p, ~p"
900               "~n", [CX, AX, PX, Addr, PortNo]),
901            exit({unexpected_event, UNEX})
902    end.
903
904
905%% Verify {active, N} socket management.
906%% This is difficult to do since we do not just receive data messages.
907%% Also, how do we know that sctp behaves the same way on all platforms?
908active_n(Config) when is_list(Config) ->
909    ?TC_TRY(active_n, fun() -> do_active_n(Config) end).
910
911do_active_n(_Config) ->
912    N = 3,
913    S1 = ok(gen_sctp:open([{active,N}])),
914    [{active,N}] = ok(inet:getopts(S1, [active])),
915    ok = inet:setopts(S1, [{active,-N}]),
916    receive
917        {sctp_passive, S1} -> ok
918    after
919        5000 ->
920            exit({error,sctp_passive_failure})
921    end,
922    [{active,false}] = ok(inet:getopts(S1, [active])),
923    ok = inet:setopts(S1, [{active,0}]),
924    receive
925        {sctp_passive, S1} -> ok
926    after
927        5000 ->
928            exit({error,sctp_passive_failure})
929    end,
930    ok = inet:setopts(S1, [{active,32767}]),
931    {error,einval} = inet:setopts(S1, [{active,1}]),
932    {error,einval} = inet:setopts(S1, [{active,-32769}]),
933    ok = inet:setopts(S1, [{active,-32768}]),
934    receive
935        {sctp_passive, S1} -> ok
936    after
937        5000 ->
938            exit({error,sctp_passive_failure})
939    end,
940    [{active,false}] = ok(inet:getopts(S1, [active])),
941    ok = inet:setopts(S1, [{active,N}]),
942    ok = inet:setopts(S1, [{active,true}]),
943    [{active,true}] = ok(inet:getopts(S1, [active])),
944    receive
945        _ -> exit({error,active_n})
946    after
947        0 ->
948            ok
949    end,
950    ok = inet:setopts(S1, [{active,N}]),
951    ok = inet:setopts(S1, [{active,once}]),
952    [{active,once}] = ok(inet:getopts(S1, [active])),
953    receive
954        _ -> exit({error,active_n})
955    after
956        0 ->
957            ok
958    end,
959    {error,einval} = inet:setopts(S1, [{active,32768}]),
960    ok = inet:setopts(S1, [{active,false}]),
961    [{active,false}] = ok(inet:getopts(S1, [active])),
962    ok = gen_sctp:listen(S1, true),
963    S1Port = ok(inet:port(S1)),
964    S2 = ok(gen_sctp:open(0, [{active,false}])),
965    Assoc = ok(gen_sctp:connect(S2, "localhost", S1Port, [])),
966    ok = inet:setopts(S1, [{active,N}]),
967    active_n_flush_connect_msgs(S1),
968    active_n_send_loop(N, S2, Assoc, S1),
969    S3 = ok(gen_sctp:open([{active,0}])),
970    receive
971        {sctp_passive,S3} ->
972            [{active,false}] = ok(inet:getopts(S3, [active]))
973    after
974        5000 ->
975            exit({error,udp_passive})
976    end,
977    ok = gen_sctp:close(S3),
978    ok = gen_sctp:close(S2),
979    ok = gen_sctp:close(S1),
980    ok.
981
982
983%% There is no way to know how many addresses this host has,
984%% and if there is "too many" (more then N = 3), then the
985%% socket may already be passive. In this case the send-
986%% loop will fail.
987%% So, if we get a passive-message here, we just give up (=skip).
988active_n_flush_connect_msgs(Sock) ->
989    %% This seems only to be needed on certain platforms
990    active_n_flush_connect_msgs(os:type(), Sock).
991
992active_n_flush_connect_msgs(_, Sock) ->
993    do_active_n_flush_connect_msgs(Sock).
994%% active_n_flush_connect_msgs({unix, freebsd}, Sock) ->
995%%     do_active_n_flush_connect_msgs(Sock);
996%% active_n_flush_connect_msgs(_, _) ->
997%%     ok.
998
999do_active_n_flush_connect_msgs(Sock) ->
1000    receive
1001        {sctp_passive, Sock} ->
1002            ?P("connect-flush-loop -> premature passive"),
1003            ?SKIPT("Too many addresses (premature passive)");
1004
1005        {sctp, Sock,
1006         _FromIP, _FromPort,
1007         {[], #sctp_assoc_change{state = comm_up}}} ->
1008            ?P("connect-flush-loop -> "
1009               "connect message discard - assoc change : comm-up"),
1010            ok = inet:setopts(Sock, [{active, 1}]),
1011            do_active_n_flush_connect_msgs(Sock);
1012
1013        {sctp, Sock,
1014         _FromIP, _FromPort,
1015         {[], #sctp_paddr_change{state    = addr_confirmed,
1016                                 addr     = Addr,
1017                                 error    = Error,
1018                                 assoc_id = AID}}} ->
1019            ?P("connect-flush-loop -> "
1020               "connect message discard - paddr change : addr-confirmed:"
1021               "~n   Addr:    ~p"
1022               "~n   Error:   ~p"
1023               "~n   AssocID: ~p", [Addr, Error, AID]),
1024            ok = inet:setopts(Sock, [{active, 1}]),
1025            do_active_n_flush_connect_msgs(Sock)
1026
1027    after 5000 ->
1028            ok
1029    end.
1030
1031active_n_send_loop(Count, SrcSock, SndAssoc, DstSock) ->
1032    active_n_send_loop(0, Count, SrcSock, SndAssoc, DstSock).
1033
1034active_n_send_loop(Count, Count, _SndSock, _SndAssoc, RcvSock) ->
1035    ?P("send-loop -> we are done - wait for passive"),
1036    receive
1037        {sctp_passive, RcvSock} ->
1038            ?P("received passive"),
1039            ok
1040    after
1041        5000 ->
1042            ?P("UNEXPECTED TIMEOUT: "
1043               "~n   Message Queue:    ~p"
1044               "~n   Active:           ~p",
1045               [process_info(self(), messages),
1046                inet:getopts(RcvSock, [active])]),
1047            exit({error, timeout})
1048    end;
1049
1050active_n_send_loop(Sent, Count, SndSock, SndAssoc, RcvSock) ->
1051    Msg = list_to_binary("message " ++ integer_to_list(Sent+1)),
1052    ?P("send-loop(~w,~w) -> send message (on ~p)", [Sent, Count, SndSock]),
1053    ok = gen_sctp:send(SndSock, SndAssoc, 0, Msg),
1054    receive
1055        {sctp, RcvSock, FromIP, FromPort, {[SR], Msg}}
1056        when is_record(SR, sctp_sndrcvinfo) ->
1057            ?P("send-loop(~w,~w) -> "
1058               "recv (expected) data message (on ~p):"
1059               "~n   Msg:  ~p"
1060               "~n   From: ~p, ~p",
1061               [Sent, Count,
1062                RcvSock, Msg, FromIP, FromPort]),
1063            active_n_send_loop(Sent+1, Count, SndSock, SndAssoc, RcvSock);
1064
1065        {sctp, RcvSock, _FromIP, _FromPort, {_AncData, _Data}} ->
1066            %% ignore non-data messages
1067            %% we should not get any here because of the flush loop,
1068            %% but just in case...
1069            ?P("send-loop(~w,~w) -> "
1070               "ignore non-data messages (on ~p):"
1071               "~n   From:    ~p:~p"
1072               "~n   AncData: ~p"
1073               "~n   Data:    ~p",
1074               [Sent, Count,
1075                RcvSock, _FromIP, _FromPort, _AncData, _Data]),
1076
1077            %% It may be too late to update here,
1078            %% the socket may already have gone passive
1079            %% and generated a passive message!
1080
1081            ok = inet:setopts(RcvSock, [{active, 1}]),
1082
1083            active_n_send_loop(Sent, Count, SndSock, SndAssoc, RcvSock);
1084
1085        Other ->
1086            ?P("send-loop(~w,~w) -> "
1087               "UNEXPECTED: "
1088               "~n   Other:     ~p"
1089               "~n   Send Sock: ~p"
1090               "~n   Recv Sock: ~p", [Sent, Count,
1091                                      Other, SndSock, RcvSock]),
1092            exit({unexpected, Other})
1093    after
1094        5000 ->
1095            exit({error,timeout})
1096    end.
1097
1098%% Hello world stream socket.
1099basic_stream(Config) when is_list(Config) ->
1100    {ok,S} = gen_sctp:open([{type,stream}]),
1101    ok = gen_sctp:listen(S, true),
1102    ok =
1103	do_from_other_process(
1104	  fun () -> gen_sctp:listen(S, 10) end),
1105    ok = gen_sctp:close(S),
1106    ok.
1107
1108%% Minimal data transfer.
1109xfer_stream_min(Config) when is_list(Config) ->
1110    {_, OSName} = os:type(),
1111    Stream = 0,
1112    Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
1113    Loopback = {127,0,0,1},
1114    {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
1115    ?LOGVAR(Sb),
1116    {ok,Pb} = inet:port(Sb),
1117    ?LOGVAR(Pb),
1118    ok = gen_sctp:listen(Sb, true),
1119
1120    {ok,Sa} = gen_sctp:open([{type,stream}]),
1121    ?LOGVAR(Sa),
1122    {ok,Pa} = inet:port(Sa),
1123    ?LOGVAR(Pa),
1124    #sctp_assoc_change{state=comm_up,
1125		       error=0,
1126		       outbound_streams=SaOutboundStreams,
1127		       inbound_streams=SaInboundStreams,
1128		       assoc_id=SaAssocId_X} =
1129	log_ok(gen_sctp:connect(Sa, Loopback, Pb, [])),
1130    ?LOGVAR(SaAssocId_X),
1131    [{_,#sctp_paddrinfo{assoc_id=SaAssocId,state=active}}] =
1132	log_ok(inet:getopts(Sa, [{sctp_get_peer_addr_info,
1133				  #sctp_paddrinfo{address={Loopback,Pb}}}])),
1134    ?LOGVAR(SaAssocId),
1135    match_unless_solaris(SaAssocId_X, SaAssocId),
1136
1137    {SbOutboundStreams,SbInboundStreams,SbAssocId} =
1138	case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
1139	    {Loopback,Pa,
1140	     #sctp_assoc_change{state=comm_up,
1141				error=0,
1142				outbound_streams=OS,
1143				inbound_streams=IS,
1144				assoc_id=AI}} ->
1145		{OS,IS,AI};
1146	    {Loopback,Pa,
1147	     #sctp_paddr_change{state=addr_confirmed,
1148				addr={Loopback,Pa},
1149				error=0,
1150				assoc_id=AI}} ->
1151		{Loopback,Pa,
1152		 #sctp_assoc_change{state=comm_up,
1153				    error=0,
1154				    outbound_streams=OS,
1155				    inbound_streams=IS,
1156				    assoc_id=AI}} =
1157		    recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
1158		{OS,IS,AI}
1159	end,
1160    ?LOGVAR(SbAssocId),
1161    SaOutboundStreams = SbInboundStreams,
1162    ?LOGVAR(SaOutboundStreams),
1163    SbOutboundStreams = SaInboundStreams,
1164    ?LOGVAR(SbOutboundStreams),
1165    ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
1166    case log_ok(gen_sctp:recv(Sb, infinity)) of
1167	{Loopback,
1168	 Pa,
1169	 [#sctp_sndrcvinfo{stream   = Stream,
1170			   assoc_id = SbAssocId}],
1171	 Data} ->
1172            ?P("[1] received expected data with ancillary data => done"),
1173            ok;
1174
1175	{Loopback,
1176	 Pa,
1177         [],
1178	 #sctp_paddr_change{addr     = {Loopback,_},
1179			    state    = addr_available,
1180			    error    = 0,
1181			    assoc_id = SbAssocId}} ->
1182            ?P("[2] received paddr change => recv again"),
1183            Res2 = log_ok(gen_sctp:recv(Sb, infinity)),
1184            ?P("[2] recv ok => "
1185               "~n   ~p", [Res2]),
1186	    {Loopback,
1187	     Pa,
1188	     [#sctp_sndrcvinfo{stream   = Stream,
1189			       assoc_id = SbAssocId}],
1190	     Data} = Res2,
1191            ?P("[2] received expected data with ancillary data => done"),
1192            Res2;
1193
1194	{Loopback,
1195	 Pa,
1196	 [#sctp_sndrcvinfo{stream    = Stream,
1197			   assoc_id  = SbAssocId}],
1198	 #sctp_paddr_change{addr     = {Loopback,_},
1199			    state    = addr_confirmed,
1200			    error    = 0,
1201			    assoc_id = SbAssocId}} ->
1202            ?P("[3] received paddr change with ancillary data => recv again"),
1203            Res3 = log_ok(gen_sctp:recv(Sb, infinity)),
1204            ?P("[3] recv ok => "
1205               "~n   ~p", [Res3]),
1206	    {Loopback,
1207	     Pa,
1208	     [#sctp_sndrcvinfo{stream   = Stream,
1209			       assoc_id = SbAssocId}],
1210	     Data} = Res3,
1211            ?P("[3] received expected data with ancillary data => done"),
1212            Res3;
1213
1214        %% It seems that on FreeBSD (for instance) we don't get any
1215        %% AncData with this.
1216	{Loopback,
1217	 Pa,
1218	 [],
1219	 #sctp_paddr_change{addr     = {Loopback,_},
1220			    state    = addr_confirmed,
1221			    error    = 0,
1222			    assoc_id = SbAssocId}} when (OSName =:= freebsd) ->
1223            ?P("[4] received paddr change without ancillary data => "
1224               "recv again"),
1225            Res4 = log_ok(gen_sctp:recv(Sb, infinity)),
1226            ?P("[4] recv ok => "
1227               "~n   ~p", [Res4]),
1228	    {Loopback,
1229	     Pa,
1230	     [#sctp_sndrcvinfo{stream   = Stream,
1231                               assoc_id = SbAssocId}],
1232	     Data} = Res4,
1233            ?P("[4] received expected data with ancillary data => done"),
1234            Res4;
1235
1236        {FromIPX, FromPortX, AncDataX, DataX} = Other1 ->
1237            ?P("UNEXPECTED: "
1238               "~n   FromIP:   ~p"
1239               "~n   FromPort: ~p"
1240               "~n   AncData:  ~p"
1241               "~n   Data:     ~p"
1242               "~nwhen"
1243               "~n   Loopback: ~p"
1244               "~n   Pa:       ~p",
1245               [FromIPX, FromPortX, AncDataX, DataX, Loopback, Pa]),
1246            exit({unexpected, Other1});
1247        Other2 ->
1248            ?P("UNEXPECTED: "
1249               "~n   Other:    ~p"
1250               "~nwhen"
1251               "~n   Loopback: ~p"
1252               "~n   Pa:       ~p",
1253               [Other2, Loopback, Pa]),
1254            exit({unexpected, Other2})
1255    end,
1256    ok =
1257	do_from_other_process(
1258	  fun () -> gen_sctp:send(Sb, SbAssocId, 0, Data) end),
1259    case log_ok(gen_sctp:recv(Sa, infinity)) of
1260	{Loopback,Pb,
1261	 [#sctp_sndrcvinfo{stream=Stream,
1262			   assoc_id=SaAssocId}],
1263	 Data} -> ok;
1264	Event1 ->
1265	    {Loopback,Pb,
1266	     #sctp_paddr_change{state=addr_confirmed,
1267				addr={_,Pb},
1268				error=0,
1269				assoc_id=SaAssocId}} =
1270		recv_event(Event1),
1271	    {Loopback,Pb,
1272	     [#sctp_sndrcvinfo{stream=Stream,
1273			       assoc_id=SaAssocId}],
1274	     Data} =
1275		log_ok(gen_sctp:recv(Sa, infinity))
1276    end,
1277    ok = gen_sctp:close(Sa),
1278    {Loopback,Pa,
1279     #sctp_shutdown_event{assoc_id=SbAssocId}} =
1280	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
1281    {Loopback,Pa,
1282     #sctp_assoc_change{state=shutdown_comp,
1283			error=0,
1284			assoc_id=SbAssocId}} =
1285	recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
1286    ok = gen_sctp:close(Sb),
1287
1288    receive
1289	Msg -> ct:fail({received,Msg})
1290    after 17 -> ok
1291    end,
1292    ok.
1293
1294
1295
1296do_from_other_process(Fun) ->
1297    Parent = self(),
1298    Ref = make_ref(),
1299    Child =
1300	spawn(fun () ->
1301		      try Fun() of
1302			  Result ->
1303			      Parent ! {Ref,Result}
1304		      catch
1305			  Class:Reason:Stacktrace ->
1306			      Parent ! {Ref,Class,Reason,Stacktrace}
1307		      end
1308	      end),
1309    Mref = erlang:monitor(process, Child),
1310    receive
1311	{Ref,Result} ->
1312	    receive {'DOWN',Mref,_,_,_} -> Result end;
1313	{Ref,Class,Reason,Stacktrace} ->
1314	    receive {'DOWN',Mref,_,_,_} ->
1315		    erlang:raise(Class, Reason, Stacktrace)
1316	    end;
1317	{'DOWN',Mref,_,_,Reason} ->
1318	    erlang:exit(Reason)
1319    end.
1320
1321
1322%% Peel off an SCTP stream socket ({active,once}).
1323
1324peeloff_active_once(Config) ->
1325    peeloff(Config, [{active,once}]).
1326
1327%% Peel off an SCTP stream socket ({active,true}).
1328
1329peeloff_active_true(Config) ->
1330    peeloff(Config, [{active,true}]).
1331
1332%% Peel off an SCTP stream socket ({active,N}).
1333
1334peeloff_active_n(Config) ->
1335    peeloff(Config, [{active,1}]).
1336
1337peeloff(Config, SockOpts) when is_list(Config) ->
1338    Addr = {127,0,0,1},
1339    Stream = 0,
1340    Timeout = 333,
1341    StartTime = timestamp(),
1342    S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
1343    ?LOGVAR(S1),
1344    P1 = socket_call(S1, get_port),
1345    ?LOGVAR(P1),
1346    Socket1 = socket_call(S1, get_socket),
1347    ?LOGVAR(Socket1),
1348    socket_call(S1, {listen,true}),
1349    S2 = socket_open([{ifaddr,Addr}|SockOpts], Timeout),
1350    ?LOGVAR(S2),
1351    P2 = socket_call(S2, get_port),
1352    ?LOGVAR(P2),
1353    Socket2 = socket_call(S2, get_socket),
1354    ?LOGVAR(Socket2),
1355    %%
1356    socket_call(S2, {connect_init,Addr,P1,[]}),
1357    S2Ai =
1358	receive
1359	    {S2,{Addr,P1,
1360		 #sctp_assoc_change{
1361		    state=comm_up,
1362		    assoc_id=AssocId2}}} -> AssocId2
1363	after Timeout ->
1364		socket_bailout([S1,S2], StartTime)
1365	end,
1366    ?LOGVAR(S2Ai),
1367    S1Ai =
1368	receive
1369	    {S1,{Addr,P2,
1370		 #sctp_assoc_change{
1371		    state=comm_up,
1372		    assoc_id=AssocId1}}} -> AssocId1
1373	after Timeout ->
1374		socket_bailout([S1,S2], StartTime)
1375	end,
1376    ?LOGVAR(S1Ai),
1377    %%
1378    socket_call(S2, {send,S2Ai,Stream,<<"Number one">>}),
1379    receive
1380	{S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
1381    after Timeout ->
1382	    socket_bailout([S1,S2], StartTime)
1383    end,
1384    socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
1385    receive
1386	{S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
1387    after Timeout ->
1388	    socket_bailout([S1,S2], StartTime)
1389    end,
1390    %%
1391    S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout),
1392    ?LOGVAR(S3),
1393    P3_X = socket_call(S3, get_port),
1394    ?LOGVAR(P3_X),
1395    P3 = case P3_X of 0 -> P1; _ -> P3_X end,
1396    [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] =
1397	socket_call(S3,
1398		    {getopts,[{sctp_get_peer_addr_info,
1399			       #sctp_paddrinfo{address={Addr,P2}}}]}),
1400    %%S3Ai = S1Ai,
1401    ?LOGVAR(S3Ai),
1402    %%
1403    socket_call(S3, {send,S3Ai,Stream,<<"Number three">>}),
1404    receive
1405	{S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
1406    after Timeout ->
1407	    socket_bailout([S1,S2,S3], StartTime)
1408    end,
1409    socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
1410    receive
1411	{S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
1412    after Timeout ->
1413	    socket_bailout([S1,S2,S3], StartTime)
1414    end,
1415    %%
1416    inet:i(sctp),
1417    socket_close_verbose(S1, StartTime),
1418    socket_close_verbose(S2, StartTime),
1419    receive
1420	{S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
1421	    match_unless_solaris(S3Ai, S3Ai_X)
1422    after Timeout ->
1423	    socket_bailout([S3], StartTime)
1424    end,
1425    receive
1426	{S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
1427					assoc_id=S3Ai}}} -> ok
1428    after Timeout ->
1429	    socket_bailout([S3], StartTime)
1430    end,
1431    socket_close_verbose(S3, StartTime),
1432    [] = flush(),
1433    ok.
1434
1435
1436
1437%% Check sndbuf and recbuf behaviour.
1438buffers(Config) when is_list(Config) ->
1439    Limit = 4096,
1440    Addr = {127,0,0,1},
1441    Stream = 1,
1442    Timeout = 3333,
1443    StartTime = timestamp(),
1444    S1 = socket_open([{ip,Addr}], Timeout),
1445    ?LOGVAR(S1),
1446    P1 = socket_call(S1, get_port),
1447    ?LOGVAR(P1),
1448    ok = socket_call(S1, {listen,true}),
1449    S2 = socket_open([{ip,Addr}], Timeout),
1450    ?LOGVAR(S2),
1451    P2 = socket_call(S2, get_port),
1452    ?LOGVAR(P2),
1453    %%
1454    socket_call(S2, {connect_init,Addr,P1,[]}),
1455    S2Ai =
1456	receive
1457	    {S2,{Addr,P1,
1458		 #sctp_assoc_change{
1459		    state=comm_up,
1460		    assoc_id=AssocId2}}} -> AssocId2
1461	after Timeout ->
1462		socket_bailout([S1,S2], StartTime)
1463	end,
1464    S1Ai =
1465	receive
1466	    {S1,{Addr,P2,
1467		 #sctp_assoc_change{
1468		    state=comm_up,
1469		    assoc_id=AssocId1}}} -> AssocId1
1470	after Timeout ->
1471		socket_bailout([S1,S2], StartTime)
1472	end,
1473    %%
1474    socket_call(S1, {setopts,[{recbuf,Limit}]}),
1475    Recbuf =
1476	case socket_call(S1, {getopts,[recbuf]}) of
1477	    [{recbuf,RB1}] when RB1 >= Limit -> RB1
1478	end,
1479    Data = mk_data(Recbuf+Limit),
1480    socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}),
1481    socket_call(S2, {send,S2Ai,Stream,Data}),
1482    receive
1483	{S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
1484    after Timeout ->
1485	    socket_bailout([S1,S2], StartTime)
1486    end,
1487    %%
1488    socket_close_verbose(S1, StartTime),
1489    receive
1490	{S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
1491    after Timeout ->
1492	    socket_bailout([S2], StartTime)
1493    end,
1494    receive
1495	{S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
1496					assoc_id=S2Ai}}} -> ok
1497    after Timeout ->
1498	    socket_bailout([S2], StartTime)
1499    end,
1500    socket_close_verbose(S2, StartTime),
1501    [] = flush(),
1502    ok.
1503
1504mk_data(Bytes) ->
1505    mk_data(0, Bytes, <<>>).
1506%%
1507mk_data(N, Bytes, Bin) when N < Bytes ->
1508    mk_data(N+4, Bytes, <<Bin/binary,N:32>>);
1509mk_data(_, _, Bin) ->
1510    Bin.
1511
1512
1513
1514%% Test opening a multihoming ipv4 socket.
1515open_multihoming_ipv4_socket(Config) when is_list(Config) ->
1516    ?P("get addrs by family (inet)"),
1517    case get_addrs_by_family(inet, 2) of
1518	{ok, [Addr1, Addr2]} ->
1519            ?P("got addrs: "
1520               "~n      Addr1: ~p"
1521               "~n      Addr2: ~p", [Addr1, Addr2]),
1522	    do_open_and_connect([Addr1, Addr2], Addr1);
1523	{error, Reason} ->
1524            ?P("failed get addrs: "
1525               "~n      ~p", [Reason]),
1526	    {skip, Reason}
1527    end.
1528
1529%% This test is mostly aimed to indicate whether host has a
1530%% non-working ipv6 setup.  Test opening a unihoming (non-multihoming)
1531%% ipv6 socket.
1532open_unihoming_ipv6_socket(Config) when is_list(Config) ->
1533    case get_addrs_by_family(inet6, 1) of
1534	{ok, [Addr]} ->
1535	    do_open_and_connect([Addr], Addr);
1536	{error, Reason} ->
1537	    {skip, Reason}
1538    end.
1539
1540
1541%% Test opening a multihoming ipv6 socket.
1542open_multihoming_ipv6_socket(Config) when is_list(Config) ->
1543    case get_addrs_by_family(inet6, 2) of
1544	{ok, [Addr1, Addr2]} ->
1545	    do_open_and_connect([Addr1, Addr2], Addr1);
1546	{error, Reason} ->
1547	    {skip, Reason}
1548    end.
1549
1550%% Test opening a multihoming ipv6 socket with ipv4 and ipv6 addresses.
1551open_multihoming_ipv4_and_ipv6_socket(Config) when is_list(Config) ->
1552    case get_addrs_by_family(inet_and_inet6, 2) of
1553	{ok, [[InetAddr1, InetAddr2], [Inet6Addr1, Inet6Addr2]]} ->
1554	    %% Connect to the first address to test bind
1555	    do_open_and_connect([InetAddr1, Inet6Addr1, InetAddr2],
1556				InetAddr1),
1557	    do_open_and_connect([Inet6Addr1, InetAddr1],
1558				Inet6Addr1),
1559
1560	    %% Connect an address, not the first,
1561	    %% to test sctp_bindx
1562	    do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
1563				Inet6Addr2),
1564	    do_open_and_connect([Inet6Addr1, Inet6Addr2, InetAddr1],
1565				InetAddr1);
1566	{error, Reason} ->
1567	    {skip, Reason}
1568    end.
1569
1570%% Test inet:socknames/peernames on unihoming IPv4 sockets.
1571names_unihoming_ipv4(Config) when is_list(Config) ->
1572    do_names(Config, inet, 1).
1573
1574%% Test inet:socknames/peernames on unihoming IPv6 sockets.
1575names_unihoming_ipv6(Config) when is_list(Config) ->
1576    do_names(Config, inet6, 1).
1577
1578%% Test inet:socknames/peernames on multihoming IPv4 sockets.
1579names_multihoming_ipv4(Config) when is_list(Config) ->
1580    do_names(Config, inet, 2).
1581
1582%% Test inet:socknames/peernames on multihoming IPv6 sockets.
1583names_multihoming_ipv6(Config) when is_list(Config) ->
1584    do_names(Config, inet6, 2).
1585
1586
1587
1588do_names(_, FamilySpec, AddressCount) ->
1589    Fun =
1590	fun (ServerSocket, _, ServerAssoc, ClientSocket, _, ClientAssoc) ->
1591		ServerSocknamesNoassoc =
1592		    lists:sort(ok(inet:socknames(ServerSocket))),
1593		?LOGVAR(ServerSocknamesNoassoc),
1594		ServerSocknames =
1595		    lists:sort(ok(inet:socknames(ServerSocket, ServerAssoc))),
1596		?LOGVAR(ServerSocknames),
1597		[_|_] =
1598		    ordsets:intersection
1599		      (ServerSocknamesNoassoc, ServerSocknames),
1600		ClientSocknamesNoassoc =
1601		    lists:sort(ok(inet:socknames(ClientSocket))),
1602		?LOGVAR(ClientSocknamesNoassoc),
1603		ClientSocknames =
1604		    lists:sort(ok(inet:socknames(ClientSocket, ClientAssoc))),
1605		?LOGVAR(ClientSocknames),
1606		[_|_] =
1607		    ordsets:intersection
1608		      (ClientSocknamesNoassoc, ClientSocknames),
1609		err([einval,enotconn], inet:peernames(ServerSocket)),
1610		ServerPeernames =
1611		    lists:sort(ok(inet:peernames(ServerSocket, ServerAssoc))),
1612		?LOGVAR(ServerPeernames),
1613		err([einval,enotconn], inet:peernames(ClientSocket)),
1614		ClientPeernames =
1615		    lists:sort(ok(inet:peernames(ClientSocket, ClientAssoc))),
1616		?LOGVAR(ClientPeernames),
1617		ServerSocknames = ClientPeernames,
1618		ClientSocknames = ServerPeernames,
1619		{ok,Socket} =
1620		    gen_sctp:peeloff(ServerSocket, ServerAssoc),
1621		SocknamesNoassoc =
1622		    lists:sort(ok(inet:socknames(Socket))),
1623		?LOGVAR(SocknamesNoassoc),
1624		Socknames =
1625		    lists:sort(ok(inet:socknames(Socket, ServerAssoc))),
1626		?LOGVAR(Socknames),
1627		true =
1628		    ordsets:is_subset(SocknamesNoassoc, Socknames),
1629		Peernames =
1630		    lists:sort(ok(inet:peernames(Socket, ServerAssoc))),
1631		?LOGVAR(Peernames),
1632		ok = gen_sctp:close(Socket),
1633		Socknames = ClientPeernames,
1634		ClientSocknames = Peernames,
1635		ok
1636	end,
1637    case get_addrs_by_family(FamilySpec, AddressCount) of
1638	{ok, Addresses} when length(Addresses) =:= AddressCount ->
1639	    do_open_and_connect(Addresses, hd(Addresses), Fun);
1640	{error, Reason} ->
1641	    {skip, Reason}
1642    end.
1643
1644
1645
1646get_addrs_by_family(Family, NumAddrs) ->
1647    case os:type() of
1648	{unix,linux} ->
1649	    get_addrs_by_family_aux(Family, NumAddrs);
1650	{unix,freebsd} ->
1651	    get_addrs_by_family_aux(Family, NumAddrs);
1652	{unix,sunos} ->
1653	    case get_addrs_by_family_aux(Family, NumAddrs) of
1654		{ok, [InetAddrs, Inet6Addrs]} when Family =:= inet_and_inet6 ->
1655		    %% Man page for sctp_bindx on Solaris says: "If sock is an
1656		    %% Internet Protocol Version 6 (IPv6) socket, addrs should
1657		    %% be an array of sockaddr_in6 structures containing IPv6
1658		    %% or IPv4-mapped IPv6 addresses."
1659		    {ok, [ipv4_map_addrs(InetAddrs), Inet6Addrs]};
1660		{ok, Addrs} ->
1661		    {ok, Addrs};
1662		{error, Reason} ->
1663		    {error, Reason}
1664	    end;
1665	Os ->
1666	    Reason = if Family =:= inet_and_inet6 ->
1667			     ?F("Mixing ipv4 and ipv6 addresses for "
1668			       " multihoming has not been verified on ~p",
1669                                [Os]);
1670			true ->
1671			     ?F("Multihoming for ~p has not been verified "
1672                                "on ~p", [Family, Os])
1673		     end,
1674	    {error, Reason}
1675    end.
1676
1677get_addrs_by_family_aux(Family, NumAddrs) when Family =:= inet;
1678					       Family =:= inet6 ->
1679    case inet:getaddr(localhost, Family) of
1680	{error, eafnosupport = Reason} ->
1681            ?P("failed get (~w) addrs for localhost: ~p", [Family, Reason]),
1682	    {error, ?F("No support for ~p (~p)", [Family, Reason])};
1683        {error, nxdomain = Reason} ->
1684            ?P("failed get (~w) addrs for localhost: ~p", [Family, Reason]),
1685	    {error, ?F("No support for ~p (~p)", [Family, Reason])};
1686	{ok, _} ->
1687            ?P("got addr for localhost (ignored)"),
1688	    IfAddrs = ok(inet:getifaddrs()),
1689            ?P("IfAddrs: ~p", [IfAddrs]),
1690	    case filter_addrs_by_family(IfAddrs, Family) of
1691		Addrs when length(Addrs) >= NumAddrs ->
1692		    {ok, lists:sublist(Addrs, NumAddrs)};
1693		[] ->
1694		    {error, ?F("Need ~p ~p address(es) found none~n",
1695                               [NumAddrs, Family])};
1696		Addrs ->
1697		    {error,
1698		     ?F("Need ~p ~p address(es) found only ~p: ~p~n",
1699                        [NumAddrs, Family, length(Addrs), Addrs])}
1700	    end
1701    end;
1702get_addrs_by_family_aux(inet_and_inet6, NumAddrs) ->
1703    try {ok, [case get_addrs_by_family_aux(Family, NumAddrs) of
1704                  {ok, Addrs}     -> Addrs;
1705                  {error, Reason} -> throw({error, Reason})
1706              end || Family <- [inet, inet6]]}
1707    catch
1708        throw:{error, _} = ERROR ->
1709            ERROR
1710    end.
1711
1712filter_addrs_by_family(IfAddrs, Family) ->
1713    lists:flatten([[Addr || {addr, Addr} <- Info,
1714			    is_good_addr(Addr, Family)]
1715		   || {_IfName, Info} <- IfAddrs]).
1716
1717is_good_addr(Addr, inet) when tuple_size(Addr) =:= 4 ->
1718    true;
1719is_good_addr({0,0,0,0,0,16#ffff,_,_}, inet6) ->
1720    false; %% ipv4 mapped
1721is_good_addr({16#fe80,_,_,_,_,_,_,_}, inet6) ->
1722    false; %% link-local
1723is_good_addr(Addr, inet6) when tuple_size(Addr) =:= 8 ->
1724    true;
1725is_good_addr(_Addr, _Family) ->
1726    false.
1727
1728ipv4_map_addrs(InetAddrs) ->
1729    [begin
1730	 <<AB:16>> = <<A,B>>,
1731	 <<CD:16>> = <<C,D>>,
1732	 {0, 0, 0, 0, 0, 16#ffff, AB, CD}
1733     end || {A,B,C,D} <- InetAddrs].
1734
1735do_open_and_connect(ServerAddresses, AddressToConnectTo) ->
1736    Fun = fun (_, _, _, _, _, _) -> ok end,
1737    do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun).
1738%%
1739do_open_and_connect(ServerAddresses, AddressToConnectTo, Fun) ->
1740    {ServerFamily, ServerOpts} = get_family_by_addrs(ServerAddresses),
1741    io:format("Serving ~p addresses: ~p~n",
1742	      [ServerFamily, ServerAddresses]),
1743    S1 = ok(gen_sctp:open(0, [{ip,Addr} || Addr <- ServerAddresses] ++
1744			      [ServerFamily|ServerOpts])),
1745    ok = gen_sctp:listen(S1, true),
1746    P1 = ok(inet:port(S1)),
1747    ClientFamily = get_family_by_addr(AddressToConnectTo),
1748    io:format("Connecting to ~p ~p~n",
1749	      [ClientFamily, AddressToConnectTo]),
1750    ClientOpts =
1751	[ClientFamily |
1752	 case ClientFamily of
1753	     inet6 ->
1754		 [{ipv6_v6only,true}];
1755	     _ ->
1756		 []
1757	 end],
1758    S2 = ok(gen_sctp:open(0, ClientOpts)),
1759    log(open),
1760    %% Verify client can connect
1761    #sctp_assoc_change{state=comm_up} = S2Assoc =
1762	ok(gen_sctp:connect(S2, AddressToConnectTo, P1, [])),
1763    log(comm_up),
1764    %% verify server side also receives comm_up from client
1765    S1Assoc = recv_comm_up_eventually(S1),
1766    Result = Fun(S1, ServerFamily, S1Assoc, S2, ClientFamily, S2Assoc),
1767    ok = gen_sctp:close(S2),
1768    ok = gen_sctp:close(S1),
1769    Result.
1770
1771%% If at least one of the addresses is an ipv6 address, return inet6, else inet.
1772get_family_by_addrs(Addresses) ->
1773    case lists:usort([get_family_by_addr(Addr) || Addr <- Addresses]) of
1774	[inet, inet6] -> {inet6, [{ipv6_v6only, false}]};
1775	[inet]        -> {inet,  []};
1776	[inet6]       -> {inet6, []}
1777    end.
1778
1779get_family_by_addr(Addr) when tuple_size(Addr) =:= 4 -> inet;
1780get_family_by_addr(Addr) when tuple_size(Addr) =:= 8 -> inet6.
1781
1782recv_comm_up_eventually(S) ->
1783    case ok(gen_sctp:recv(S)) of
1784	{_Addr, _Port, _Info,
1785	 #sctp_assoc_change{state=comm_up} = Assoc} ->
1786	    Assoc;
1787	{_Addr, _Port, _Info, _OtherSctpMsg} = Msg ->
1788	    log({unexpected,Msg}),
1789	    recv_comm_up_eventually(S)
1790    end.
1791
1792
1793%%
1794recv_close(Config) when is_list(Config) ->
1795    ?P("create server socket (and listen)"),
1796    {ok, S} = gen_sctp:open(),
1797    gen_sctp:listen(S, true),
1798    {ok, SPort} = inet:port(S),
1799
1800    ?P("create client socket (and connect)"),
1801    {ok, C} = gen_sctp:open(),
1802    {ok, _} = gen_sctp:connect(C, localhost, SPort, []),
1803
1804    TC = self(),
1805    RECV = fun() ->
1806                   ?P("try setup recv(s)"),
1807                   ok = recv_close_setup_recv(S),
1808                   ?P("announce ready"),
1809                   TC ! {self(), ready},
1810                   ?P("try data recv"),
1811                   Res = gen_sctp:recv(S),
1812                   ?P("recv res: "
1813                      "~n   ~p", [Res]),
1814                   exit(Res)
1815           end,
1816    ?P("spawn reader - then await reader ready"),
1817    {Pid, MRef} = spawn_monitor(RECV),
1818    receive
1819        {'DOWN', MRef, process, Pid, PreReason} ->
1820            %% Make sure it does not die for some other reason...
1821            ?P("unexpected reader termination:"
1822               "~n   ~p", [PreReason]),
1823            (catch gen_sctp:close(S)),
1824            (catch gen_sctp:close(C)),
1825            ?line ct:fail("Unexpected pre close from reader (~p): ~p",
1826                          [Pid, PreReason]);
1827        {Pid, ready} ->
1828            ?P("reader ready"),
1829            ok
1830    after 30000 -> % Just in case...
1831            %% This is **extreme**, but there is no way to know
1832            %% how long it will take to iterate through all the
1833            %% addresses of a host...
1834            ?P("reader ready timeout"),
1835            (catch gen_sctp:close(S)),
1836            (catch gen_sctp:close(C)),
1837            ?line ct:fail("Unexpected pre close timeout (~p)", [Pid])
1838    end,
1839
1840    ?P("\"ensure\" reader reading..."),
1841    receive
1842        Any ->
1843            ?P("Received unexpected message: "
1844               "~n   ~p", [Any]),
1845            (catch gen_sctp:close(S)),
1846            (catch gen_sctp:close(C)),
1847            ?line ct:fail("Unexpected message: ~p", [Any])
1848    after 5000 ->
1849            ok
1850    end,
1851
1852    ?P("close server socket"),
1853    ok = gen_sctp:close(S),
1854    ?P("await reader termination"),
1855    receive
1856        {'DOWN', MRef, process, Pid, {error, closed}} ->
1857            ?P("expected reader termination result"),
1858            (catch gen_sctp:close(C)),
1859            ok;
1860        {'DOWN', MRef, process, Pid, PostReason} ->
1861            ?P("unexpected reader termination: "
1862               "~n   ~p", [PostReason]),
1863            (catch gen_sctp:close(C)),
1864            ?line ct:fail("Unexpected post close from reader (~p): ~p",
1865                          [Pid, PostReason])
1866    after 5000 ->
1867            ?P("unexpected reader termination timeout"),
1868            demonitor(MRef, [flush]),
1869            (catch gen_sctp:close(C)),
1870            exit(Pid, kill),
1871            ?line ct:fail("Reader (~p) termination timeout", [Pid])
1872    end,
1873    ?P("close client socket"),
1874    (catch gen_sctp:close(C)),
1875    ?P("done"),
1876    ok.
1877
1878
1879recv_close_setup_recv(S) ->
1880    recv_close_setup_recv(S, 1).
1881
1882recv_close_setup_recv(S, N) ->
1883    ?P("try setup recv ~w", [N]),
1884    case gen_sctp:recv(S, 5000) of
1885        {ok, {Addr,
1886              Port,
1887              _AncData,
1888              Data}} when is_tuple(Addr) andalso is_integer(Port) ->
1889            ?P("setup recv ~w: "
1890               "~n   ~p", [N, Data]),
1891            recv_close_setup_recv(S, N+1);
1892        {error, timeout} ->
1893            ok
1894    end.
1895
1896
1897%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1898%%% socket gen_server ultra light
1899
1900socket_open(SockOpts0, Timeout) ->
1901    SockOpts =
1902	case lists:keyfind(active,1,SockOpts0) of
1903	    false ->
1904		[{active,once}|SockOpts0];
1905	    _ ->
1906		SockOpts0
1907	end,
1908    Opts = [{type,seqpacket},binary|SockOpts],
1909    Starter =
1910	fun () ->
1911		{ok,Socket} =
1912		    gen_sctp:open(Opts),
1913		Socket
1914	end,
1915    s_start(Starter, Timeout).
1916
1917socket_peeloff(Socket, AssocId, SocketOpts, Timeout) ->
1918    Opts = [binary|SocketOpts],
1919    Starter =
1920	fun () ->
1921		{ok,NewSocket} =
1922		    gen_sctp:peeloff(Socket, AssocId),
1923		ok = inet:setopts(NewSocket, Opts),
1924		NewSocket
1925	end,
1926    s_start(Starter, Timeout).
1927
1928socket_close_verbose(S, StartTime) ->
1929    History = socket_history(socket_close(S), StartTime),
1930    io:format("socket_close ~p:~n    ~p.~n", [S,History]),
1931    History.
1932
1933socket_close(S) ->
1934    s_req(S, close).
1935
1936socket_call(S, Request) ->
1937    s_req(S, {Request}).
1938
1939%% socket_get(S, Key) ->
1940%%     s_req(S, {get,Key}).
1941
1942socket_bailout([S|Ss], StartTime) ->
1943    History = socket_history(socket_close(S), StartTime),
1944    io:format("bailout ~p:~n    ~p.~n", [S,History]),
1945    socket_bailout(Ss, StartTime);
1946socket_bailout([], _) ->
1947    io:format("flush: ~p.~n", [flush()]),
1948    ct:fail(socket_bailout).
1949
1950socket_history({State,Flush}, StartTime) ->
1951    {lists:keysort(
1952       2,
1953       lists:flatten(
1954	 [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals]
1955	  || {Key,Vals} <- gb_trees:to_list(State)])),
1956     Flush}.
1957
1958s_handler(Socket) ->
1959    fun ({listen,Listen}) ->
1960	    ok = gen_sctp:listen(Socket, Listen);
1961	(get_port) ->
1962	    ok(inet:port(Socket));
1963	(get_socket) ->
1964	    Socket;
1965	({connect_init,ConAddr,ConPort,ConOpts}) ->
1966	    ok = gen_sctp:connect_init(Socket, ConAddr, ConPort, ConOpts);
1967	({send,AssocId,Stream,Data}) ->
1968	    ok = gen_sctp:send(Socket, AssocId, Stream, Data);
1969	({send,OtherSocket,AssocId,Stream,Data}) ->
1970	    ok = gen_sctp:send(OtherSocket, AssocId, Stream, Data);
1971	({setopts,Opts}) ->
1972	    ok = inet:setopts(Socket, Opts);
1973	({getopts,Optnames}) ->
1974	    ok(inet:getopts(Socket, Optnames))
1975    end.
1976
1977s_req(S, Req) ->
1978    Mref = erlang:monitor(process, S),
1979    S ! {self(),Mref,Req},
1980    receive
1981	{'DOWN',Mref,_,_,Error} ->
1982	    exit(Error);
1983	{S,Mref,Reply} ->
1984	    erlang:demonitor(Mref, [flush]),
1985	    Reply
1986    end.
1987
1988s_start(Starter, Timeout) ->
1989    Parent = self(),
1990    Owner =
1991	spawn_link(
1992	  fun () ->
1993		  s_start(Starter(), Timeout, Parent)
1994	  end),
1995    Owner.
1996
1997s_start(Socket, Timeout, Parent) ->
1998    Handler = s_handler(Socket),
1999    try
2000	s_loop(Socket, Timeout, Parent, Handler, gb_trees:empty())
2001    catch
2002	Class:Reason:Stacktrace ->
2003	    io:format(?MODULE_STRING":socket exception ~w:~w at~n"
2004		      "~p.~n", [Class,Reason,Stacktrace]),
2005	    erlang:raise(Class, Reason, Stacktrace)
2006    end.
2007
2008s_loop(Socket, Timeout, Parent, Handler, State) ->
2009    receive
2010	{Parent,Ref,close} -> % socket_close()
2011	    erlang:send_after(Timeout, self(), {Parent,Ref,exit}),
2012	    s_loop(Socket, Timeout, Parent, Handler, State);
2013	{Parent,Ref,exit} ->
2014	    ok = gen_sctp:close(Socket),
2015	    Key = exit,
2016	    NewState = gb_push(Key, Socket, State),
2017	    Parent ! {self(),Ref,{NewState,flush()}};
2018	{Parent,Ref,{Msg}} ->
2019	    Result = Handler(Msg),
2020	    Key = req,
2021	    NewState = gb_push(Key, {Msg,Result}, State),
2022	    Parent ! {self(),Ref,Result},
2023	    s_loop(Socket, Timeout, Parent, Handler, NewState);
2024	%% {Parent,Ref,{get,Key}} ->
2025	%%     Parent ! {self(),Ref,gb_get(Key, State)},
2026	%%     s_loop(Socket, Timeout, Parent, Handler, State);
2027	{sctp,Socket,Addr,Port,
2028	 {[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}}
2029	  when not is_tuple(Data) ->
2030	    case gb_get({assoc_change,AssocId}, State) of
2031		[{Addr,Port,
2032		  #sctp_assoc_change{
2033		     state=comm_up,
2034		     inbound_streams=Is}}|_]
2035		  when 0 =< Stream, Stream < Is-> ok;
2036		[] -> ok
2037	    end,
2038	    Key = {msg,AssocId,Stream},
2039	    NewState = gb_push(Key, {Addr,Port,SRI,Data}, State),
2040	    Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
2041	    again(Socket),
2042	    s_loop(Socket, Timeout, Parent, Handler, NewState);
2043	{sctp,Socket,Addr,Port,
2044	 {SRI,#sctp_assoc_change{assoc_id=AssocId,state=St}=SAC}} ->
2045	    case SRI of
2046		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
2047		[] -> ok
2048	    end,
2049	    Key = {assoc_change,AssocId},
2050	    case {gb_get(Key, State),St} of
2051		{[],_} -> ok;
2052		{[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
2053		  when St =:= comm_lost; St =:= shutdown_comp -> ok
2054	    end,
2055	    NewState = gb_push(Key, {Addr,Port,SAC}, State),
2056	    Parent ! {self(),{Addr,Port,SAC}},
2057	    again(Socket),
2058	    s_loop(Socket, Timeout, Parent, Handler, NewState);
2059	{sctp,Socket,Addr,Port,
2060	 {SRI,#sctp_paddr_change{assoc_id=AssocId,
2061				 addr={_,P},
2062				 state=St}=SPC}} ->
2063	    match_unless_solaris(Port, P),
2064	    case SRI of
2065		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
2066		[] -> ok
2067	    end,
2068	    case {gb_get({assoc_change,AssocId}, State),St} of
2069		{[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_}
2070		  when St =:= addr_available;
2071		       St =:= addr_confirmed -> ok;
2072		{[],addr_confirmed} -> ok
2073	    end,
2074	    Key = {paddr_change,AssocId},
2075	    NewState = gb_push(Key, {Addr,Port,SPC}, State),
2076	    again(Socket),
2077	    s_loop(Socket, Timeout, Parent, Handler, NewState);
2078	{sctp,Socket,Addr,Port,
2079	 {SRI,#sctp_shutdown_event{assoc_id=AssocId}=SSE}} ->
2080	    case SRI of
2081		[#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
2082		[] -> ok
2083	    end,
2084	    case gb_get({assoc_change,AssocId}, State) of
2085		[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok;
2086		[] -> ok
2087	    end,
2088	    Key = {shutdown_event,AssocId},
2089	    NewState = gb_push(Key, {Addr,Port}, State),
2090	    Parent ! {self(), {Addr,Port,SSE}},
2091	    again(Socket),
2092	    s_loop(Socket, Timeout, Parent, Handler, NewState);
2093	Unexpected ->
2094	    erlang:error({unexpected,Unexpected})
2095    end.
2096
2097again(Socket) ->
2098    receive
2099	{sctp_passive,Socket} ->
2100	    [{active, false}] = ok(inet:getopts(Socket, [active])),
2101	    ok = inet:setopts(Socket,[{active,1}])
2102    after 0 ->
2103	    ok = inet:setopts(Socket, [{active,once}])
2104    end.
2105
2106gb_push(Key, Val, GBT) ->
2107    TS = timestamp(),
2108    case gb_trees:lookup(Key, GBT) of
2109	none ->
2110	    gb_trees:insert(Key, [{TS,Val}], GBT);
2111	{value,V} ->
2112	    gb_trees:update(Key, [{TS,Val}|V], GBT)
2113    end.
2114
2115gb_get(Key, GBT) ->
2116    case gb_trees:lookup(Key, GBT) of
2117	none ->
2118	    [];
2119	{value,V} ->
2120	    [Val || {_TS,Val} <- V]
2121    end.
2122
2123match_unless_solaris(A, B) ->
2124    case os:type() of
2125	{unix,sunos} -> B;
2126	_ -> A = B
2127    end.
2128
2129
2130%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2131
2132timestamp() ->
2133    erlang:monotonic_time().
2134
2135
2136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2137
2138log_ok(X) ->
2139    log(ok(X)).
2140
2141ok({ok, X}) ->
2142    X;
2143ok({error, X}) ->
2144    ?P("ERROR: ~p", [X]),
2145    ?line ct:fail({unexpected_error, X});
2146ok(X) ->
2147    ?P("UNEXPECTED: ~p", [X]),
2148    ?line ct:fail({unexpected, X}).
2149
2150
2151log(X) ->
2152    ?P("LOG: ~p", [X]),
2153    X.
2154
2155err([], Result) ->
2156    erlang:error(Result);
2157err([Reason|_], {error,Reason}) ->
2158    ok;
2159err([_|Reasons], Result) ->
2160    err(Reasons, Result).
2161
2162
2163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2164
2165open_failed_str(Reason) ->
2166    ?F("Open failed: ~w", [Reason]).
2167
2168