%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2007-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
-module(inet_sockopt_SUITE).
-include_lib("common_test/include/ct.hrl").
-define(C_GET_IPPROTO_TCP,1).
-define(C_GET_IPPROTO_IP,2).
-define(C_GET_SOL_SOCKET,3).
-define(C_GET_SOL_IP,4).
-define(C_GET_TCP_KEEPIDLE,11).
-define(C_GET_TCP_LINGER2,12).
-define(C_GET_TCP_INFO,13).
-define(C_GET_SO_REUSEADDR,14).
-define(C_GET_SO_KEEPALIVE,15).
-define(C_GET_SO_LINGER,16).
-define(C_GET_LINGER_SIZE,21).
-define(C_GET_TCP_INFO_SIZE,22).
-define(C_GET_OFF_LINGER_L_ONOFF,31).
-define(C_GET_OFF_LINGER_L_LINGER,32).
-define(C_GET_OFF_TCPI_SACKED,33).
-define(C_GET_OFF_TCPI_OPTIONS,34).
-define(C_GET_SIZ_LINGER_L_ONOFF,41).
-define(C_GET_SIZ_LINGER_L_LINGER,42).
-define(C_GET_SIZ_TCPI_SACKED,43).
-define(C_GET_SIZ_TCPI_OPTIONS,44).
-define(C_QUIT,99).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
simple/1, loop_all/1, simple_raw/1, simple_raw_getbin/1,
multiple_raw/1, multiple_raw_getbin/1,
doc_examples_raw/1,doc_examples_raw_getbin/1,
large_raw/1,large_raw_getbin/1,combined/1,combined_getbin/1,
ipv6_v6only_udp/1, ipv6_v6only_tcp/1, ipv6_v6only_sctp/1,
use_ipv6_v6only_udp/1,
type_errors/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap,{minutes,1}}].
all() ->
[simple, loop_all, simple_raw, simple_raw_getbin,
multiple_raw, multiple_raw_getbin,
doc_examples_raw, doc_examples_raw_getbin, large_raw,
large_raw_getbin, combined, combined_getbin,
ipv6_v6only_udp, ipv6_v6only_tcp, ipv6_v6only_sctp,
use_ipv6_v6only_udp,
type_errors].
groups() ->
[].
init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
init_per_testcase(_Func, Config) ->
Config.
end_per_testcase(_Func, _Config) ->
ok.
%% Test inet:setopt/getopt simple functionality.
simple(Config) when is_list(Config) ->
XOpt = case os:type() of
{unix,_} -> [{reuseaddr,true}];
_ -> []
end,
Opt = [{nodelay,true},
{keepalive,true},{packet,4},
{active,false}|XOpt],
OptTags = [X || {X,_} <- Opt],
{S1,S2} = create_socketpair(Opt, Opt),
{ok,Opt} = inet:getopts(S1,OptTags),
{ok,Opt} = inet:getopts(S2,OptTags),
COpt = [{X,case X of nodelay -> false;_ -> Y end} || {X,Y} <- Opt],
inet:setopts(S1,COpt),
{ok,COpt} = inet:getopts(S1,OptTags),
{ok,Opt} = inet:getopts(S2,OptTags),
gen_tcp:close(S1),
gen_tcp:close(S2),
ok.
%% Loop through all socket options and check that they work.
loop_all(Config) when is_list(Config) ->
ListenFailures =
lists:foldr(make_check_fun(listen,1),[],all_listen_options()),
ConnectFailures =
lists:foldr(make_check_fun(connect,2),[],all_connect_options()),
case ListenFailures++ConnectFailures of
[] ->
ok;
Failed ->
{comment,lists:flatten(
io_lib:format("Non mandatory failed:~w",
[Failed]))}
end.
%% Test simple setopt/getopt of raw options.
simple_raw(Config) when is_list(Config) ->
do_simple_raw(Config,false).
%% Test simple setopt/getopt of raw options, with binaries in getopt.
simple_raw_getbin(Config) when is_list(Config) ->
do_simple_raw(Config,true).
do_simple_raw(Config,Binary) when is_list(Config) ->
Port = start_helper(Config),
SolSocket = ask_helper(Port,?C_GET_SOL_SOCKET),
SoKeepAlive = ask_helper(Port,?C_GET_SO_KEEPALIVE),
OptionTrue = {raw,SolSocket,SoKeepAlive,<<1:32/native>>},
OptionFalse = {raw,SolSocket,SoKeepAlive,<<0:32/native>>},
{S1,S2} = create_socketpair([OptionTrue],[{keepalive,true}]),
{ok,[{keepalive,true}]} = inet:getopts(S1,[keepalive]),
{ok,[{keepalive,true}]} = inet:getopts(S2,[keepalive]),
{ok,[{raw,SolSocket,SoKeepAlive,X1B}]} =
inet:getopts(S1,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]),
X1 = nintbin2int(X1B),
{ok,[{raw,SolSocket,SoKeepAlive,X2B}]} =
inet:getopts(S2,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]),
X2 = nintbin2int(X2B),
true = X1 > 0,
true = X2 > 0,
inet:setopts(S1,[{keepalive,false}]),
inet:setopts(S2,[OptionFalse]),
{ok,[{keepalive,false}]} = inet:getopts(S1,[keepalive]),
{ok,[{keepalive,false}]} = inet:getopts(S2,[keepalive]),
{ok,[{raw,SolSocket,SoKeepAlive,Y1B}]} =
inet:getopts(S1,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]),
Y1 = nintbin2int(Y1B),
{ok,[{raw,SolSocket,SoKeepAlive,Y2B}]} =
inet:getopts(S2,[{raw,SolSocket,SoKeepAlive,binarify(4,Binary)}]),
Y2 = nintbin2int(Y2B),
true = Y1 == 0,
true = Y2 == 0,
gen_tcp:close(S1),
gen_tcp:close(S2),
stop_helper(Port),
ok.
nintbin2int(<<Int:32/native>>) -> Int;
nintbin2int(<<Int:24/native>>) -> Int;
nintbin2int(<<Int:16/native>>) -> Int;
nintbin2int(<<Int:8/native>>) -> Int;
nintbin2int(<<>>) -> 0.
%% Test setopt/getopt of multiple raw options.
multiple_raw(Config) when is_list(Config) ->
do_multiple_raw(Config,false).
%% Test setopt/getopt of multiple raw options, with binaries in
%% getopt.
multiple_raw_getbin(Config) when is_list(Config) ->
do_multiple_raw(Config,true).
do_multiple_raw(Config, Binary) ->
Port = start_helper(Config),
SolSocket = ask_helper(Port, ?C_GET_SOL_SOCKET),
SoKeepalive = ask_helper(Port, ?C_GET_SO_KEEPALIVE),
SoKeepaliveTrue = {raw,SolSocket,SoKeepalive,<<1:32/native>>},
SoKeepaliveFalse = {raw,SolSocket,SoKeepalive,<<0:32/native>>},
SoReuseaddr = ask_helper(Port, ?C_GET_SO_REUSEADDR),
SoReuseaddrTrue = {raw,SolSocket,SoReuseaddr,<<1:32/native>>},
SoReuseaddrFalse = {raw,SolSocket,SoReuseaddr,<<0:32/native>>},
{S1,S2} =
create_socketpair(
[SoReuseaddrFalse,SoKeepaliveTrue],
[SoKeepaliveFalse,SoReuseaddrTrue]),
{ok,[{reuseaddr,false},{keepalive,true}]} =
inet:getopts(S1, [reuseaddr,keepalive]),
{ok,
[{raw,SolSocket,SoReuseaddr,S1R1},
{raw,SolSocket,SoKeepalive,S1K1}]} =
inet:getopts(
S1,
[{raw,SolSocket,SoReuseaddr,binarify(4, Binary)},
{raw,SolSocket,SoKeepalive,binarify(4, Binary)}]),
true = nintbin2int(S1R1) =:= 0,
true = nintbin2int(S1K1) =/= 0,
{ok,[{keepalive,false},{reuseaddr,true}]} =
inet:getopts(S2, [keepalive,reuseaddr]),
{ok,
[{raw,SolSocket,SoKeepalive,S2K1},
{raw,SolSocket,SoReuseaddr,S2R1}]} =
inet:getopts(
S2,
[{raw,SolSocket,SoKeepalive,binarify(4, Binary)},
{raw,SolSocket,SoReuseaddr,binarify(4, Binary)}]),
true = nintbin2int(S2K1) =:= 0,
true = nintbin2int(S2R1) =/= 0,
%%
ok = inet:setopts(
S1, [SoReuseaddrTrue,SoKeepaliveFalse]),
ok = inet:setopts(
S2, [SoKeepaliveTrue,SoReuseaddrFalse]),
{ok,
[{raw,SolSocket,SoReuseaddr,S1R2},
{raw,SolSocket,SoKeepalive,S1K2}]} =
inet:getopts(
S1,
[{raw,SolSocket,SoReuseaddr,binarify(4, Binary)},
{raw,SolSocket,SoKeepalive,binarify(4, Binary)}]),
true = nintbin2int(S1R2) =/= 0,
true = nintbin2int(S1K2) =:= 0,
{ok,
[{raw,SolSocket,SoKeepalive,S2K2},
{raw,SolSocket,SoReuseaddr,S2R2}]} =
inet:getopts(
S2,
[{raw,SolSocket,SoKeepalive,binarify(4, Binary)},
{raw,SolSocket,SoReuseaddr,binarify(4, Binary)}]),
true = nintbin2int(S2K2) =/= 0,
true = nintbin2int(S2R2) =:= 0,
%%
gen_tcp:close(S1),
gen_tcp:close(S2),
stop_helper(Port),
ok.
%% Test that the example code from the documentation works.
doc_examples_raw(Config) when is_list(Config) ->
do_doc_examples_raw(Config,false).
%% Test that the example code from the documentation works when getopt
%% uses binaries.
doc_examples_raw_getbin(Config) when is_list(Config) ->
do_doc_examples_raw(Config,true).
do_doc_examples_raw(Config,Binary) when is_list(Config) ->
Port = start_helper(Config),
Proto = ask_helper(Port,?C_GET_IPPROTO_TCP),
TcpInfo = ask_helper(Port,?C_GET_TCP_INFO),
TcpInfoSize = ask_helper(Port,?C_GET_TCP_INFO_SIZE),
TcpiSackedOffset = ask_helper(Port,?C_GET_OFF_TCPI_SACKED),
TcpiOptionsOffset = ask_helper(Port,?C_GET_OFF_TCPI_OPTIONS),
TcpiSackedSize = ask_helper(Port,?C_GET_SIZ_TCPI_SACKED),
TcpiOptionsSize = ask_helper(Port,?C_GET_SIZ_TCPI_OPTIONS),
TcpLinger2 = ask_helper(Port,?C_GET_TCP_LINGER2),
stop_helper(Port),
case all_ok([Proto,TcpInfo,TcpInfoSize,TcpiSackedOffset,
TcpiOptionsOffset,TcpiSackedSize,TcpiOptionsSize,
TcpLinger2]) of
false ->
{skipped,"Does not run on this OS."};
true ->
{Sock,I} = create_socketpair([],[]),
{ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} =
inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]),
NewLinger = OrigLinger div 2,
ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2,
<<NewLinger:32/native>>}]),
{ok,[{raw,Proto,TcpLinger2,<<NewLinger:32/native>>}]} =
inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]),
ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2,
<<OrigLinger:32/native>>}]),
{ok,[{raw,Proto,TcpLinger2,<<OrigLinger:32/native>>}]} =
inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]),
{ok,[{raw,_,_,Info}]} =
inet:getopts(Sock,[{raw,Proto,TcpInfo,
binarify(TcpInfoSize,Binary)}]),
Bit1 = TcpiSackedSize * 8,
<<_:TcpiSackedOffset/binary,
TcpiSacked:Bit1/native,_/binary>> =
Info,
0 = TcpiSacked,
Bit2 = TcpiOptionsSize * 8,
<<_:TcpiOptionsOffset/binary,
TcpiOptions:Bit2/native,_/binary>> =
Info,
true = TcpiOptions =/= 0,
gen_tcp:close(Sock),
gen_tcp:close(I),
ok
end.
%% Test structs and large/too large buffers when raw.
large_raw(Config) when is_list(Config) ->
do_large_raw(Config,false).
%% Test structs and large/too large buffers when raw
%% using binaries to getopts.
large_raw_getbin(Config) when is_list(Config) ->
do_large_raw(Config,true).
do_large_raw(Config,Binary) when is_list(Config) ->
Port = start_helper(Config),
Proto = ask_helper(Port,?C_GET_SOL_SOCKET),
Linger = ask_helper(Port,?C_GET_SO_LINGER),
LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE),
LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF),
LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER),
LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF),
LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER),
stop_helper(Port),
case all_ok([Proto,Linger,LingerSize,LingerOnOffOffset,
LingerLingerOffset,LingerOnOffSize,LingerLingerSize]) of
false ->
{skipped,"Does not run on this OS."};
true ->
{Sock1,Sock2} = create_socketpair([{linger,{true,10}}],
[{linger,{false,0}}]),
LargeSize = 1024, % Solaris can take up to 1024*9,
% linux 1024*63...
TooLargeSize = 1024*64,
{ok,[{raw,Proto,Linger,Linger1}]} =
inet:getopts(Sock1,[{raw,Proto,Linger,
binarify(LargeSize,Binary)}]),
{ok,[{raw,Proto,Linger,Linger2}]} =
inet:getopts(Sock2,[{raw,Proto,Linger,
binarify(LingerSize,Binary)}]),
true = byte_size(Linger1) =:= LingerSize,
LingerLingerBits = LingerLingerSize * 8,
LingerOnOffBits = LingerOnOffSize * 8,
<<_:LingerLingerOffset/binary,
Ling1:LingerLingerBits/native,_/binary>> = Linger1,
<<_:LingerOnOffOffset/binary,
Off1:LingerOnOffBits/native,_/binary>> = Linger1,
<<_:LingerOnOffOffset/binary,
Off2:LingerOnOffBits/native,_/binary>> = Linger2,
true = Off1 =/= 0,
true = Off2 == 0,
true = Ling1 == 10,
{error,einval} =
inet:getopts(Sock1,[{raw,Proto,Linger,TooLargeSize}]),
gen_tcp:close(Sock1),
gen_tcp:close(Sock2),
ok
end.
%% Test raw structs combined w/ other options .
combined(Config) when is_list(Config) ->
do_combined(Config,false).
%% Test raw structs combined w/ other options and
%% binarise in getopts.
combined_getbin(Config) when is_list(Config) ->
do_combined(Config,true).
do_combined(Config,Binary) when is_list(Config) ->
Port = start_helper(Config),
Proto = ask_helper(Port,?C_GET_SOL_SOCKET),
Linger = ask_helper(Port,?C_GET_SO_LINGER),
LingerSize = ask_helper(Port,?C_GET_LINGER_SIZE),
LingerOnOffOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_ONOFF),
LingerLingerOffset = ask_helper(Port,?C_GET_OFF_LINGER_L_LINGER),
LingerOnOffSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_ONOFF),
LingerLingerSize = ask_helper(Port,?C_GET_SIZ_LINGER_L_LINGER),
stop_helper(Port),
case all_ok([Proto,Linger,LingerSize,LingerOnOffOffset,
LingerLingerOffset,LingerOnOffSize,LingerLingerSize]) of
false ->
{skipped,"Does not run on this OS."};
true ->
LingerLingerBits = LingerLingerSize * 8,
LingerOnOffBits = LingerOnOffSize * 8,
{LingerOn,LingerOff} =
case LingerOnOffOffset < LingerLingerOffset of
true ->
Pad1 =
list_to_binary(
lists:duplicate(LingerOnOffOffset,
0)),
Pad2Siz =
LingerLingerOffset - LingerOnOffSize -
LingerOnOffOffset,
Pad2 =
list_to_binary(
lists:duplicate(Pad2Siz,
0)),
Pad3Siz = LingerSize - LingerLingerSize -
LingerLingerOffset,
Pad3 = list_to_binary(
lists:duplicate(Pad3Siz,
0)),
{<<Pad1/binary,1:LingerOnOffBits/native,
Pad2/binary,10:LingerLingerBits/native,
Pad3/binary>>,
<<Pad1/binary,0:LingerOnOffBits/native,
Pad2/binary,0:LingerLingerBits/native,
Pad3/binary>>};
false ->
Pad1 =
list_to_binary(
lists:duplicate(LingerLingerOffset,
0)),
Pad2Siz =
LingerOnOffOffset - LingerLingerSize -
LingerLingerOffset,
Pad2 =
list_to_binary(
lists:duplicate(Pad2Siz,
0)),
Pad3Siz = LingerSize - LingerOnOffSize -
LingerOnOffOffset,
Pad3 = list_to_binary(
lists:duplicate(Pad3Siz,
0)),
{<<Pad1/binary,1:LingerLingerBits/native,
Pad2/binary,10:LingerOnOffBits/native,
Pad3/binary>>,
<<Pad1/binary,0:LingerLingerBits/native,
Pad2/binary,0:LingerOnOffBits/native,
Pad3/binary>>}
end,
RawLingerOn = {raw,Proto,Linger,LingerOn},
RawLingerOff = {raw,Proto,Linger,LingerOff},
{Sock1,Sock2} =
create_socketpair([{keepalive,true},
RawLingerOn],
[{keepalive,false},
RawLingerOff]),
{ok,[{raw,Proto,Linger,Linger1},{keepalive,Keep1}]} =
inet:getopts(Sock1,[{raw,Proto,Linger,
binarify(LingerSize,Binary)},keepalive]),
{ok,[{raw,Proto,Linger,Linger2},{keepalive,Keep2}]} =
inet:getopts(Sock2,[{raw,Proto,Linger,
binarify(LingerSize,Binary)},keepalive]),
true = byte_size(Linger1) =:= LingerSize,
<<_:LingerLingerOffset/binary,
Ling1:LingerLingerBits/native,_/binary>> = Linger1,
<<_:LingerOnOffOffset/binary,
Off1:LingerOnOffBits/native,_/binary>> = Linger1,
<<_:LingerOnOffOffset/binary,
Off2:LingerOnOffBits/native,_/binary>> = Linger2,
true = Off1 =/= 0,
true = Off2 == 0,
true = Ling1 == 10,
true = Keep1 =:= true,
true = Keep2 =:= false,
{Sock3,Sock4} =
create_socketpair([RawLingerOn,{keepalive,true}],
[RawLingerOff,{keepalive,false}]),
{ok,[{raw,Proto,Linger,Linger3},{keepalive,Keep3}]} =
inet:getopts(Sock3,[{raw,Proto,Linger,
binarify(LingerSize,Binary)},keepalive]),
{ok,[{raw,Proto,Linger,Linger4},{keepalive,Keep4}]} =
inet:getopts(Sock4,[{raw,Proto,Linger,
binarify(LingerSize,Binary)},keepalive]),
true = byte_size(Linger3) =:= LingerSize,
<<_:LingerLingerOffset/binary,
Ling3:LingerLingerBits/native,_/binary>> = Linger3,
<<_:LingerOnOffOffset/binary,
Off3:LingerOnOffBits/native,_/binary>> = Linger3,
<<_:LingerOnOffOffset/binary,
Off4:LingerOnOffBits/native,_/binary>> = Linger4,
true = Off3 =/= 0,
true = Off4 == 0,
true = Ling3 == 10,
true = Keep3 =:= true,
true = Keep4 =:= false,
{Sock5,Sock6} =
create_socketpair([{packet,4},RawLingerOn,{keepalive,true}],
[{packet,2},RawLingerOff,{keepalive,false}]),
{ok,[{packet,Pack5},{raw,Proto,Linger,Linger5},
{keepalive,Keep5}]} =
inet:getopts(Sock5,[packet,{raw,Proto,Linger,
binarify(LingerSize,Binary)},
keepalive]),
{ok,[{packet,Pack6},{raw,Proto,Linger,Linger6},
{keepalive,Keep6}]} =
inet:getopts(Sock6,[packet,{raw,Proto,Linger,
binarify(LingerSize,Binary)},
keepalive]),
true = byte_size(Linger5) =:= LingerSize,
<<_:LingerLingerOffset/binary,
Ling5:LingerLingerBits/native,_/binary>> = Linger5,
<<_:LingerOnOffOffset/binary,
Off5:LingerOnOffBits/native,_/binary>> = Linger5,
<<_:LingerOnOffOffset/binary,
Off6:LingerOnOffBits/native,_/binary>> = Linger6,
true = Off5 =/= 0,
true = Off6 == 0,
true = Ling5 == 10,
true = Keep5 =:= true,
true = Keep6 =:= false,
true = Pack5 =:= 4,
true = Pack6 =:= 2,
inet:setopts(Sock6,[{packet,4},RawLingerOn,
{keepalive,true}]),
{ok,[{packet,Pack7},{raw,Proto,Linger,Linger7},
{keepalive,Keep7}]} =
inet:getopts(Sock6,[packet,{raw,Proto,Linger,
binarify(LingerSize,Binary)},
keepalive]),
<<_:LingerOnOffOffset/binary,
Off7:LingerOnOffBits/native,_/binary>> = Linger7,
true = Off7 =/= 0,
true = Keep7 =:= true,
true = Pack7 =:= 4,
gen_tcp:close(Sock1),
gen_tcp:close(Sock2),
gen_tcp:close(Sock3),
gen_tcp:close(Sock4),
gen_tcp:close(Sock5),
gen_tcp:close(Sock6),
ok
end.
%% Test socket option ipv6_v6only for UDP.
ipv6_v6only_udp(Config) when is_list(Config) ->
ipv6_v6only(Config, gen_udp).
%% Test socket option ipv6_v6only for TCP.
ipv6_v6only_tcp(Config) when is_list(Config) ->
ipv6_v6only(Config, gen_tcp).
%% Test socket option ipv6_v6only for SCTP.
ipv6_v6only_sctp(Config) when is_list(Config) ->
ipv6_v6only(Config, gen_sctp).
ipv6_v6only(Config, Module) when is_list(Config) ->
case ipv6_v6only_open(Module, []) of
{ok,S1} ->
case inet:getopts(S1, [ipv6_v6only]) of
{ok,[{ipv6_v6only,Default}]}
when is_boolean(Default) ->
ok =
ipv6_v6only_close(Module, S1),
ipv6_v6only(Config, Module, Default);
{ok,[]} ->
io:format("Not implemented.~n", []),
%% This list of OS:es where the option is
%% supposed to be not implemented is just
%% a guess, and may grow with time.
case {os:type(),os:version()} of
{{unix,linux},{2,M,_}}
when M =< 4 -> ok
end,
%% At least this should work
{ok,S2} =
ipv6_v6only_open(
Module,
[{ipv6_v6only,true}]),
ok =
ipv6_v6only_close(Module, S2)
end;
{error,_} ->
{skipped,"Socket type not supported"}
end.
ipv6_v6only(Config, Module, Default) when is_list(Config) ->
io:format("Default ~w.~n", [Default]),
{ok,S1} =
ipv6_v6only_open(Module, [{ipv6_v6only,Default}]),
{ok,[{ipv6_v6only,Default}]} =
inet:getopts(S1, [ipv6_v6only]),
ok =
ipv6_v6only_close(Module, S1),
NotDefault = not Default,
case ipv6_v6only_open(Module, [{ipv6_v6only,NotDefault}]) of
{ok,S2} ->
io:format("Read-write.~n", []),
{ok,[{ipv6_v6only,NotDefault}]} =
inet:getopts(S2, [ipv6_v6only]),
ok;
{error,einval} ->
io:format("Read-only.~n", []),
%% This option is known to be read-only and true
%% on Windows and OpenBSD
case os:type() of
{unix,openbsd} when Default =:= true -> ok;
{win32,_} when Default =:= true -> ok
end
end.
ipv6_v6only_open(Module, Opts) ->
Module:case Module of
gen_tcp -> listen;
_ -> open
end(0, [inet6|Opts]).
ipv6_v6only_close(Module, Socket) ->
Module:close(Socket).
%% Test using socket option ipv6_v6only for UDP.
use_ipv6_v6only_udp(Config) when is_list(Config) ->
case gen_udp:open(0, [inet6,{ip,{0,0,0,0,0,0,0,1}}, {ipv6_v6only,true}]) of
{ok,S6} ->
case inet:getopts(S6, [ipv6_v6only]) of
{ok,[{ipv6_v6only,true}]} ->
use_ipv6_v6only_udp(Config, S6);
{ok,Other} ->
{skipped,{getopts,Other}}
end;
{error,_} ->
{skipped,"Socket type not supported"}
end.
use_ipv6_v6only_udp(_Config, S6) ->
{ok,Port} = inet:port(S6),
{ok,S4} = gen_udp:open(Port, [inet]),
E6 = " IPv6-echo.",
E4 = " IPv4-echo.",
Sender =
spawn_link(fun () -> use_ipv6_v6only_udp_sender(Port, E6, E4) end),
use_ipv6_v6only_udp_listener(
S6, S4, E6, E4, monitor(process, Sender)).
use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref) ->
receive
{udp,S6,IP,P,Data} ->
ok = gen_udp:send(S6, IP, P, [Data|E6]),
use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref);
{udp,S4,IP,P,Data} ->
ok = gen_udp:send(S4, IP, P, [Data|E4]),
use_ipv6_v6only_udp_listener(S6, S4, E6, E4, Mref);
{'DOWN',Mref,_,_,normal} ->
ok;
{'DOWN',Mref,_,_,Result} ->
%% Since we are linked we will never arrive here
Result;
Other ->
exit({failed,{listener_unexpected,Other}})
end.
use_ipv6_v6only_udp_sender(Port, E6, E4) ->
D6 = "IPv6-send.",
D4 = "IPv4-send.",
R6 = D6 ++ E6,
R4 = D4 ++ E4,
R6 = sndrcv({0,0,0,0,0,0,0,1}, Port, [inet6], D6),
R4 = sndrcv({127,0,0,1}, Port, [inet], D4),
ok.
sndrcv(Ip, Port, Opts, Data) ->
{ok,S} = gen_udp:open(0, Opts),
io:format("[~w:~w] ! ~s~n", [Ip,Port,Data]),
ok = gen_udp:send(S, Ip, Port, Data),
receive
{udp,S,Ip,Port,RecData} ->
io:format("[~w:~w] : ~s~n", [Ip,Port,RecData]),
RecData;
Other ->
exit({failed,{sndrcv_unexpectec,Other}})
end.
%% Test that raw data requests are not executed for bad types.
type_errors(Config) when is_list(Config) ->
BadSetOptions =
[
{raw,x,3,<<1:32>>},
{raw,1,tre,<<1:32>>},
{raw,1,3,ko},
{raw,1,3,5},
{raw,1,3},
{raw,1},
{raw},
{raw,ett},
{raw,ett,tre},
{raw,{true,10}},
{raw,{ett,tre,<<1:32>>}},
{rav,1,3,<<1:32>>},
raw,
rav,
{linger,banan}
],
BadGetOptions =
[
{raw,x,3,<<1:32>>},
{raw,1,tre,<<1:32>>},
{raw,1,3,ko},
{raw,1,3,5.1},
{raw,1,3,-3},
{raw,1,3},
{raw,1},
{raw},
{raw,ett},
{raw,ett,tre},
{raw,{true,10}},
{raw,{ett,tre,<<1:32>>}},
{rav,1,3,<<1:32>>},
raw,
rav,
{linger,banan}
],
lists:foreach(fun(Option) ->
case
catch create_socketpair([Option],[]) of
{'EXIT',badarg} ->
ok;
Unexpected1 ->
exit({unexpected,
Unexpected1})
end,
case
catch create_socketpair([],[Option]) of
{'EXIT',badarg} ->
ok;
Unexpected2 ->
exit({unexpected,
Unexpected2})
end,
{Sock1,Sock2} = create_socketpair([],[]),
case inet:setopts(Sock1, [Option]) of
{error,einval} ->
ok;
Unexpected3 ->
exit({unexpected,
Unexpected3})
end,
gen_tcp:close(Sock1),
gen_tcp:close(Sock2)
end,BadSetOptions),
{Sock1,Sock2} = create_socketpair([],[]),
lists:foreach(fun(Option) ->
case inet:getopts(Sock1, [Option]) of
{error,einval} ->
ok;
Unexpected ->
exit({unexpected,
Unexpected})
end
end,BadGetOptions),
gen_tcp:close(Sock1),
gen_tcp:close(Sock2),
ok.
all_ok([]) ->
true;
all_ok([H|T]) when H >= 0 ->
all_ok(T);
all_ok(_) ->
false.
make_check_fun(Type,Element) ->
fun({Name,V1,V2,Mand,Chang},Acc) ->
{LO1,CO1} = setelement(Element,{[],[]}, [{Name,V1}]),
{LO2,CO2} = setelement(Element,{[],[]}, [{Name,V2}]),
{X1,Y1} = create_socketpair(LO1,CO1),
{X2,Y2} = create_socketpair(LO2,CO2),
S1 = element(Element,{X1,Y1}),
S2 = element(Element,{X2,Y2}),
{ok,[{Name,R1}]} = inet:getopts(S1,[Name]),
{ok,[{Name,R2}]} = inet:getopts(S2,[Name]),
NewAcc =
case R1 =/= R2 of
true ->
case Chang of
true ->
inet:setopts(S1,[{Name,V2}]),
{ok,[{Name,R3}]} =
inet:getopts(S1,[Name]),
case {R3 =/= R1, R3 =:= R2} of
{true,true} ->
Acc;
_ ->
case Mand of
true ->
exit
({failed_sockopt,
{change,
Name}});
false ->
[{change,Name}|Acc]
end
end;
false ->
Acc
end;
false ->
case Mand of
true ->
exit({failed_sockopt,
{Type,Name}});
false ->
[{Type,Name}|Acc]
end
end,
gen_tcp:close(X1),
gen_tcp:close(Y1),
gen_tcp:close(X2),
gen_tcp:close(Y2),
NewAcc
end.
%% {OptionName,Value1,Value2,Mandatory,Changeable}
all_listen_options() ->
[{tos,0,1,false,true},
{priority,0,1,false,true},
{reuseaddr,false,true,false,true},
{keepalive,false,true,true,true},
{linger, {false,10}, {true,10},true,true},
{sndbuf,2048,4096,false,true},
{recbuf,2048,4096,false,true},
{nodelay,false,true,true,true},
{header,2,4,true,true},
{active,false,true,true,false},
{packet,2,4,true,true},
{buffer,1000,2000,true,true},
{mode,list,binary,true,true},
{deliver,term,port,true,true},
{exit_on_close, true, false, true, true},
{high_watermark,4096,8192,true,true},
{low_watermark,2048,4096,true,true},
{high_msgq_watermark,4096,8192,true,true},
{low_msgq_watermark,2048,4096,true,true},
{send_timeout,infinity,1000,true,true},
{send_timeout_close,false,true,true,true},
{delay_send,false,true,true,true},
{packet_size,0,4,true,true}
].
all_connect_options() ->
[{tos,0,1,false,true},
{priority,0,1,false,true},
{reuseaddr,false,true,false,true},
{keepalive,false,true,true,true},
{linger, {false,10}, {true,10},true,true},
{sndbuf,2048,4096,false,true},
{recbuf,2048,4096,false,true},
{nodelay,false,true,true,true},
{header,2,4,true,true},
{active,false,true,true,false},
{packet,2,4,true,true},
{buffer,1000,2000,true,true},
{mode,list,binary,true,true},
{deliver,term,port,true,true},
{exit_on_close, true, false, true, true},
{high_watermark,4096,8192,false,true},
{low_watermark,2048,4096,false,true},
{high_msgq_watermark,4096,8192,true,true},
{low_msgq_watermark,2048,4096,true,true},
{send_timeout,infinity,1000,true,true},
{send_timeout_close,false,true,true,true},
{delay_send,false,true,true,true},
{packet_size,0,4,true,true}
].
create_socketpair(ListenOptions,ConnectOptions) ->
{ok,LS}=gen_tcp:listen(0,ListenOptions),
{ok,Port}=inet:port(LS),
{ok,CS}=gen_tcp:connect(localhost,Port,ConnectOptions),
{ok,AS}=gen_tcp:accept(LS),
gen_tcp:close(LS),
{AS,CS}.
start_helper(Config) ->
Progname = filename:join(proplists:get_value(data_dir, Config), "sockopt_helper"),
Port = open_port({spawn,Progname},[eof,line]),
Port.
ask_helper(Port,Code) ->
Com = integer_to_list(Code)++"\n",
Port ! {self(),{command,Com}},
receive
{Port,{data,{eol,Text}}} ->
list_to_integer(Text);
Other ->
exit({error,{unexpected_data_from_helper,Other}})
after 3000 ->
exit({error,helper_timeout})
end.
stop_helper(Port) ->
catch ask_helper(Port,?C_QUIT),
receive
{Port,eof} ->
Port ! {self(), close},
receive
{Port,closed} ->
ok
after 1000 ->
timeout
end
after 1000 ->
timeout
end.
binarify(Size,Binary) when Binary =:= true ->
<<0:Size/unit:8>>;
binarify(Size,Binary) when Binary =:= false ->
Size.