1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2007-2021. 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(inet_sockopt_SUITE).
21
22-include_lib("common_test/include/ct.hrl").
23-include("kernel_test_lib.hrl").
24
25
26-define(C_GET_IPPROTO_TCP,1).
27-define(C_GET_IPPROTO_IP,2).
28-define(C_GET_SOL_SOCKET,3).
29-define(C_GET_SOL_IP,4).
30
31-define(C_GET_TCP_KEEPIDLE,11).
32-define(C_GET_TCP_LINGER2,12).
33-define(C_GET_TCP_INFO,13).
34-define(C_GET_SO_REUSEADDR,14).
35-define(C_GET_SO_KEEPALIVE,15).
36-define(C_GET_SO_LINGER,16).
37
38-define(C_GET_LINGER_SIZE,21).
39-define(C_GET_TCP_INFO_SIZE,22).
40
41-define(C_GET_OFF_LINGER_L_ONOFF,31).
42-define(C_GET_OFF_LINGER_L_LINGER,32).
43-define(C_GET_OFF_TCPI_SACKED,33).
44-define(C_GET_OFF_TCPI_OPTIONS,34).
45
46-define(C_GET_SIZ_LINGER_L_ONOFF,41).
47-define(C_GET_SIZ_LINGER_L_LINGER,42).
48-define(C_GET_SIZ_TCPI_SACKED,43).
49-define(C_GET_SIZ_TCPI_OPTIONS,44).
50
51-define(C_QUIT,99).
52
53-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
54	 init_per_group/2,end_per_group/2,
55	 simple/1, loop_all/1, simple_raw/1, simple_raw_getbin/1,
56	 multiple_raw/1, multiple_raw_getbin/1,
57	 doc_examples_raw/1,doc_examples_raw_getbin/1,
58	 large_raw/1,large_raw_getbin/1,combined/1,combined_getbin/1,
59	 ipv6_v6only_udp/1, ipv6_v6only_tcp/1, ipv6_v6only_sctp/1,
60	 use_ipv6_v6only_udp/1,
61	 type_errors/1]).
62
63-export([init_per_testcase/2, end_per_testcase/2]).
64
65
66suite() ->
67    [{ct_hooks,[ts_install_cth]},
68     {timetrap,{minutes,1}}].
69
70all() ->
71    [simple, loop_all, simple_raw, simple_raw_getbin,
72     multiple_raw, multiple_raw_getbin,
73     doc_examples_raw, doc_examples_raw_getbin, large_raw,
74     large_raw_getbin, combined, combined_getbin,
75     ipv6_v6only_udp, ipv6_v6only_tcp, ipv6_v6only_sctp,
76     use_ipv6_v6only_udp,
77     type_errors].
78
79groups() ->
80    [].
81
82init_per_suite(Config) ->
83    Config.
84
85end_per_suite(_Config) ->
86    ok.
87
88init_per_group(_GroupName, Config) ->
89    Config.
90
91end_per_group(_GroupName, Config) ->
92    Config.
93
94
95init_per_testcase(_Func, Config) ->
96    Config.
97
98end_per_testcase(_Func, _Config) ->
99    ok.
100
101%% Test inet:setopt/getopt simple functionality.
102simple(Config) when is_list(Config) ->
103    XOpt = case os:type() of
104	       {unix,_} -> [{reuseaddr,true}];
105	       _ -> []
106	   end,
107    Opt = [{nodelay,true},
108	   {keepalive,true},{packet,4},
109	   {active,false}|XOpt],
110    OptTags = [X || {X,_} <- Opt],
111    {S1,S2} = create_socketpair(Opt, Opt),
112    {ok,Opt} = inet:getopts(S1,OptTags),
113    {ok,Opt} = inet:getopts(S2,OptTags),
114    NoPushOpt = case os:type() of
115                    {unix, Osname} when Osname =:= linux; Osname =:= freebsd -> {nopush, true};
116                    {_,_} -> {nopush, false}
117                end,
118    COpt = [{X,case X of nodelay -> false;_ -> Y end} || {X,Y} <- [NoPushOpt|Opt]],
119    COptTags = [X || {X,_} <- COpt],
120    inet:setopts(S1,COpt),
121    {ok,COpt} = inet:getopts(S1,COptTags),
122    {ok,Opt} = inet:getopts(S2,OptTags),
123    gen_tcp:close(S1),
124    gen_tcp:close(S2),
125    ok.
126
127%% Loop through all socket options and check that they work.
128loop_all(Config) when is_list(Config) ->
129    ListenFailures =
130	lists:foldr(make_check_fun(listen,1),[],all_listen_options()),
131    ConnectFailures =
132	lists:foldr(make_check_fun(connect,2),[],all_connect_options()),
133    case ListenFailures++ConnectFailures of
134	[] ->
135	    ok;
136	Failed ->
137	    {comment,lists:flatten(
138		       io_lib:format("Non mandatory failed:~w",
139				     [Failed]))}
140    end.
141
142
143
144%% Test simple setopt/getopt of raw options.
145simple_raw(Config) when is_list(Config) ->
146    do_simple_raw(Config,false).
147
148%% Test simple setopt/getopt of raw options, with binaries in getopt.
149simple_raw_getbin(Config) when is_list(Config) ->
150    do_simple_raw(Config,true).
151
152do_simple_raw(Config,Binary) when is_list(Config) ->
153    Port = start_helper(Config),
154    SolSocket = ask_helper(Port,?C_GET_SOL_SOCKET),
155    SoKeepAlive = ask_helper(Port,?C_GET_SO_KEEPALIVE),
156    OptionTrue = {raw,SolSocket,SoKeepAlive,<<1:32/native>>},
157    OptionFalse = {raw,SolSocket,SoKeepAlive,<<0:32/native>>},
158    {S1,S2} = create_socketpair([OptionTrue],[{keepalive,true}]),
159    {ok,[{keepalive,true}]} = inet:getopts(S1,[keepalive]),
160    {ok,[{keepalive,true}]} = inet:getopts(S2,[keepalive]),
161    {ok,[{raw,SolSocket,SoKeepAlive,X1B}]} =
162	inet:getopts(S1,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]),
163    X1 = nintbin2int(X1B),
164    {ok,[{raw,SolSocket,SoKeepAlive,X2B}]} =
165	inet:getopts(S2,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]),
166    X2 = nintbin2int(X2B),
167    true = X1 > 0,
168    true = X2 > 0,
169    inet:setopts(S1,[{keepalive,false}]),
170    inet:setopts(S2,[OptionFalse]),
171    {ok,[{keepalive,false}]} = inet:getopts(S1,[keepalive]),
172    {ok,[{keepalive,false}]} = inet:getopts(S2,[keepalive]),
173    {ok,[{raw,SolSocket,SoKeepAlive,Y1B}]} =
174	inet:getopts(S1,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]),
175    Y1 = nintbin2int(Y1B),
176    {ok,[{raw,SolSocket,SoKeepAlive,Y2B}]} =
177	inet:getopts(S2,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]),
178    Y2 = nintbin2int(Y2B),
179    true = Y1 == 0,
180    true = Y2 == 0,
181    gen_tcp:close(S1),
182    gen_tcp:close(S2),
183    stop_helper(Port),
184    ok.
185
186nintbin2int(<<Int:32/native>>) -> Int;
187nintbin2int(<<Int:24/native>>) -> Int;
188nintbin2int(<<Int:16/native>>) -> Int;
189nintbin2int(<<Int:8/native>>) -> Int;
190nintbin2int(<<>>) -> 0.
191
192
193
194%% Test setopt/getopt of multiple raw options.
195multiple_raw(Config) when is_list(Config) ->
196    do_multiple_raw(Config,false).
197
198%% Test setopt/getopt of multiple raw options, with binaries in
199%% getopt.
200multiple_raw_getbin(Config) when is_list(Config) ->
201    do_multiple_raw(Config,true).
202
203do_multiple_raw(Config, Binary) ->
204    Port = start_helper(Config),
205    SolSocket = ask_helper(Port, ?C_GET_SOL_SOCKET),
206    SoKeepalive = ask_helper(Port, ?C_GET_SO_KEEPALIVE),
207    SoKeepaliveTrue = {raw,SolSocket,SoKeepalive,<<1:32/native>>},
208    SoKeepaliveFalse = {raw,SolSocket,SoKeepalive,<<0:32/native>>},
209    SoReuseaddr = ask_helper(Port, ?C_GET_SO_REUSEADDR),
210    SoReuseaddrTrue = {raw,SolSocket,SoReuseaddr,<<1:32/native>>},
211    SoReuseaddrFalse = {raw,SolSocket,SoReuseaddr,<<0:32/native>>},
212    {S1,S2} =
213	create_socketpair(
214	  [SoReuseaddrFalse,SoKeepaliveTrue],
215	  [SoKeepaliveFalse,SoReuseaddrTrue]),
216    {ok,[{reuseaddr,false},{keepalive,true}]} =
217	inet:getopts(S1, [reuseaddr,keepalive]),
218    {ok,
219     [{raw,SolSocket,SoReuseaddr,S1R1},
220      {raw,SolSocket,SoKeepalive,S1K1}]} =
221	inet:getopts(
222	  S1,
223	  [{raw,SolSocket,SoReuseaddr,binarify(4, Binary)},
224	   {raw,SolSocket,SoKeepalive,binarify(4, Binary)}]),
225    true = nintbin2int(S1R1) =:= 0,
226    true = nintbin2int(S1K1) =/= 0,
227    {ok,[{keepalive,false},{reuseaddr,true}]} =
228	inet:getopts(S2, [keepalive,reuseaddr]),
229    {ok,
230     [{raw,SolSocket,SoKeepalive,S2K1},
231      {raw,SolSocket,SoReuseaddr,S2R1}]} =
232	inet:getopts(
233	  S2,
234	  [{raw,SolSocket,SoKeepalive,binarify(4, Binary)},
235	   {raw,SolSocket,SoReuseaddr,binarify(4, Binary)}]),
236    true = nintbin2int(S2K1) =:= 0,
237    true = nintbin2int(S2R1) =/= 0,
238    %%
239    ok = inet:setopts(
240	   S1, [SoReuseaddrTrue,SoKeepaliveFalse]),
241    ok = inet:setopts(
242	   S2, [SoKeepaliveTrue,SoReuseaddrFalse]),
243    {ok,
244     [{raw,SolSocket,SoReuseaddr,S1R2},
245      {raw,SolSocket,SoKeepalive,S1K2}]} =
246	inet:getopts(
247	  S1,
248	  [{raw,SolSocket,SoReuseaddr,binarify(4, Binary)},
249	   {raw,SolSocket,SoKeepalive,binarify(4, Binary)}]),
250    true = nintbin2int(S1R2) =/= 0,
251    true = nintbin2int(S1K2) =:= 0,
252    {ok,
253     [{raw,SolSocket,SoKeepalive,S2K2},
254      {raw,SolSocket,SoReuseaddr,S2R2}]} =
255	inet:getopts(
256	  S2,
257	  [{raw,SolSocket,SoKeepalive,binarify(4, Binary)},
258	   {raw,SolSocket,SoReuseaddr,binarify(4, Binary)}]),
259    true = nintbin2int(S2K2) =/= 0,
260    true = nintbin2int(S2R2) =:= 0,
261    %%
262    gen_tcp:close(S1),
263    gen_tcp:close(S2),
264    stop_helper(Port),
265    ok.
266
267
268
269%% Test that the example code from the documentation works.
270doc_examples_raw(Config) when is_list(Config) ->
271    do_doc_examples_raw(Config,false).
272
273%% Test that the example code from the documentation works when getopt
274%% uses binaries.
275doc_examples_raw_getbin(Config) when is_list(Config) ->
276    do_doc_examples_raw(Config,true).
277
278do_doc_examples_raw(Config,Binary) when is_list(Config) ->
279    Port = start_helper(Config),
280    Proto = ask_helper(Port,?C_GET_IPPROTO_TCP),
281    TcpInfo = ask_helper(Port,?C_GET_TCP_INFO),
282    TcpInfoSize = ask_helper(Port,?C_GET_TCP_INFO_SIZE),
283    TcpiSackedOffset = ask_helper(Port,?C_GET_OFF_TCPI_SACKED),
284    TcpiOptionsOffset = ask_helper(Port,?C_GET_OFF_TCPI_OPTIONS),
285    TcpiSackedSize = ask_helper(Port,?C_GET_SIZ_TCPI_SACKED),
286    TcpiOptionsSize = ask_helper(Port,?C_GET_SIZ_TCPI_OPTIONS),
287    TcpLinger2 = ask_helper(Port,?C_GET_TCP_LINGER2),
288    stop_helper(Port),
289    case all_ok([Proto,TcpInfo,TcpInfoSize,TcpiSackedOffset,
290		 TcpiOptionsOffset,TcpiSackedSize,TcpiOptionsSize,
291		 TcpLinger2]) of
292	false ->
293	    {skipped,"Does not run on this OS."};
294	true ->
295	    {Sock,I} = create_socketpair([],[]),
296	    {ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} =
297		inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]),
298	    NewLinger = OrigLinger div 2,
299	    ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2,
300				     <<NewLinger:32/native>>}]),
301	    {ok,[{raw,Proto,TcpLinger2,<<NewLinger:32/native>>}]} =
302		inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]),
303	    ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2,
304				     <<OrigLinger:32/native>>}]),
305	    {ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} =
306		inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]),
307	    {ok,[{raw,_,_,Info}]} =
308		inet:getopts(Sock,[{raw,Proto,TcpInfo,
309				    binarify(TcpInfoSize,Binary)}]),
310	    Bit1 = TcpiSackedSize * 8,
311            <<_:TcpiSackedOffset/binary,
312	      TcpiSacked:Bit1/native,_/binary>> =
313		Info,
314	    0 = TcpiSacked,
315	    Bit2 = TcpiOptionsSize * 8,
316            <<_:TcpiOptionsOffset/binary,
317	      TcpiOptions:Bit2/native,_/binary>> =
318		Info,
319	    true = TcpiOptions =/= 0,
320	    gen_tcp:close(Sock),
321	    gen_tcp:close(I),
322	    ok
323    end.
324
325%% Test structs and large/too large buffers when raw.
326large_raw(Config) when is_list(Config) ->
327    do_large_raw(Config,false).
328
329%% Test structs and large/too large buffers when raw
330%% using binaries to getopts.
331large_raw_getbin(Config) when is_list(Config) ->
332    do_large_raw(Config,true).
333
334do_large_raw(Config,Binary) when is_list(Config) ->
335    Port = start_helper(Config),
336    Proto = ask_helper(Port,?C_GET_SOL_SOCKET),
337    Linger = ask_helper(Port,?C_GET_SO_LINGER),
338    LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE),
339    LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF),
340    LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER),
341    LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF),
342    LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER),
343    stop_helper(Port),
344    case all_ok([Proto,Linger,LingerSize,LingerOnOffOffset,
345		 LingerLingerOffset,LingerOnOffSize,LingerLingerSize]) of
346	false ->
347	    {skipped,"Does not run on this OS."};
348	true ->
349	    {Sock1,Sock2} = create_socketpair([{linger,{true,10}}],
350					      [{linger,{false,0}}]),
351	    LargeSize = 1024,  % Solaris can take up to 1024*9,
352						% linux 1024*63...
353	    TooLargeSize = 1024*64,
354	    {ok,[{raw,Proto,Linger,Linger1}]} =
355		inet:getopts(Sock1,[{raw,Proto,Linger,
356				     binarify(LargeSize,Binary)}]),
357	    {ok,[{raw,Proto,Linger,Linger2}]} =
358		inet:getopts(Sock2,[{raw,Proto,Linger,
359				     binarify(LingerSize,Binary)}]),
360	    true = byte_size(Linger1) =:= LingerSize,
361	    LingerLingerBits = LingerLingerSize * 8,
362	    LingerOnOffBits = LingerOnOffSize * 8,
363	    <<_:LingerLingerOffset/binary,
364	      Ling1:LingerLingerBits/native,_/binary>> = Linger1,
365	    <<_:LingerOnOffOffset/binary,
366	      Off1:LingerOnOffBits/native,_/binary>> = Linger1,
367	    <<_:LingerOnOffOffset/binary,
368	      Off2:LingerOnOffBits/native,_/binary>> = Linger2,
369	    true = Off1 =/= 0,
370	    true = Off2 == 0,
371	    true = Ling1 == 10,
372	    {error,einval} =
373		inet:getopts(Sock1,[{raw,Proto,Linger,TooLargeSize}]),
374	    gen_tcp:close(Sock1),
375	    gen_tcp:close(Sock2),
376	    ok
377    end.
378
379%% Test raw structs combined w/ other options .
380combined(Config) when is_list(Config) ->
381    do_combined(Config,false).
382
383%% Test raw structs combined w/ other options and
384%% binarise in getopts.
385combined_getbin(Config) when is_list(Config) ->
386    do_combined(Config,true).
387
388do_combined(Config,Binary) when is_list(Config) ->
389    Port = start_helper(Config),
390    Proto = ask_helper(Port,?C_GET_SOL_SOCKET),
391    Linger = ask_helper(Port,?C_GET_SO_LINGER),
392    LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE),
393    LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF),
394    LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER),
395    LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF),
396    LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER),
397    stop_helper(Port),
398    case all_ok([Proto,Linger,LingerSize,LingerOnOffOffset,
399		 LingerLingerOffset,LingerOnOffSize,LingerLingerSize]) of
400	false ->
401	    {skipped,"Does not run on this OS."};
402	true ->
403	    LingerLingerBits = LingerLingerSize * 8,
404	    LingerOnOffBits = LingerOnOffSize * 8,
405	    {LingerOn,LingerOff} =
406		case LingerOnOffOffset <  LingerLingerOffset of
407		    true ->
408			Pad1 =
409			    list_to_binary(
410			      lists:duplicate(LingerOnOffOffset,
411					      0)),
412			Pad2Siz =
413			    LingerLingerOffset - LingerOnOffSize -
414			    LingerOnOffOffset,
415			Pad2 =
416			    list_to_binary(
417			      lists:duplicate(Pad2Siz,
418					      0)),
419			Pad3Siz = LingerSize - LingerLingerSize -
420			    LingerLingerOffset,
421			Pad3 = list_to_binary(
422				 lists:duplicate(Pad3Siz,
423						 0)),
424			{<<Pad1/binary,1:LingerOnOffBits/native,
425			   Pad2/binary,10:LingerLingerBits/native,
426			   Pad3/binary>>,
427			 <<Pad1/binary,0:LingerOnOffBits/native,
428			   Pad2/binary,0:LingerLingerBits/native,
429			   Pad3/binary>>};
430		    false ->
431			Pad1 =
432			    list_to_binary(
433			      lists:duplicate(LingerLingerOffset,
434					      0)),
435			Pad2Siz =
436			    LingerOnOffOffset - LingerLingerSize -
437			    LingerLingerOffset,
438			Pad2 =
439			    list_to_binary(
440			      lists:duplicate(Pad2Siz,
441					      0)),
442			Pad3Siz = LingerSize - LingerOnOffSize -
443			    LingerOnOffOffset,
444			Pad3 = list_to_binary(
445				 lists:duplicate(Pad3Siz,
446						 0)),
447			{<<Pad1/binary,1:LingerLingerBits/native,
448			   Pad2/binary,10:LingerOnOffBits/native,
449			   Pad3/binary>>,
450			 <<Pad1/binary,0:LingerLingerBits/native,
451			   Pad2/binary,0:LingerOnOffBits/native,
452			   Pad3/binary>>}
453		end,
454	    RawLingerOn = {raw,Proto,Linger,LingerOn},
455	    RawLingerOff = {raw,Proto,Linger,LingerOff},
456	    {Sock1,Sock2} =
457		create_socketpair([{keepalive,true},
458				   RawLingerOn],
459				  [{keepalive,false},
460				   RawLingerOff]),
461	    {ok,[{raw,Proto,Linger,Linger1},{keepalive,Keep1}]} =
462		inet:getopts(Sock1,[{raw,Proto,Linger,
463				     binarify(LingerSize,Binary)},keepalive]),
464	    {ok,[{raw,Proto,Linger,Linger2},{keepalive,Keep2}]} =
465		inet:getopts(Sock2,[{raw,Proto,Linger,
466				     binarify(LingerSize,Binary)},keepalive]),
467	    true = byte_size(Linger1) =:= LingerSize,
468	    <<_:LingerLingerOffset/binary,
469	      Ling1:LingerLingerBits/native,_/binary>> = Linger1,
470	    <<_:LingerOnOffOffset/binary,
471	      Off1:LingerOnOffBits/native,_/binary>> = Linger1,
472	    <<_:LingerOnOffOffset/binary,
473	      Off2:LingerOnOffBits/native,_/binary>> = Linger2,
474	    true = Off1 =/= 0,
475	    true = Off2 == 0,
476	    true = Ling1 == 10,
477	    true = Keep1 =:= true,
478	    true = Keep2 =:= false,
479	    {Sock3,Sock4} =
480		create_socketpair([RawLingerOn,{keepalive,true}],
481				  [RawLingerOff,{keepalive,false}]),
482	    {ok,[{raw,Proto,Linger,Linger3},{keepalive,Keep3}]} =
483		inet:getopts(Sock3,[{raw,Proto,Linger,
484				     binarify(LingerSize,Binary)},keepalive]),
485	    {ok,[{raw,Proto,Linger,Linger4},{keepalive,Keep4}]} =
486		inet:getopts(Sock4,[{raw,Proto,Linger,
487				     binarify(LingerSize,Binary)},keepalive]),
488	    true = byte_size(Linger3) =:= LingerSize,
489	    <<_:LingerLingerOffset/binary,
490	      Ling3:LingerLingerBits/native,_/binary>> = Linger3,
491	    <<_:LingerOnOffOffset/binary,
492	      Off3:LingerOnOffBits/native,_/binary>> = Linger3,
493	    <<_:LingerOnOffOffset/binary,
494	      Off4:LingerOnOffBits/native,_/binary>> = Linger4,
495	    true = Off3 =/= 0,
496	    true = Off4 == 0,
497	    true = Ling3 == 10,
498	    true = Keep3 =:= true,
499	    true = Keep4 =:= false,
500	    {Sock5,Sock6} =
501		create_socketpair([{packet,4},RawLingerOn,{keepalive,true}],
502				  [{packet,2},RawLingerOff,{keepalive,false}]),
503	    {ok,[{packet,Pack5},{raw,Proto,Linger,Linger5},
504		 {keepalive,Keep5}]} =
505		inet:getopts(Sock5,[packet,{raw,Proto,Linger,
506					    binarify(LingerSize,Binary)},
507				    keepalive]),
508	    {ok,[{packet,Pack6},{raw,Proto,Linger,Linger6},
509		 {keepalive,Keep6}]} =
510		inet:getopts(Sock6,[packet,{raw,Proto,Linger,
511					    binarify(LingerSize,Binary)},
512				    keepalive]),
513	    true = byte_size(Linger5) =:= LingerSize,
514	    <<_:LingerLingerOffset/binary,
515	      Ling5:LingerLingerBits/native,_/binary>> = Linger5,
516	    <<_:LingerOnOffOffset/binary,
517	      Off5:LingerOnOffBits/native,_/binary>> = Linger5,
518	    <<_:LingerOnOffOffset/binary,
519	      Off6:LingerOnOffBits/native,_/binary>> = Linger6,
520	    true = Off5 =/= 0,
521	    true = Off6 == 0,
522	    true = Ling5 == 10,
523	    true = Keep5 =:= true,
524	    true = Keep6 =:= false,
525	    true = Pack5 =:= 4,
526	    true = Pack6 =:= 2,
527	    inet:setopts(Sock6,[{packet,4},RawLingerOn,
528				{keepalive,true}]),
529	    {ok,[{packet,Pack7},{raw,Proto,Linger,Linger7},
530		 {keepalive,Keep7}]} =
531		inet:getopts(Sock6,[packet,{raw,Proto,Linger,
532					    binarify(LingerSize,Binary)},
533				    keepalive]),
534	    <<_:LingerOnOffOffset/binary,
535	      Off7:LingerOnOffBits/native,_/binary>> = Linger7,
536	    true = Off7 =/= 0,
537	    true = Keep7 =:= true,
538	    true = Pack7 =:= 4,
539	    gen_tcp:close(Sock1),
540	    gen_tcp:close(Sock2),
541	    gen_tcp:close(Sock3),
542	    gen_tcp:close(Sock4),
543	    gen_tcp:close(Sock5),
544	    gen_tcp:close(Sock6),
545	    ok
546    end.
547
548
549
550%% Test socket option ipv6_v6only for UDP.
551ipv6_v6only_udp(Config) when is_list(Config) ->
552    ipv6_v6only(Config, gen_udp).
553
554%% Test socket option ipv6_v6only for TCP.
555ipv6_v6only_tcp(Config) when is_list(Config) ->
556    ipv6_v6only(Config, gen_tcp).
557
558%% Test socket option ipv6_v6only for SCTP.
559ipv6_v6only_sctp(Config) when is_list(Config) ->
560    ipv6_v6only(Config, gen_sctp).
561
562ipv6_v6only(Config, Module) when is_list(Config) ->
563    case ipv6_v6only_open(Module, []) of
564	{ok,S1} ->
565	    case inet:getopts(S1, [ipv6_v6only]) of
566		{ok,[{ipv6_v6only,Default}]}
567		  when is_boolean(Default) ->
568		    ok =
569			ipv6_v6only_close(Module, S1),
570		    ipv6_v6only(Config, Module, Default);
571		{ok,[]} ->
572		    io:format("Not implemented.~n", []),
573		    %% This list of OS:es where the option is
574		    %% supposed to be not implemented is just
575		    %% a guess, and may grow with time.
576		    case {os:type(),os:version()} of
577			{{unix,linux},{2,M,_}}
578			  when M =< 4 -> ok
579		    end,
580		    %% At least this should work
581		    {ok,S2} =
582			ipv6_v6only_open(
583			  Module,
584			  [{ipv6_v6only,true}]),
585		    ok =
586			ipv6_v6only_close(Module, S2)
587	    end;
588	{error,_} ->
589	    {skipped,"Socket type not supported"}
590    end.
591
592ipv6_v6only(Config, Module, Default) when is_list(Config) ->
593    io:format("Default ~w.~n", [Default]),
594    {ok,S1} =
595	ipv6_v6only_open(Module, [{ipv6_v6only,Default}]),
596    {ok,[{ipv6_v6only,Default}]} =
597	inet:getopts(S1, [ipv6_v6only]),
598    ok =
599	ipv6_v6only_close(Module, S1),
600    NotDefault = not Default,
601    case ipv6_v6only_open(Module, [{ipv6_v6only,NotDefault}]) of
602	{ok,S2} ->
603	    io:format("Read-write.~n", []),
604	    {ok,[{ipv6_v6only,NotDefault}]} =
605		inet:getopts(S2, [ipv6_v6only]),
606	    ok;
607	{error,einval} ->
608	    io:format("Read-only.~n", []),
609	    %% This option is known to be read-only and true
610	    %% on Windows and OpenBSD
611	    case os:type() of
612		{unix,openbsd} when Default =:= true -> ok;
613		{win32,_} when Default =:= true -> ok
614	    end
615    end.
616
617ipv6_v6only_open(Module, Opts) ->
618    Module:case Module of
619	       gen_tcp -> listen;
620	       _ -> open
621	   end(0, [inet6|Opts]).
622
623ipv6_v6only_close(Module, Socket) ->
624    Module:close(Socket).
625
626
627%% Test using socket option ipv6_v6only for UDP.
628use_ipv6_v6only_udp(Config) when is_list(Config) ->
629    ?TC_TRY(use_ipv6_v6only_udp,
630	    fun() -> do_use_ipv6_v6only_udp(Config) end).
631
632do_use_ipv6_v6only_udp(Config) ->
633    case gen_udp:open(0, [inet6,{ip,{0,0,0,0,0,0,0,1}}, {ipv6_v6only,true}]) of
634	{ok,S6} ->
635	    ?P("IPv6 socket option created - ensure ipv6_v6only"),
636	    case inet:getopts(S6, [ipv6_v6only]) of
637		{ok,[{ipv6_v6only,true}]} ->
638		    use_ipv6_v6only_udp(Config, S6);
639		{ok, Other} ->
640		    ?P("IPv6 socket option created - "
641		       "failed ensuring ipv6_v6only"),
642		    exit({skip, {getopts, Other}})
643	    end;
644	{error, OReason} ->
645	    ?P("failed create IPv6 socket: "
646	       "~n   ~p", [OReason]),
647	    exit({skip, "Socket type not supported"})
648    end.
649
650use_ipv6_v6only_udp(_Config, S6) ->
651    {ok, Port} = inet:port(S6),
652    ?P("IPv6 socket port number: ~w", [Port]),
653    S4 = case gen_udp:open(Port, [inet]) of
654	     {ok, Sock4} ->
655		 ?P("IPv4 socket created with port number ~w", [Port]),
656		 Sock4;
657	     {error, eaddrinuse = Reason} ->
658		 ?P("failed create IPv4 socket: ~p => skip", [Reason]),
659		 exit({skip, Reason});
660	     {error, Reason} ->
661		 ?P("failed create IPv4 socket: "
662		    "~n   ~p", [Reason]),
663		 ct:fail({failed_open, inet, Reason})
664	 end,
665    E6 = " IPv6-echo.",
666    E4 = " IPv4-echo.",
667    {Sender, MRef} =
668	spawn_monitor(fun () ->
669			      use_ipv6_v6only_udp_sender(Port, E6, E4)
670		      end),
671    use_ipv6_v6only_udp_listener(
672      S6, S4, E6, E4, Sender, MRef).
673
674use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Sender, Mref) ->
675    ?P("await upd message"),
676    receive
677	{udp,S6,IP,P,Data} ->
678	    ?P("received (IPv6) upd message"),
679	    ok = gen_udp:send(S6, IP, P, [Data|E6]),
680	    use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Sender, Mref);
681	{udp,S4,IP,P,Data} ->
682	    ?P("received (IPv4) upd message"),
683	    ok = gen_udp:send(S4, IP, P, [Data|E4]),
684	    use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Sender, Mref);
685	{'DOWN', Mref,_,_, normal} ->
686	    ?P("received expected normal down message"),
687	    ok;
688	{'DOWN', Mref,_,_, Result} ->
689	    %% Since we are linked we will never arrive here
690	    ?P("received expected down message: "
691	       "~n   ~p", [Result]),
692	    Result;
693	Other ->
694	    ?P("received unexpected message: "
695	       "~n   ~p", [Other]),
696	    exit({failed, {listener_unexpected, Other}})
697    end.
698
699use_ipv6_v6only_udp_sender(Port, E6, E4) ->
700    D6 = "IPv6-send.",
701    D4 = "IPv4-send.",
702    R6 = D6 ++ E6,
703    R4 = D4 ++ E4,
704    R6 = sndrcv({0,0,0,0,0,0,0,1}, Port, [inet6], D6),
705    R4 = sndrcv({127,0,0,1}, Port, [inet], D4),
706    ok.
707
708sndrcv(Ip, Port, Opts, Data) ->
709    {ok,S} = gen_udp:open(0, Opts),
710    ?P("[~w:~w] ! ~s", [Ip, Port, Data]),
711    ok = gen_udp:send(S, Ip, Port, Data),
712    receive
713	{udp, S, Ip, Port, RecData} ->
714	    ?P("[~w:~w] : ~s", [Ip, Port, RecData]),
715	    RecData;
716	Other ->
717	    ?P("received unexpected message: "
718	       "~n   ~p", [Other]),
719	    exit({failed, {sndrcv_unexpectec, Other}})
720    end.
721
722
723
724%% Test that raw data requests are not executed for bad types.
725type_errors(Config) when is_list(Config) ->
726    BadSetOptions =
727	[
728	 {raw,x,3,<<1:32>>},
729	 {raw,1,tre,<<1:32>>},
730	 {raw,1,3,ko},
731	 {raw,1,3,5},
732	 {raw,1,3},
733	 {raw,1},
734	 {raw},
735	 {raw,ett},
736	 {raw,ett,tre},
737	 {raw,{true,10}},
738	 {raw,{ett,tre,<<1:32>>}},
739	 {rav,1,3,<<1:32>>},
740	 raw,
741	 rav,
742	 {linger,banan}
743	],
744    BadGetOptions =
745	[
746	 {raw,x,3,<<1:32>>},
747	 {raw,1,tre,<<1:32>>},
748	 {raw,1,3,ko},
749	 {raw,1,3,5.1},
750	 {raw,1,3,-3},
751	 {raw,1,3},
752	 {raw,1},
753	 {raw},
754	 {raw,ett},
755	 {raw,ett,tre},
756	 {raw,{true,10}},
757	 {raw,{ett,tre,<<1:32>>}},
758	 {rav,1,3,<<1:32>>},
759	 raw,
760	 rav,
761	 {linger,banan}
762	],
763    lists:foreach(fun(Option) ->
764			  case
765			      catch create_socketpair([Option],[]) of
766			      {'EXIT',badarg} ->
767				  ok;
768			      Unexpected1 ->
769				  exit({unexpected,
770					Unexpected1})
771			  end,
772			  case
773			      catch create_socketpair([],[Option]) of
774			      {'EXIT',badarg} ->
775				  ok;
776			      Unexpected2 ->
777				  exit({unexpected,
778					Unexpected2})
779			  end,
780			  {Sock1,Sock2} = create_socketpair([],[]),
781			  case inet:setopts(Sock1, [Option]) of
782			      {error,einval} ->
783				  ok;
784			      Unexpected3 ->
785				  exit({unexpected,
786					Unexpected3})
787			  end,
788			  gen_tcp:close(Sock1),
789			  gen_tcp:close(Sock2)
790		  end,BadSetOptions),
791    {Sock1,Sock2} = create_socketpair([],[]),
792    lists:foreach(fun(Option) ->
793			  case inet:getopts(Sock1, [Option]) of
794			      {error,einval} ->
795				  ok;
796			      Unexpected ->
797				  exit({unexpected,
798					Unexpected})
799			  end
800		  end,BadGetOptions),
801    gen_tcp:close(Sock1),
802    gen_tcp:close(Sock2),
803    ok.
804
805all_ok([]) ->
806    true;
807all_ok([H|T]) when H >= 0 ->
808    all_ok(T);
809all_ok(_) ->
810    false.
811
812
813make_check_fun(Type,Element) ->
814    fun({Name,V1,V2,Mand,Chang},Acc) ->
815	    {LO1,CO1} = setelement(Element,{[],[]}, [{Name,V1}]),
816	    {LO2,CO2} = setelement(Element,{[],[]}, [{Name,V2}]),
817	    {X1,Y1} = create_socketpair(LO1,CO1),
818	    {X2,Y2} = create_socketpair(LO2,CO2),
819	    S1 = element(Element,{X1,Y1}),
820	    S2 = element(Element,{X2,Y2}),
821	    {ok,[{Name,R1}]} = inet:getopts(S1,[Name]),
822	    {ok,[{Name,R2}]} = inet:getopts(S2,[Name]),
823	    NewAcc =
824		case R1 =/= R2 of
825		    true ->
826			case Chang of
827			    true ->
828				inet:setopts(S1,[{Name,V2}]),
829				{ok,[{Name,R3}]} =
830				    inet:getopts(S1,[Name]),
831				case {R3 =/= R1, R3 =:= R2} of
832				    {true,true} ->
833					Acc;
834				    _ ->
835					case Mand of
836					    true ->
837						exit
838						  ({failed_sockopt,
839						    {change,
840						     Name}});
841					    false ->
842						[{change,Name}|Acc]
843					end
844				end;
845			    false ->
846				Acc
847			end;
848		    false ->
849			case Mand of
850			    true ->
851				exit({failed_sockopt,
852				      {Type,Name}});
853			    false ->
854				[{Type,Name}|Acc]
855			end
856		end,
857	    gen_tcp:close(X1),
858	    gen_tcp:close(Y1),
859	    gen_tcp:close(X2),
860	    gen_tcp:close(Y2),
861	    NewAcc
862    end.
863
864%% {OptionName,Value1,Value2,Mandatory,Changeable}
865all_listen_options() ->
866    [{tos,0,1,false,true},
867     {priority,0,1,false,true},
868     {reuseaddr,false,true,false,true},
869     {keepalive,false,true,true,true},
870     {linger, {false,10}, {true,10},true,true},
871     {sndbuf,2048,4096,false,true},
872     {recbuf,2048,4096,false,true},
873     {nodelay,false,true,true,true},
874     {header,2,4,true,true},
875     {active,false,true,true,false},
876     {packet,2,4,true,true},
877     {buffer,1000,2000,true,true},
878     {mode,list,binary,true,true},
879     {deliver,term,port,true,true},
880     {exit_on_close, true, false, true, true},
881     {high_watermark,4096,8192,true,true},
882     {low_watermark,2048,4096,true,true},
883     {high_msgq_watermark,4096,8192,true,true},
884     {low_msgq_watermark,2048,4096,true,true},
885     {send_timeout,infinity,1000,true,true},
886     {send_timeout_close,false,true,true,true},
887     {delay_send,false,true,true,true},
888     {packet_size,0,4,true,true}
889    ].
890all_connect_options() ->
891    [{tos,0,1,false,true},
892     {priority,0,1,false,true},
893     {reuseaddr,false,true,false,true},
894     {keepalive,false,true,true,true},
895     {linger, {false,10}, {true,10},true,true},
896     {sndbuf,2048,4096,false,true},
897     {recbuf,2048,4096,false,true},
898     {nodelay,false,true,true,true},
899     {header,2,4,true,true},
900     {active,false,true,true,false},
901     {packet,2,4,true,true},
902     {buffer,1000,2000,true,true},
903     {mode,list,binary,true,true},
904     {deliver,term,port,true,true},
905     {exit_on_close, true, false, true, true},
906     {high_watermark,4096,8192,false,true},
907     {low_watermark,2048,4096,false,true},
908     {high_msgq_watermark,4096,8192,true,true},
909     {low_msgq_watermark,2048,4096,true,true},
910     {send_timeout,infinity,1000,true,true},
911     {send_timeout_close,false,true,true,true},
912     {delay_send,false,true,true,true},
913     {packet_size,0,4,true,true}
914    ].
915
916
917create_socketpair(ListenOptions,ConnectOptions) ->
918    {ok,LS}=gen_tcp:listen(0,ListenOptions),
919    {ok,Port}=inet:port(LS),
920    {ok,CS}=gen_tcp:connect(localhost,Port,ConnectOptions),
921    {ok,AS}=gen_tcp:accept(LS),
922    gen_tcp:close(LS),
923    {AS,CS}.
924
925
926start_helper(Config) ->
927    Progname = filename:join(proplists:get_value(data_dir, Config), "sockopt_helper"),
928    Port = open_port({spawn,Progname},[eof,line]),
929    Port.
930
931ask_helper(Port,Code) ->
932    Com = integer_to_list(Code)++"\n",
933    Port ! {self(),{command,Com}},
934    receive
935	{Port,{data,{eol,Text}}} ->
936	    list_to_integer(Text);
937	Other ->
938	    exit({error,{unexpected_data_from_helper,Other}})
939    after 3000 ->
940	    exit({error,helper_timeout})
941    end.
942
943stop_helper(Port) ->
944    catch ask_helper(Port,?C_QUIT),
945    receive
946	{Port,eof} ->
947	    Port ! {self(), close},
948	    receive
949		{Port,closed} ->
950		    ok
951	    after 1000 ->
952		    timeout
953	    end
954    after 1000 ->
955	    timeout
956    end.
957
958binarify(Size,Binary) when Binary =:= true ->
959    <<0:Size/unit:8>>;
960binarify(Size,Binary) when Binary =:= false ->
961    Size.
962