diff options
Diffstat (limited to 'lib/kernel')
-rw-r--r-- | lib/kernel/src/inet.erl | 67 | ||||
-rw-r--r-- | lib/kernel/src/local_tcp.erl | 2 | ||||
-rw-r--r-- | lib/kernel/test/gen_tcp_api_SUITE.erl | 249 | ||||
-rw-r--r-- | lib/kernel/test/gen_udp_SUITE.erl | 157 |
4 files changed, 429 insertions, 46 deletions
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 9fc685e728..a91a6ed517 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1344,18 +1344,12 @@ open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module) case prim_inet:open(Protocol, Family, Type, OpenOpts) of {ok,S} -> case prim_inet:setopts(S, Opts) of + ok when Addr =:= undefined -> + inet_db:register_socket(S, Module), + {ok,S}; ok -> - case - case Addr of - undefined -> - {ok, undefined}; - _ when is_list(Addr) -> - bindx(S, Addr, Port); - _ -> - prim_inet:bind(S, Addr, Port) - end - of - {ok, _} -> + case bind(S, Addr, Port) of + {ok, _} -> inet_db:register_socket(S, Module), {ok,S}; Error -> @@ -1373,6 +1367,11 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when is_integer(Fd) -> fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module). +bind(S, Addr, Port) when is_list(Addr) -> + bindx(S, Addr, Port); +bind(S, Addr, Port) -> + prim_inet:bind(S, Addr, Port). + bindx(S, [Addr], Port0) -> {IP, Port} = set_bindx_port(Addr, Port0), prim_inet:bind(S, IP, Port); @@ -1413,34 +1412,36 @@ fdopen(Fd, Opts, Protocol, Family, Type, Module) -> fdopen(Fd, any, 0, Opts, Protocol, Family, Type, Module). fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) -> - IsAnyAddr = (Addr == {0,0,0,0} orelse Addr == {0,0,0,0,0,0,0,0} - orelse Addr == any), - Bound = Port == 0 andalso IsAnyAddr, + Bound = + %% We do not do any binding if default port+addr options + %% were given, in order to keep backwards compatability + %% with pre Erlang/OTP 17 + case Addr of + {0,0,0,0} when Port =:= 0 -> true; + {0,0,0,0,0,0,0,0} when Port =:= 0 -> true; + any when Port =:= 0 -> true; + _ -> false + end, case prim_inet:fdopen(Protocol, Family, Type, Fd, Bound) of {ok, S} -> case prim_inet:setopts(S, Opts) of + ok + when Addr =:= undefined; + Bound -> + inet_db:register_socket(S, Module), + {ok, S}; ok -> - case if - Bound -> - %% We do not do any binding if default - %% port+addr options where given in order - %% to keep backwards compatability with - %% pre Erlang/TOP 17 - {ok, ok}; - is_list(Addr) -> - bindx(S, Addr, Port); - true -> - prim_inet:bind(S, Addr, Port) - end of - {ok, _} -> - inet_db:register_socket(S, Module), - {ok, S}; - Error -> - prim_inet:close(S), - Error + case bind(S, Addr, Port) of + {ok, _} -> + inet_db:register_socket(S, Module), + {ok, S}; + Error -> + prim_inet:close(S), + Error end; Error -> - prim_inet:close(S), Error + prim_inet:close(S), + Error end; Error -> Error end. diff --git a/lib/kernel/src/local_tcp.erl b/lib/kernel/src/local_tcp.erl index e3c67dfbb7..90e0fa2162 100644 --- a/lib/kernel/src/local_tcp.erl +++ b/lib/kernel/src/local_tcp.erl @@ -175,4 +175,4 @@ accept(L, Timeout) -> %% Create a port/socket from a file descriptor %% fdopen(Fd, Opts) -> - inet:fdopen(Fd, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE). + inet:open(Fd, undefined, 0, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE). diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl index 54298e6309..026cbf8b28 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE.erl +++ b/lib/kernel/test/gen_tcp_api_SUITE.erl @@ -34,7 +34,11 @@ t_recv_timeout/1, t_recv_eof/1, t_recv_delim/1, t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1, t_shutdown_async/1, - t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]). + t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1, + t_local_basic/1, t_local_unbound/1, t_local_fdopen/1, + t_local_fdopen_listen/1, t_local_fdopen_listen_unbound/1, + t_local_fdopen_connect/1, t_local_fdopen_connect_unbound/1, + t_local_abstract/1]). -export([getsockfd/0,closesockfd/1]). @@ -45,12 +49,18 @@ suite() -> all() -> [{group, t_accept}, {group, t_connect}, {group, t_recv}, t_shutdown_write, t_shutdown_both, t_shutdown_error, - t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6]. + t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6, + {group, t_local}]. groups() -> [{t_accept, [], [t_accept_timeout]}, {t_connect, [], [t_connect_timeout, t_connect_bad]}, - {t_recv, [], [t_recv_timeout, t_recv_eof, t_recv_delim]}]. + {t_recv, [], [t_recv_timeout, t_recv_eof, t_recv_delim]}, + {t_local, [], + [t_local_basic, t_local_unbound, t_local_fdopen, + t_local_fdopen_listen, t_local_fdopen_listen_unbound, + t_local_fdopen_connect, t_local_fdopen_connect_unbound, + t_local_abstract]}]. @@ -60,17 +70,36 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_group(t_local, Config) -> + case gen_tcp:connect({local,<<"/">>}, 0, []) of + {error,eafnosupport} -> + {skip, "AF_LOCAL not supported"}; + {error,_} -> + Config + end; init_per_group(_GroupName, Config) -> Config. -end_per_group(_,_Config) -> +end_per_group(t_local, _Config) -> + delete_local_filenames(); +end_per_group(_, _Config) -> ok. + +init_per_testcase(Func, Config) + when Func =:= undefined -> % Insert your testcase name here + dbg:tracer(), + dbg:p(self(), c), + dbg:tpl(prim_inet, cx), + dbg:tpl(local_tcp, cx), + dbg:tpl(inet, cx), + dbg:tpl(gen_tcp, cx), + Config; init_per_testcase(_Func, Config) -> Config. end_per_testcase(_Func, _Config) -> - ok. + dbg:stop(). %%% gen_tcp:accept/1,2 @@ -308,6 +337,192 @@ implicit_inet6(S, Addr) -> ok = gen_tcp:close(S1). + +t_local_basic(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + %% + L = + ok( + gen_tcp:listen(0, [{ifaddr,{local,SFile}},{active,false}])), + C = + ok( + gen_tcp:connect( + {local,SFile}, 0, [{ifaddr,{local,CFile}},{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, CAddr), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + %% + ok = file:delete(SFile), + ok = file:delete(CFile), + ok. + +t_local_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = file:delete(SFile), + ok. + +t_local_fdopen(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + C0 = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + Fd = ok(prim_inet:getfd(C0)), + ok = prim_inet:ignorefd(C0, true), + C = ok(gen_tcp:fdopen(Fd, [local])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = gen_tcp:close(C0), + ok = file:delete(SFile), + ok. + +t_local_fdopen_listen(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + L0 = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + Fd = ok(prim_inet:getfd(L0)), + L = ok(gen_tcp:listen(0, [{fd,Fd},local,{active,false}])), + C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(L0), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = file:delete(SFile), + ok. + +t_local_fdopen_listen_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + P = ok(prim_inet:open(tcp, local, stream)), + Fd = ok(prim_inet:getfd(P)), + L = + ok(gen_tcp:listen( + 0, [{fd,Fd},{ifaddr,SAddr},{active,false}])), + C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(P), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = file:delete(SFile), + ok. + +t_local_fdopen_connect(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + P = ok(prim_inet:open(tcp, local, stream)), + Fd = ok(prim_inet:getfd(P)), + C = + ok(gen_tcp:connect( + SAddr, 0, [{fd,Fd},{ifaddr,CAddr},{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, CAddr), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = gen_tcp:close(P), + ok = file:delete(SFile), + ok. + +t_local_fdopen_connect_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])), + P = ok(prim_inet:open(tcp, local, stream)), + Fd = ok(prim_inet:getfd(P)), + C = ok(gen_tcp:connect(SAddr, 0, [{fd,Fd},{active,false}])), + S = ok(gen_tcp:accept(L)), + SAddr = ok(inet:sockname(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, {local,<<>>}), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok = gen_tcp:close(P), + ok = file:delete(SFile), + ok. + +t_local_abstract(_Config) -> + case os:type() of + {unix,linux} -> + AbstAddr = {local,<<>>}, + L = + ok(gen_tcp:listen( + 0, [{ifaddr,AbstAddr},{active,false}])), + {local,_} = SAddr = ok(inet:sockname(L)), + C = + ok(gen_tcp:connect( + SAddr, 0, [{ifaddr,AbstAddr},{active,false}])), + {local,_} = CAddr = ok(inet:sockname(C)), + S = ok(gen_tcp:accept(L)), + {error,enotconn} = inet:peername(L), + local_handshake(S, SAddr, C, CAddr), + ok = gen_tcp:close(L), + ok = gen_tcp:close(S), + ok = gen_tcp:close(C), + ok; + _ -> + {skip,"AF_LOCAL Abstract Addresses only supported on Linux"} + end. + + +local_handshake(S, SAddr, C, CAddr) -> + SData = "9876543210", + CData = "0123456789", + SAddr = ok(inet:sockname(S)), + CAddr = ok(inet:sockname(C)), + CAddr = ok(inet:peername(S)), + SAddr = ok(inet:peername(C)), + ok = gen_tcp:send(C, CData), + ok = gen_tcp:send(S, SData), + CData = ok(gen_tcp:recv(S, length(CData))), + SData = ok(gen_tcp:recv(C, length(SData))), + ok. + %%% Utilities %% Calls M:F/length(A), which should return a timeout error, and complete @@ -369,8 +584,28 @@ unused_ip(A, B, C, D) -> {error, _} -> {ok, {A, B, C, D}} end. -ok({ok,V}) -> V. - +ok({ok,V}) -> V; +ok(NotOk) -> + try throw(not_ok) + catch + Thrown -> + erlang:raise( + error, {Thrown, NotOk}, tl(erlang:get_stacktrace())) + end. getsockfd() -> undefined. closesockfd(_FD) -> undefined. + +local_filename(Tag) -> + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). + +bin_filename(String) -> + unicode:characters_to_binary(String, file:native_name_encoding()). + +delete_local_filenames() -> + _ = + [file:delete(F) || + F <- + filelib:wildcard( + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_*")], + ok. diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 44539bf44c..db4e174374 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -35,7 +35,9 @@ -export([send_to_closed/1, active_n/1, buffer_size/1, binary_passive_recv/1, bad_address/1, - read_packets/1, open_fd/1, connect/1, implicit_inet6/1]). + read_packets/1, open_fd/1, connect/1, implicit_inet6/1, + local_basic/1, local_unbound/1, + local_fdopen/1, local_fdopen_unbound/1, local_abstract/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -44,10 +46,13 @@ suite() -> all() -> [send_to_closed, buffer_size, binary_passive_recv, bad_address, read_packets, open_fd, connect, - implicit_inet6, active_n]. + implicit_inet6, active_n, + {group, local}]. groups() -> - []. + [{local, [], + [local_basic, local_unbound, + local_fdopen, local_fdopen_unbound, local_abstract]}]. init_per_suite(Config) -> Config. @@ -55,9 +60,19 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_group(local, Config) -> + case gen_udp:open(0, [local]) of + {ok,S} -> + ok = gen_udp:close(S), + Config; + {error,eafnosupport} -> + {skip, "AF_LOCAL not supported"} + end; init_per_group(_GroupName, Config) -> Config. +end_per_group(local, _Config) -> + delete_local_filenames(); end_per_group(_GroupName, Config) -> Config. @@ -65,7 +80,7 @@ end_per_group(_GroupName, Config) -> init_per_testcase(_Case, Config) -> Config. -end_per_testcase(_Case, Config) -> +end_per_testcase(_Case, _Config) -> ok. %%------------------------------------------------------------- @@ -550,6 +565,116 @@ active_n(Config) when is_list(Config) -> ok = gen_udp:close(S1), ok. + +local_basic(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + %% + S = ok(gen_udp:open(0, [{ifaddr,{local,SFile}},{active,false}])), + C = ok(gen_udp:open(0, [{ifaddr,{local,CFile}},{active,false}])), + SAddr = ok(inet:sockname(S)), + CAddr = ok(inet:sockname(C)), + local_handshake(S, SAddr, C, CAddr), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + %% + ok = file:delete(SFile), + ok = file:delete(CFile), + ok. + +local_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + S = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])), + C = ok(gen_udp:open(0, [local,{active,false}])), + SAddr = ok(inet:sockname(S)), + local_handshake(S, SAddr, C, undefined), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + %% + ok = file:delete(SFile), + ok. + +local_fdopen(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + CFile = local_filename(client), + CAddr = {local,bin_filename(CFile)}, + _ = file:delete(SFile), + _ = file:delete(CFile), + %% + S0 = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])), + C = ok(gen_udp:open(0, [{ifaddr,{local,CFile}},{active,false}])), + SAddr = ok(inet:sockname(S0)), + CAddr = ok(inet:sockname(C)), + Fd = ok(prim_inet:getfd(S0)), + S = ok(gen_udp:open(0, [{fd,Fd},local,{active,false}])), + SAddr = ok(inet:sockname(S)), + local_handshake(S, SAddr, C, CAddr), + ok = gen_udp:close(S), + ok = gen_udp:close(S0), + ok = gen_udp:close(C), + %% + ok = file:delete(SFile), + ok = file:delete(CFile), + ok. + +local_fdopen_unbound(_Config) -> + SFile = local_filename(server), + SAddr = {local,bin_filename(SFile)}, + _ = file:delete(SFile), + %% + S = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])), + C0 = ok(gen_udp:open(0, [local,{active,false}])), + SAddr = ok(inet:sockname(S)), + Fd = ok(prim_inet:getfd(C0)), + C = ok(gen_udp:open(0, [{fd,Fd},local,{active,false}])), + local_handshake(S, SAddr, C, undefined), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + ok = gen_udp:close(C0), + %% + ok = file:delete(SFile), + ok. + +local_abstract(_Config) -> + case os:type() of + {unix,linux} -> + S = ok(gen_udp:open(0, [{ifaddr,{local,<<>>}},{active,false}])), + C = ok(gen_udp:open(0, [{ifaddr,{local,<<>>}},{active,false}])), + {local,_} = SAddr = ok(inet:sockname(S)), + {local,_} = CAddr = ok(inet:sockname(C)), + local_handshake(S, SAddr, C, CAddr), + ok = gen_udp:close(S), + ok = gen_udp:close(C), + ok; + _ -> + {skip,"AF_LOCAL Abstract Addresses only supported on Linux"} + end. + + +local_handshake(S, SAddr, C, CAddr) -> + SData = "9876543210", + CData = "0123456789", + ok = gen_udp:send(C, SAddr, 0, CData), + case ok(gen_tcp:recv(S, 112)) of + {{unspec,<<>>}, 0, CData} when CAddr =:= undefined -> + ok; + {{local,<<>>}, 0, CData} when CAddr =:= undefined -> + ok; + {CAddr, 0, CData} when CAddr =/= undefined -> + ok = gen_udp:send(S, CAddr, 0, SData), + {SAddr, 0, SData} = ok(gen_tcp:recv(C, 112)), + ok + + end. + %% %% Utils %% @@ -630,4 +755,26 @@ implicit_inet6(S1, Active, Addr) -> {Addr,P2,"pong"} = ok(gen_udp:recv(S1, 1024)), ok = gen_udp:close(S2). -ok({ok,V}) -> V. +ok({ok,V}) -> V; +ok(NotOk) -> + try throw(not_ok) + catch + Thrown -> + erlang:raise( + error, {Thrown, NotOk}, tl(erlang:get_stacktrace())) + end. + + +local_filename(Tag) -> + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). + +bin_filename(String) -> + unicode:characters_to_binary(String, file:native_name_encoding()). + +delete_local_filenames() -> + _ = + [file:delete(F) || + F <- + filelib:wildcard( + "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_*")], + ok. |