From 86751fcc8665748a6bc8eeb5ab078f403d14107e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 29 May 2019 12:38:26 +0200 Subject: [esock] Make it it work for IPv6 The nif code expects the socket address to be "complete", that is all fields must exist. This was not the case for a couple of functions, sendto, sendmsg and connect. What was mssing was a call to 'ensure_sockaddr'. Test: 1) Add a condition function to test if IPv6 is available and works. 2) Needed to adjust a test case (actually a common function used in several test cases) since the socket address data type has two optional fields, which we do not initiate (when sending), but the nif-code does fill in. OTP-15897 --- erts/emulator/test/socket_SUITE.erl | 129 +++++++++++++---------- erts/emulator/test/socket_test_lib.erl | 180 +++++++++++++++++++++++++++++++++ erts/preloaded/ebin/socket.beam | Bin 76064 -> 76320 bytes erts/preloaded/src/socket.erl | 14 ++- 4 files changed, 263 insertions(+), 60 deletions(-) (limited to 'erts') diff --git a/erts/emulator/test/socket_SUITE.erl b/erts/emulator/test/socket_SUITE.erl index b80cc794e7..e19bb9b1bb 100644 --- a/erts/emulator/test/socket_SUITE.erl +++ b/erts/emulator/test/socket_SUITE.erl @@ -8146,7 +8146,7 @@ which_multicast_address3(Domain, [MAddrStr|MAddrs]) -> end. which_local_host_ifname(Domain) -> - case which_local_host_info(Domain) of + case ?LIB:which_local_host_info(Domain) of {ok, {Name, _Addr, _Flags}} -> Name; {error, Reason} -> @@ -19408,7 +19408,9 @@ tpp_udp_client_handler_msg_exchange_loop(_Sock, _Dest, _Send, _Recv, _Msg, Start) -> Stop = ?LIB:timestamp(), {Sent, Received, Start, Stop}; -tpp_udp_client_handler_msg_exchange_loop(Sock, Dest, Send, Recv, Data, +tpp_udp_client_handler_msg_exchange_loop(Sock, + #{family := local} = Dest, + Send, Recv, Data, Num, N, Sent, Received, Start) -> case tpp_udp_send_req(Sock, Send, Data, Dest) of {ok, SendSz} -> @@ -19427,6 +19429,28 @@ tpp_udp_client_handler_msg_exchange_loop(Sock, Dest, Send, Recv, Data, {error, SReason} -> ?SEV_EPRINT("send (~w of ~w): ~p", [N, Num, SReason]), exit({send, SReason, N}) + end; +tpp_udp_client_handler_msg_exchange_loop(Sock, + #{addr := Addr, port := Port} = Dest0, + Send, Recv, Data, + Num, N, Sent, Received, Start) -> + case tpp_udp_send_req(Sock, Send, Data, Dest0) of + {ok, SendSz} -> + case tpp_udp_recv_rep(Sock, Recv) of + {ok, NewData, RecvSz, #{addr := Addr, port := Port} = Dest1} -> + tpp_udp_client_handler_msg_exchange_loop(Sock, Dest1, + Send, Recv, + NewData, Num, N+1, + Sent+SendSz, + Received+RecvSz, + Start); + {error, RReason} -> + ?SEV_EPRINT("recv (~w of ~w): ~p", [N, Num, RReason]), + exit({recv, RReason, N}) + end; + {error, SReason} -> + ?SEV_EPRINT("send (~w of ~w): ~p", [N, Num, SReason]), + exit({send, SReason, N}) end. @@ -26355,7 +26379,7 @@ which_local_socket_addr(local = Domain) -> %% We should really implement this using the (new) net module, %% but until that gets the necessary functionality... which_local_socket_addr(Domain) -> - case which_local_host_info(Domain) of + case ?LIB:which_local_host_info(Domain) of {ok, {_Name, _Flags, Addr}} -> #{family => Domain, addr => Addr}; @@ -26366,52 +26390,52 @@ which_local_socket_addr(Domain) -> %% Returns the interface (name), flags and address (not 127...) %% of the local host. -which_local_host_info(Domain) -> - case inet:getifaddrs() of - {ok, IFL} -> - which_local_host_info(Domain, IFL); - {error, _} = ERROR -> - ERROR - end. - -which_local_host_info(_Domain, []) -> - ?FAIL(no_address); -which_local_host_info(Domain, [{"lo" ++ _, _}|IFL]) -> - which_local_host_info(Domain, IFL); -which_local_host_info(Domain, [{"docker" ++ _, _}|IFL]) -> - which_local_host_info(Domain, IFL); -which_local_host_info(Domain, [{"br-" ++ _, _}|IFL]) -> - which_local_host_info(Domain, IFL); -which_local_host_info(Domain, [{Name, IFO}|IFL]) -> - case which_local_host_info2(Domain, IFO) of - {ok, {Flags, Addr}} -> - {ok, {Name, Flags, Addr}}; - {error, _} -> - which_local_host_info(Domain, IFL) - end; -which_local_host_info(Domain, [_|IFL]) -> - which_local_host_info(Domain, IFL). +%% which_local_host_info(Domain) -> +%% case inet:getifaddrs() of +%% {ok, IFL} -> +%% which_local_host_info(Domain, IFL); +%% {error, _} = ERROR -> +%% ERROR +%% end. -which_local_host_info2(Domain, IFO) -> - case lists:keysearch(flags, 1, IFO) of - {value, {flags, Flags}} -> - which_local_host_info2(Domain, IFO, Flags); - false -> - {error, no_flags} - end. +%% which_local_host_info(_Domain, []) -> +%% ?FAIL(no_address); +%% which_local_host_info(Domain, [{"lo" ++ _, _}|IFL]) -> +%% which_local_host_info(Domain, IFL); +%% which_local_host_info(Domain, [{"docker" ++ _, _}|IFL]) -> +%% which_local_host_info(Domain, IFL); +%% which_local_host_info(Domain, [{"br-" ++ _, _}|IFL]) -> +%% which_local_host_info(Domain, IFL); +%% which_local_host_info(Domain, [{Name, IFO}|IFL]) -> +%% case which_local_host_info2(Domain, IFO) of +%% {ok, {Flags, Addr}} -> +%% {ok, {Name, Flags, Addr}}; +%% {error, _} -> +%% which_local_host_info(Domain, IFL) +%% end; +%% which_local_host_info(Domain, [_|IFL]) -> +%% which_local_host_info(Domain, IFL). + +%% which_local_host_info2(Domain, IFO) -> +%% case lists:keysearch(flags, 1, IFO) of +%% {value, {flags, Flags}} -> +%% which_local_host_info2(Domain, IFO, Flags); +%% false -> +%% {error, no_flags} +%% end. -which_local_host_info2(_Domain, [], _Flags) -> - {error, no_address}; -which_local_host_info2(inet = _Domain, [{addr, Addr}|_IFO], Flags) - when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> - {ok, {Flags, Addr}}; -which_local_host_info2(inet6 = _Domain, [{addr, Addr}|_IFO], Flags) - when (size(Addr) =:= 8) andalso - (element(1, Addr) =/= 0) andalso - (element(1, Addr) =/= 16#fe80) -> - {ok, {Flags, Addr}}; -which_local_host_info2(Domain, [_|IFO], Flags) -> - which_local_host_info2(Domain, IFO, Flags). +%% which_local_host_info2(_Domain, [], _Flags) -> +%% {error, no_address}; +%% which_local_host_info2(inet = _Domain, [{addr, Addr}|_IFO], Flags) +%% when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> +%% {ok, {Flags, Addr}}; +%% which_local_host_info2(inet6 = _Domain, [{addr, Addr}|_IFO], Flags) +%% when (size(Addr) =:= 8) andalso +%% (element(1, Addr) =/= 0) andalso +%% (element(1, Addr) =/= 16#fe80) -> +%% {ok, {Flags, Addr}}; +%% which_local_host_info2(Domain, [_|IFO], Flags) -> +%% which_local_host_info2(Domain, IFO, Flags). @@ -26429,7 +26453,7 @@ has_ip_multicast_support() -> case os:type() of {unix, OsName} when (OsName =:= linux) orelse (OsName =:= sunos) -> - case which_local_host_info(inet) of + case ?LIB:which_local_host_info(inet) of {ok, {_Name, Flags, _Addr}} -> case lists:member(multicast, Flags) of true -> @@ -26491,13 +26515,8 @@ has_support_unix_domain_socket() -> %% support for IPv6. If not, there is no point in running IPv6 tests. %% Currently we just skip. has_support_ipv6() -> - %% case socket:supports(ipv6) of - %% true -> - %% ok; - %% false -> - %% {error, not_supported} - %% end. - not_yet_implemented(). + %%not_yet_implemented(). + ?LIB:has_support_ipv6(). diff --git a/erts/emulator/test/socket_test_lib.erl b/erts/emulator/test/socket_test_lib.erl index 4e65c4f3c0..2ded82ae13 100644 --- a/erts/emulator/test/socket_test_lib.erl +++ b/erts/emulator/test/socket_test_lib.erl @@ -32,12 +32,22 @@ %% String and format f/2, + %% Generic 'has support' test function(s) + has_support_ipv6/0, + + which_local_host_info/1, + %% Skipping not_yet_implemented/0, skip/1 ]). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(FAIL(R), exit(R)). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% pi(Item) when is_atom(Item) -> @@ -86,6 +96,176 @@ f(F, A) -> lists:flatten(io_lib:format(F, A)). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +has_support_ipv6() -> + case socket:supports(ipv6) of + true -> + ok; + false -> + skip("IPv6 Not Supported") + end, + Domain = inet6, + LocalAddr = + case which_local_addr(Domain) of + {ok, Addr} -> + Addr; + {error, R1} -> + skip(f("Local Address eval failed: ~p", [R1])) + end, + ServerSock = + case socket:open(Domain, dgram, udp) of + {ok, SS} -> + SS; + {error, R2} -> + skip(f("(server) socket open failed: ~p", [R2])) + end, + LocalSA = #{family => Domain, addr => LocalAddr}, + ServerPort = + case socket:bind(ServerSock, LocalSA) of + {ok, P1} -> + P1; + {error, R3} -> + socket:close(ServerSock), + skip(f("(server) socket bind failed: ~p", [R3])) + end, + ServerSA = LocalSA#{port => ServerPort}, + ClientSock = + case socket:open(Domain, dgram, udp) of + {ok, CS} -> + CS; + {error, R4} -> + skip(f("(client) socket open failed: ~p", [R4])) + end, + case socket:bind(ClientSock, LocalSA) of + {ok, _} -> + ok; + {error, R5} -> + socket:close(ServerSock), + socket:close(ClientSock), + skip(f("(client) socket bind failed: ~p", [R5])) + end, + case socket:sendto(ClientSock, <<"hejsan">>, ServerSA) of + ok -> + ok; + {error, R6} -> + socket:close(ServerSock), + socket:close(ClientSock), + skip(f("failed socket sendto test: ~p", [R6])) + end, + case socket:recvfrom(ServerSock) of + {ok, {_, <<"hejsan">>}} -> + socket:close(ServerSock), + socket:close(ClientSock), + ok; + {error, R7} -> + socket:close(ServerSock), + socket:close(ClientSock), + skip(f("failed socket recvfrom test: ~p", [R7])) + end. + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% This gets the local address (not {127, _} or {0, ...} or {16#fe80, ...}) +%% We should really implement this using the (new) net module, +%% but until that gets the necessary functionality... +which_local_addr(Domain) -> + case which_local_host_info(Domain) of + {ok, {_Name, _Flags, Addr}} -> + {ok, Addr}; + {error, _Reason} = ERROR -> + ERROR + end. + +%% case inet:getifaddrs() of +%% {ok, IFL} -> +%% which_addr(Domain, IFL); +%% {error, Reason} -> +%% ?FAIL({inet, getifaddrs, Reason}) +%% end. + +%% which_addr(_Domain, []) -> +%% ?FAIL(no_address); +%% which_addr(Domain, [{"lo" ++ _, _}|IFL]) -> +%% which_addr(Domain, IFL); +%% which_addr(Domain, [{_Name, IFO}|IFL]) -> +%% case which_addr2(Domain, IFO) of +%% {ok, Addr} -> +%% Addr; +%% {error, no_address} -> +%% which_addr(Domain, IFL) +%% end; +%% which_addr(Domain, [_|IFL]) -> +%% which_addr(Domain, IFL). + +%% which_addr2(_Domain, []) -> +%% {error, no_address}; +%% which_addr2(inet = _Domain, [{addr, Addr}|_IFO]) +%% when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> +%% {ok, Addr}; +%% which_addr2(inet6 = _Domain, [{addr, Addr}|_IFO]) +%% when (size(Addr) =:= 8) andalso +%% (element(1, Addr) =/= 0) andalso +%% (element(1, Addr) =/= 16#fe80) -> +%% {ok, Addr}; +%% which_addr2(Domain, [_|IFO]) -> +%% which_addr2(Domain, IFO). + + +%% Returns the interface (name), flags and address (not 127...) +%% of the local host. +which_local_host_info(Domain) -> + case inet:getifaddrs() of + {ok, IFL} -> + which_local_host_info(Domain, IFL); + {error, _} = ERROR -> + ERROR + end. + +which_local_host_info(_Domain, []) -> + {error, no_address}; +which_local_host_info(Domain, [{"lo" ++ _, _}|IFL]) -> + which_local_host_info(Domain, IFL); +which_local_host_info(Domain, [{"docker" ++ _, _}|IFL]) -> + which_local_host_info(Domain, IFL); +which_local_host_info(Domain, [{"br-" ++ _, _}|IFL]) -> + which_local_host_info(Domain, IFL); +which_local_host_info(Domain, [{Name, IFO}|IFL]) -> + case which_local_host_info2(Domain, IFO) of + {ok, {Flags, Addr}} -> + {ok, {Name, Flags, Addr}}; + {error, _} -> + which_local_host_info(Domain, IFL) + end; +which_local_host_info(Domain, [_|IFL]) -> + which_local_host_info(Domain, IFL). + +which_local_host_info2(Domain, IFO) -> + case lists:keysearch(flags, 1, IFO) of + {value, {flags, Flags}} -> + which_local_host_info2(Domain, IFO, Flags); + false -> + {error, no_flags} + end. + +which_local_host_info2(_Domain, [], _Flags) -> + {error, no_address}; +which_local_host_info2(inet = _Domain, [{addr, Addr}|_IFO], Flags) + when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> + {ok, {Flags, Addr}}; +which_local_host_info2(inet6 = _Domain, [{addr, Addr}|_IFO], Flags) + when (size(Addr) =:= 8) andalso + (element(1, Addr) =/= 0) andalso + (element(1, Addr) =/= 16#fe80) -> + {ok, {Flags, Addr}}; +which_local_host_info2(Domain, [_|IFO], Flags) -> + which_local_host_info2(Domain, IFO, Flags). + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% not_yet_implemented() -> diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam index 25eb0b2f4a..648519149f 100644 Binary files a/erts/preloaded/ebin/socket.beam and b/erts/preloaded/ebin/socket.beam differ diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index be94e3a867..910c8be294 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -1301,7 +1301,7 @@ connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout) ((Timeout =:= nowait) orelse (Timeout =:= infinity) orelse is_integer(Timeout)) -> TS = timestamp(Timeout), - case nif_connect(SockRef, SockAddr) of + case nif_connect(SockRef, ensure_sockaddr(SockAddr)) of ok -> %% Connected! ok; @@ -1620,7 +1620,7 @@ sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout) (Timeout =:= infinity) orelse (is_integer(Timeout) andalso (Timeout > 0))) -> EFlags = enc_send_flags(Flags), - do_sendto(SockRef, Data, Dest, EFlags, Timeout); + do_sendto(SockRef, Data, ensure_sockaddr(Dest), EFlags, Timeout); sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout) when is_binary(Data) andalso ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso @@ -1629,7 +1629,7 @@ sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout) (Timeout =:= infinity) orelse (is_integer(Timeout) andalso (Timeout > 0))) -> EFlags = enc_send_flags(Flags), - do_sendto(SockRef, Data, Dest, EFlags, Timeout). + do_sendto(SockRef, Data, ensure_sockaddr(Dest), EFlags, Timeout). do_sendto(SockRef, Data, Dest, EFlags, Timeout) -> TS = timestamp(Timeout), @@ -1820,8 +1820,12 @@ do_sendmsg_rest([B|IOVec], Written) -> ensure_msghdr(#{ctrl := []} = M) -> ensure_msghdr(maps:remove(ctrl, M)); -ensure_msghdr(#{iov := IOV} = M) when is_list(IOV) andalso (IOV =/= []) -> - M#{iov := erlang:iolist_to_iovec(IOV)}; +ensure_msghdr(#{iov := IOV, addr := Addr} = M) + when is_list(IOV) andalso (IOV =/= []) -> + M#{iov => erlang:iolist_to_iovec(IOV), addr => ensure_sockaddr(Addr)}; +ensure_msghdr(#{iov := IOV} = M) + when is_list(IOV) andalso (IOV =/= []) -> + M#{iov => erlang:iolist_to_iovec(IOV)}; ensure_msghdr(_) -> einval(). -- cgit v1.2.3