diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/kernel/doc/src/gen_udp.xml | 87 | ||||
-rw-r--r-- | lib/kernel/doc/src/inet.xml | 50 | ||||
-rw-r--r-- | lib/kernel/src/gen_udp.erl | 75 | ||||
-rw-r--r-- | lib/kernel/src/inet.erl | 16 | ||||
-rw-r--r-- | lib/kernel/src/inet6_udp.erl | 23 | ||||
-rw-r--r-- | lib/kernel/src/inet_udp.erl | 23 | ||||
-rw-r--r-- | lib/kernel/src/local_udp.erl | 8 | ||||
-rw-r--r-- | lib/kernel/test/gen_tcp_misc_SUITE.erl | 6 | ||||
-rw-r--r-- | lib/kernel/test/gen_udp_SUITE.erl | 143 |
9 files changed, 377 insertions, 54 deletions
diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index d20fc1fdfd..6c0d072fed 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -213,12 +213,93 @@ </func> <func> - <name name="send" arity="4" since=""/> + <name name="send" arity="3" since="OTP @OTP-15747@"/> <fsummary>Send a packet.</fsummary> <desc> <p> - Sends a packet to the specified address and port. Argument - <c><anno>Address</anno></c> can be a hostname or a socket address. + Sends a packet to the specified <c><anno>Destination</anno></c>. + </p> + <p> + This function is equivalent to + <seealso marker="#send-4-AncData"><c>send(<anno>Socket</anno>, <anno>Destination</anno>, [], <anno>Packet</anno>)</c></seealso>. + </p> + </desc> + </func> + + <func> + <name name="send" arity="4" clause_i="1" since=""/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Host</anno></c> + and <c><anno>Port</anno></c>. + </p> + <p> + This clause is equivalent to + <seealso marker="#send/5"><c>send(<anno>Socket</anno>, <anno>Host</anno>, <anno>Port</anno>, [], <anno>Packet</anno>)</c></seealso>. + </p> + </desc> + </func> + + <func> + <name name="send" arity="4" clause_i="2" anchor="send-4-AncData" since="OTP @OTP-15747@"/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Destination</anno></c> + with ancillary data <c><anno>AncData</anno></c>. + </p> + <note> + <p> + The ancillary data <c><anno>AncData</anno></c> + contains options that for this single message + override the default options for the socket, + an operation that may not be supported on all platforms, + and if so return <c>{error, einval}</c>. + Using more than one of an ancillary data item type + may also not be supported. + <c><anno>AncData</anno> =:= []</c> is always supported. + </p> + </note> + </desc> + </func> + + <func> + <name name="send" arity="4" clause_i="3" since="OTP @OTP-15747@"/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Destination</anno></c>. + Since <c><anno>Destination</anno></c> is complete, + <c><anno>PortZero</anno></c> is redundant and has to be <c>0</c>. + </p> + <p> + This is a legacy clause mostly for + <c><anno>Destination</anno> = {local, Binary}</c> + where <c><anno>PortZero</anno></c> is superfluous. + It is equivalent to + <seealso marker="#send-4-AncData"><c>send(<anno>Socket</anno>, <anno>Destination</anno>, [], <anno>Packet</anno>)</c></seealso>, the clause right above here. + </p> + </desc> + </func> + + <func> + <name name="send" arity="5" since="OTP @OTP-15747@"/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Host</anno></c> + and <c><anno>Port</anno></c>, + with ancillary data <c><anno>AncData</anno></c>. + </p> + <p> + Argument <c><anno>Host</anno></c> can be + a hostname or a socket address, + and <c><anno>Port</anno></c> can be a port number + or a service name atom. + These are resolved into a <c>Destination</c> and after that + this function is equivalent to + <seealso marker="#send-4-AncData"><c>send(<anno>Socket</anno>, Destination, <anno>AncData</anno>, <anno>Packet</anno>)</c></seealso>, read there about ancillary data. </p> </desc> </func> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 5e33bbc3ff..adaa2684d8 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -118,6 +118,42 @@ fe80::204:acff:fe17:bf38 <name name="port_number"/> </datatype> <datatype> + <name name="family_address" since="OTP @OTP-15747@"/> + <desc> + <p> + A general address format on the form <c>{Family, Destination}</c> + where <c>Family</c> is an atom such as <c>local</c> + and the format of <c>Destination</c> depends on <c>Family</c>, + and is a complete address + (for example an IP address including port number). + </p> + </desc> + </datatype> + <datatype> + <name name="inet_address" since="OTP @OTP-15747@"/> + <desc> + <warning> + <p> + This address format is for now experimental + and for completeness to make all address families have a + <c>{Family, Destination}</c> representation. + </p> + </warning> + </desc> + </datatype> + <datatype> + <name name="inet6_address" since="OTP @OTP-15747@"/> + <desc> + <warning> + <p> + This address format is for now experimental + and for completeness to make all address families have a + <c>{Family, Destination}</c> representation. + </p> + </warning> + </desc> + </datatype> + <datatype> <name name="local_address"/> <desc> <p> @@ -180,12 +216,16 @@ fe80::204:acff:fe17:bf38 <name name="ancillary_data"/> <desc> <p> - Ancillary data received with the data packet - or read with the socket option + Ancillary data received with the data packet, + read with the socket option <seealso marker="gen_tcp#type-pktoptions_value"> <c>pktoptions</c> </seealso> - from a TCP socket. + from a TCP socket, + or to set in a call to + <seealso marker="gen_udp#send-4-AncData"><c>gen_udp:send/4</c></seealso> + or + <seealso marker="gen_udp#send/5"><c>gen_udp:send/5</c></seealso>. </p> <p> The value(s) correspond to the currently active socket @@ -193,7 +233,9 @@ fe80::204:acff:fe17:bf38 <seealso marker="inet#option-recvtos"><c>recvtos</c></seealso>, <seealso marker="inet#option-recvtclass"><c>recvtclass</c></seealso> and - <seealso marker="inet#option-recvttl"><c>recvttl</c></seealso>. + <seealso marker="inet#option-recvttl"><c>recvttl</c></seealso>, + or for a single send operation the option(s) to override + the currently active socket option(s). </p> </desc> </datatype> diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index d6e8652e77..247ebc50f3 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -20,7 +20,7 @@ -module(gen_udp). -export([open/1, open/2, close/1]). --export([send/2, send/4, recv/2, recv/3, connect/3]). +-export([send/2, send/3, send/4, send/5, recv/2, recv/3, connect/3]). -export([controlling_process/2]). -export([fdopen/2]). @@ -125,20 +125,80 @@ open(Port, Opts0) -> close(S) -> inet:udp_close(S). --spec send(Socket, Address, Port, Packet) -> ok | {error, Reason} when +-spec send(Socket, Destination, Packet) -> ok | {error, Reason} when Socket :: socket(), - Address :: inet:socket_address() | inet:hostname(), - Port :: inet:port_number(), + Destination :: {inet:ip_address(), inet:port_number()} | + inet:family_address(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(). +%%% +send(Socket, Destination, Packet) -> + send(Socket, Destination, [], Packet). + +-spec send(Socket, Host, Port, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Host :: inet:hostname() | inet:ip_address(), + Port :: inet:port_number() | atom(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(); +%%% + (Socket, Destination, AncData, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Destination :: {inet:ip_address(), inet:port_number()} | + inet:family_address(), + AncData :: inet:ancillary_data(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(); +%%% + (Socket, Destination, PortZero, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Destination :: {inet:ip_address(), inet:port_number()} | + inet:family_address(), + PortZero :: inet:port_number(), Packet :: iodata(), Reason :: not_owner | inet:posix(). +%%% +send(S, {_,_} = Destination, PortZero = AncData, Packet) when is_port(S) -> + %% Destination is {Family,Addr} | {IP,Port}, + %% so it is complete - argument PortZero is redundant + if + PortZero =:= 0 -> + case inet_db:lookup_socket(S) of + {ok, Mod} -> + Mod:send(S, Destination, [], Packet); + Error -> + Error + end; + is_integer(PortZero) -> + %% Redundant PortZero; must be 0 + {error, einval}; + is_list(AncData) -> + case inet_db:lookup_socket(S) of + {ok, Mod} -> + Mod:send(S, Destination, AncData, Packet); + Error -> + Error + end + end; +send(S, Host, Port, Packet) when is_port(S) -> + send(S, Host, Port, [], Packet). -send(S, Address, Port, Packet) when is_port(S) -> +-spec send(Socket, Host, Port, AncData, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Host :: inet:hostname() | inet:ip_address() | inet:local_address(), + Port :: inet:port_number() | atom(), + AncData :: inet:ancillary_data(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(). +%%% +send(S, Host, Port, AncData, Packet) + when is_port(S), is_list(AncData) -> case inet_db:lookup_socket(S) of {ok, Mod} -> - case Mod:getaddr(Address) of + case Mod:getaddr(Host) of {ok,IP} -> case Mod:getserv(Port) of - {ok,UP} -> Mod:send(S, IP, UP, Packet); + {ok,P} -> Mod:send(S, {IP,P}, AncData, Packet); {error,einval} -> exit(badarg); Error -> Error end; @@ -149,6 +209,7 @@ send(S, Address, Port, Packet) when is_port(S) -> Error end. +%% Connected send send(S, Packet) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 9f22eb6aaa..7940903658 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -75,7 +75,8 @@ -export_type([address_family/0, socket_protocol/0, hostent/0, hostname/0, ip4_address/0, ip6_address/0, ip_address/0, port_number/0, - local_address/0, socket_address/0, returned_non_ip_address/0, + family_address/0, local_address/0, + socket_address/0, returned_non_ip_address/0, socket_setopt/0, socket_getopt/0, ancillary_data/0, posix/0, socket/0, stat_option/0]). %% imports @@ -100,11 +101,16 @@ 0..65535,0..65535,0..65535,0..65535}. -type ip_address() :: ip4_address() | ip6_address(). -type port_number() :: 0..65535. --type local_address() :: {local, File :: binary() | string()}. +-type family_address() :: inet_address() | inet6_address() | local_address(). +-type inet_address() :: + {'inet', {ip4_address() | 'any' | 'loopback', port_number()}}. +-type inet6_address() :: + {'inet6', {ip6_address() | 'any' | 'loopback', port_number()}}. +-type local_address() :: {'local', File :: binary() | string()}. -type returned_non_ip_address() :: - {local, binary()} | - {unspec, <<>>} | - {undefined, any()}. + {'local', binary()} | + {'unspec', <<>>} | + {'undefined', any()}. -type posix() :: 'eaddrinuse' | 'eaddrnotavail' | 'eafnosupport' | 'ealready' | 'econnaborted' | 'econnrefused' | 'econnreset' | diff --git a/lib/kernel/src/inet6_udp.erl b/lib/kernel/src/inet6_udp.erl index 71db0357cd..cb95a69798 100644 --- a/lib/kernel/src/inet6_udp.erl +++ b/lib/kernel/src/inet6_udp.erl @@ -65,16 +65,25 @@ open(Port, Opts) -> {ok, _} -> exit(badarg) end. -send(S, Addr = {A,B,C,D,E,F,G,H}, P, Data) - when ?ip6(A,B,C,D,E,F,G,H), ?port(P) -> - prim_inet:sendto(S, Addr, P, Data). +send(S, {A,B,C,D,E,F,G,H} = IP, Port, Data) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) -> + prim_inet:sendto(S, {IP, Port}, [], Data); +send(S, {{A,B,C,D,E,F,G,H}, Port} = Addr, AncData, Data) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Addr, AncData, Data); +send(S, {?FAMILY, {{A,B,C,D,E,F,G,H}, Port}} = Address, AncData, Data) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data); +send(S, {?FAMILY, {loopback, Port}} = Address, AncData, Data) + when ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data). send(S, Data) -> - prim_inet:sendto(S, {0,0,0,0,0,0,0,0}, 0, Data). + prim_inet:sendto(S, {any, 0}, [], Data). -connect(S, Addr = {A,B,C,D,E,F,G,H}, P) - when ?ip6(A,B,C,D,E,F,G,H), ?port(P) -> - prim_inet:connect(S, Addr, P). +connect(S, Addr = {A,B,C,D,E,F,G,H}, Port) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) -> + prim_inet:connect(S, Addr, Port). recv(S, Len) -> prim_inet:recvfrom(S, Len). diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl index 1e624b9e90..083059a2dc 100644 --- a/lib/kernel/src/inet_udp.erl +++ b/lib/kernel/src/inet_udp.erl @@ -66,16 +66,25 @@ open(Port, Opts) -> {ok, _} -> exit(badarg) end. -send(S, {A,B,C,D} = Addr, P, Data) - when ?ip(A,B,C,D), ?port(P) -> - prim_inet:sendto(S, Addr, P, Data). +send(S, {A,B,C,D} = IP, Port, Data) + when ?ip(A,B,C,D), ?port(Port) -> + prim_inet:sendto(S, {IP, Port}, [], Data); +send(S, {{A,B,C,D}, Port} = Addr, AncData, Data) + when ?ip(A,B,C,D), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Addr, AncData, Data); +send(S, {?FAMILY, {{A,B,C,D}, Port}} = Address, AncData, Data) + when ?ip(A,B,C,D), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data); +send(S, {?FAMILY, {loopback, Port}} = Address, AncData, Data) + when ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data). send(S, Data) -> - prim_inet:sendto(S, {0,0,0,0}, 0, Data). + prim_inet:sendto(S, {any, 0}, [], Data). -connect(S, Addr = {A,B,C,D}, P) - when ?ip(A,B,C,D), ?port(P) -> - prim_inet:connect(S, Addr, P). +connect(S, Addr = {A,B,C,D}, Port) + when ?ip(A,B,C,D), ?port(Port) -> + prim_inet:connect(S, Addr, Port). recv(S, Len) -> prim_inet:recvfrom(S, Len). diff --git a/lib/kernel/src/local_udp.erl b/lib/kernel/src/local_udp.erl index 481a8c4910..933e56228b 100644 --- a/lib/kernel/src/local_udp.erl +++ b/lib/kernel/src/local_udp.erl @@ -70,11 +70,13 @@ open(0, Opts) -> {ok, _} -> exit(badarg) end. -send(S, Addr = {?FAMILY,_}, 0, Data) -> - prim_inet:sendto(S, Addr, 0, Data). +send(S, {?FAMILY,_} = Addr, 0, Data) -> + prim_inet:sendto(S, Addr, [], Data); +send(S, {?FAMILY,_} = Addr, AncData, Data) when is_list(AncData) -> + prim_inet:sendto(S, Addr, AncData, Data). %% send(S, Data) -> - prim_inet:sendto(S, {?FAMILY,<<>>}, 0, Data). + prim_inet:sendto(S, {?FAMILY,<<>>}, [], Data). connect(S, Addr = {?FAMILY,_}, 0) -> prim_inet:connect(S, Addr, 0). diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 52edfaee29..e10c3dc857 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1984,7 +1984,7 @@ recvtclass(_Config) -> recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. -recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Does not return any value - not implemented for pktoptions recvtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); @@ -1996,7 +1996,7 @@ recvtos_ok(_, _) -> false. recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. -recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Does not return any value - not implemented for pktoptions recvttl_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,7,0}); @@ -2009,7 +2009,7 @@ recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Using the option returns einval, so it is not implemented. -recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); %% Does not return any value - not implemented for pktoptions recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); %% diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 4bcd9b35da..730886865c 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -37,6 +37,7 @@ buffer_size/1, binary_passive_recv/1, max_buffer_size/1, bad_address/1, read_packets/1, open_fd/1, connect/1, implicit_inet6/1, recvtos/1, recvtosttl/1, recvttl/1, recvtclass/1, + sendtos/1, sendtosttl/1, sendttl/1, sendtclass/1, local_basic/1, local_unbound/1, local_fdopen/1, local_fdopen_unbound/1, local_abstract/1]). @@ -49,6 +50,7 @@ all() -> bad_address, read_packets, open_fd, connect, implicit_inet6, active_n, recvtos, recvtosttl, recvttl, recvtclass, + sendtos, sendtosttl, sendttl, sendtclass, {group, local}]. groups() -> @@ -567,19 +569,19 @@ active_n(Config) when is_list(Config) -> recvtos(_Config) -> test_recv_opts( - inet, [{recvtos,tos,96}], + inet, [{recvtos,tos,96}], false, fun recvtos_ok/2). recvtosttl(_Config) -> test_recv_opts( - inet, [{recvtos,tos,96},{recvttl,ttl,33}], + inet, [{recvtos,tos,96},{recvttl,ttl,33}], false, fun (OSType, OSVer) -> recvtos_ok(OSType, OSVer) andalso recvttl_ok(OSType, OSVer) end). recvttl(_Config) -> test_recv_opts( - inet, [{recvttl,ttl,33}], + inet, [{recvttl,ttl,33}], false, fun recvttl_ok/2). recvtclass(_Config) -> @@ -591,15 +593,48 @@ recvtclass(_Config) -> of [_] -> test_recv_opts( - inet6, [{recvtclass,tclass,224}], + inet6, [{recvtclass,tclass,224}], false, fun recvtclass_ok/2); [] -> {skip,ipv6_not_supported,IFs} end. + +sendtos(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96}], true, + fun sendtos_ok/2). + +sendtosttl(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96},{recvttl,ttl,33}], true, + fun (OSType, OSVer) -> + sendtos_ok(OSType, OSVer) andalso sendttl_ok(OSType, OSVer) + end). + +sendttl(_Config) -> + test_recv_opts( + inet, [{recvttl,ttl,33}], true, + fun sendttl_ok/2). + +sendtclass(_Config) -> + {ok,IFs} = inet:getifaddrs(), + case + [Name || + {Name,Opts} <- IFs, + lists:member({addr,{0,0,0,0,0,0,0,1}}, Opts)] + of + [_] -> + test_recv_opts( + inet6, [{recvtclass,tclass,224}], true, + fun sendtclass_ok/2); + [] -> + {skip,ipv6_not_supported,IFs} + end. + %% These version numbers are just above the highest noted in daily tests %% where the test fails for a plausible reason, that is the lowest -%% where we can expect that the test mighe succeed, so +%% where we can expect that the test might succeed, so %% skip on platforms lower than this. %% %% On newer versions it might be fixed, but we'll see about that @@ -618,16 +653,55 @@ recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); recvtos_ok({unix,_}, _) -> true; recvtos_ok(_, _) -> false. +%% Option has no effect +recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% recvttl_ok({unix,_}, _) -> true; recvttl_ok(_, _) -> false. %% Using the option returns einval, so it is not implemented. recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0}); recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11}); +%% Option has no effect +recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% recvtclass_ok({unix,_}, _) -> true; recvtclass_ok(_, _) -> false. + +%% To send ancillary data seems to require much higher version numbers +%% than receiving it... +%% + +%% Using the option returns einval, so it is not implemented. +sendtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); +sendtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,5,0}); +sendtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +sendtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0}); +sendtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); +%% +sendtos_ok({unix,_}, _) -> true; +sendtos_ok(_, _) -> false. + +%% Using the option returns einval, so it is not implemented. +sendttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); +sendttl_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0}); +%% Using the option returns enoprotoopt, so it is not implemented. +sendttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); +%% Option has no effect +sendttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,5,0}); +%% +sendttl_ok({unix,_}, _) -> true; +sendttl_ok(_, _) -> false. + +%% Using the option returns einval, so it is not implemented. +sendtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0}); +sendtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11}); +%% +sendtclass_ok({unix,_}, _) -> true; +sendtclass_ok(_, _) -> false. + + semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> if X1 > X2 -> false; @@ -640,18 +714,18 @@ semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> end; semver_lt(_, {_,_,_}) -> false. -test_recv_opts(Family, Spec, OSFilter) -> +test_recv_opts(Family, Spec, TestSend, OSFilter) -> OSType = os:type(), OSVer = os:version(), case OSFilter(OSType, OSVer) of true -> io:format("Os: ~p, ~p~n", [OSType,OSVer]), - test_recv_opts(Family, Spec, OSType, OSVer); + test_recv_opts(Family, Spec, TestSend, OSType, OSVer); false -> {skip,{not_supported_for_os_version,{OSType,OSVer}}} end. %% -test_recv_opts(Family, Spec, _OSType, _OSVer) -> +test_recv_opts(Family, Spec, TestSend, _OSType, _OSVer) -> Timeout = 5000, RecvOpts = [RecvOpt || {RecvOpt,_,_} <- Spec], TrueRecvOpts = [{RecvOpt,true} || {RecvOpt,_,_} <- Spec], @@ -676,16 +750,33 @@ test_recv_opts(Family, Spec, _OSType, _OSVer) -> ok = inet:setopts(S1, TrueRecvOpts_OptsVals), {ok,TrueRecvOpts_OptsVals} = inet:getopts(S1, RecvOpts ++ Opts), %% + %% S1 now has true receive options and set option values + %% {ok,S2} = gen_udp:open(0, [Family,binary,{active,true}|FalseRecvOpts]), {ok,P2} = inet:port(S2), {ok,FalseRecvOpts_OptsVals2} = inet:getopts(S2, RecvOpts ++ Opts), OptsVals2 = FalseRecvOpts_OptsVals2 -- FalseRecvOpts, %% - ok = gen_udp:send(S2, Addr, P1, <<"abcde">>), + %% S2 now has false receive options and default option values, + %% OptsVals2 contains the default option values + %% + ok = gen_udp:send(S2, {Addr,P1}, <<"abcde">>), ok = gen_udp:send(S1, Addr, P2, <<"fghij">>), + TestSend andalso + begin + ok = gen_udp:send(S2, Addr, P1, OptsVals, <<"ABCDE">>), + ok = gen_udp:send(S2, {Addr,P1}, OptsVals, <<"12345">>) + end, {ok,{_,P2,OptsVals3,<<"abcde">>}} = gen_udp:recv(S1, 0, Timeout), verify_sets_eq(OptsVals3, OptsVals2), + TestSend andalso + begin + {ok,{_,P2,OptsVals0,<<"ABCDE">>}} = gen_udp:recv(S1, 0, Timeout), + {ok,{_,P2,OptsVals1,<<"12345">>}} = gen_udp:recv(S1, 0, Timeout), + verify_sets_eq(OptsVals0, OptsVals), + verify_sets_eq(OptsVals1, OptsVals) + end, receive {udp,S2,_,P1,<<"fghij">>} -> ok; @@ -700,8 +791,16 @@ test_recv_opts(Family, Spec, _OSType, _OSVer) -> ok = inet:setopts(S2, TrueRecvOpts), {ok,TrueRecvOpts} = inet:getopts(S2, RecvOpts), %% - ok = gen_udp:send(S2, Addr, P1, <<"klmno">>), - ok = gen_udp:send(S1, Addr, P2, <<"pqrst">>), + %% S1 now has false receive options and set option values + %% + %% S2 now has true receive options and default option values + %% + ok = gen_udp:send(S2, {Addr,P1}, [], <<"klmno">>), + ok = gen_udp:send(S1, {Family,{loopback,P2}}, <<"pqrst">>), + TestSend andalso + begin + ok = gen_udp:send(S1, {Family,{loopback,P2}}, OptsVals2, <<"PQRST">>) + end, {ok,{_,P2,<<"klmno">>}} = gen_udp:recv(S1, 0, Timeout), receive {udp,S2,_,P1,OptsVals4,<<"pqrst">>} -> @@ -711,9 +810,18 @@ test_recv_opts(Family, Spec, _OSType, _OSVer) -> after Timeout -> exit(timeout) end, + TestSend andalso + receive + {udp,S2,_,P1,OptsVals5,<<"PQRST">>} -> + verify_sets_eq(OptsVals5, OptsVals2); + Other3 -> + exit({unexpected,Other3}) + after Timeout -> + exit(timeout) + end, ok = gen_udp:close(S1), ok = gen_udp:close(S2), -%% exit({{OSType,OSVer},success}), % In search for the truth +%%% exit({{_OSType,_OSVer},success}), % In search for the truth ok. verify_sets_eq(L1, L2) -> @@ -867,6 +975,10 @@ connect(Config) when is_list(Config) -> implicit_inet6(Config) when is_list(Config) -> Host = ok(inet:gethostname()), case inet:getaddr(Host, inet6) of + {ok,{16#fe80,0,0,0,_,_,_,_} = Addr} -> + {skip, + "Got link local IPv6 address: " + ++inet:ntoa(Addr)}; {ok,Addr} -> implicit_inet6(Host, Addr); {error,Reason} -> @@ -917,11 +1029,12 @@ ok({ok,V}) -> V; ok(NotOk) -> try throw(not_ok) catch - throw:Thrown:Stacktrace -> - erlang:raise( - error, {Thrown, NotOk}, tl(Stacktrace)) + throw:not_ok:Stacktrace -> + raise_error({not_ok, NotOk}, tl(Stacktrace)) end. +raise_error(Reason, Stacktrace) -> + erlang:raise(error, Reason, Stacktrace). local_filename(Tag) -> "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). |