%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2007-2016. 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; nintbin2int(<>) -> Int; nintbin2int(<>) -> Int; nintbin2int(<>) -> 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,<>}]} = inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]), NewLinger = OrigLinger div 2, ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2, <>}]), {ok,[{raw,Proto,TcpLinger2,<>}]} = inet:getopts(Sock,[{raw,Proto,TcpLinger2,binarify(4,Binary)}]), ok = inet:setopts(Sock,[{raw,Proto,TcpLinger2, <>}]), {ok,[{raw,Proto,TcpLinger2,<>}]} = 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)), {<>, <>}; 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)), {<>, <>} 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,{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.