From 8a6390613225ec3f3e2d5bbf537d2e0737342e87 Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Fri, 11 Jul 2014 10:46:37 +0200 Subject: kernel: When doing an fdopen we now also bind the fd to the specified addr/port --- erts/emulator/drivers/common/inet_drv.c | 33 ++++++++--- erts/preloaded/ebin/prim_inet.beam | Bin 72280 -> 72484 bytes erts/preloaded/src/prim_inet.erl | 9 ++- erts/vsn.mk | 2 +- lib/kernel/doc/src/gen_tcp.xml | 7 ++- lib/kernel/src/inet.erl | 32 +++++++++-- lib/kernel/test/gen_tcp_api_SUITE.erl | 41 +++++++++++++- .../test/gen_tcp_api_SUITE_data/Makefile.src | 8 +++ .../gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c | 61 +++++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 10 files changed, 175 insertions(+), 20 deletions(-) create mode 100644 lib/kernel/test/gen_tcp_api_SUITE_data/Makefile.src create mode 100644 lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 45fac69303..4f0bf1167e 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -4155,7 +4155,8 @@ static ErlDrvSSizeT inet_ctl_open(inet_descriptor* desc, int domain, int type, /* as inet_open but pass in an open socket (MUST BE OF RIGHT TYPE) */ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, - SOCKET s, char** rbuf, ErlDrvSizeT rsize) + SOCKET s, Uint32 bound, + char** rbuf, ErlDrvSizeT rsize) { inet_address name; unsigned int sz = sizeof(name); @@ -4172,7 +4173,12 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, #ifdef __WIN32__ driver_select(desc->port, desc->event, ERL_DRV_READ, 1); #endif - desc->state = INET_STATE_BOUND; /* assume bound */ + + if (bound) + desc->state = INET_STATE_BOUND; + else + desc->state = INET_STATE_OPEN; + if (type == SOCK_STREAM) { /* check if connected */ sz = sizeof(name); if (!IS_SOCKET_ERROR(sock_peer(s, (struct sockaddr*) &name, &sz))) @@ -8642,10 +8648,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, break; } - case INET_REQ_FDOPEN: { /* pass in an open socket */ + case INET_REQ_FDOPEN: { /* pass in an open (and optionally bound) socket */ int domain; + int bound; DEBUGF(("tcp_inet_ctl(%ld): FDOPEN\r\n", (long)desc->inet.port)); - if (len != 6) return ctl_error(EINVAL, rbuf, rsize); + if (len != 6 && len != 10) return ctl_error(EINVAL, rbuf, rsize); switch(buf[0]) { case INET_AF_INET: domain = AF_INET; @@ -8663,8 +8670,13 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_error(EINVAL, rbuf, rsize); } if (buf[1] != INET_TYPE_STREAM) return ctl_error(EINVAL, rbuf, rsize); + + if (len == 6) bound = 1; + else bound = get_int32(buf+2+4); + return inet_ctl_fdopen(INETP(desc), domain, SOCK_STREAM, - (SOCKET) get_int32(buf+2), rbuf, rsize); + (SOCKET) get_int32(buf+2), + bound, rbuf, rsize); break; } @@ -10409,10 +10421,11 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return replen; - case INET_REQ_FDOPEN: { /* pass in an open (and bound) socket */ + case INET_REQ_FDOPEN: { /* pass in an open (and optionally bound) socket */ SOCKET s; + int bound; DEBUGF(("packet inet_ctl(%ld): FDOPEN\r\n", (long)desc->port)); - if (len != 6) { + if (len != 6 && len != 10) { return ctl_error(EINVAL, rbuf, rsize); } switch (buf[0]) { @@ -10437,7 +10450,11 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_error(EINVAL, rbuf, rsize); } s = (SOCKET)get_int32(buf+2); - replen = inet_ctl_fdopen(desc, af, type, s, rbuf, rsize); + + if (len == 6) bound = 1; + else bound = get_int32(buf+2+4); + + replen = inet_ctl_fdopen(desc, af, type, s, bound, rbuf, rsize); if ((*rbuf)[0] != INET_REP_ERROR) { if (desc->active) diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam index 656fbc6627..412ad8290b 100644 Binary files a/erts/preloaded/ebin/prim_inet.beam and b/erts/preloaded/ebin/prim_inet.beam differ diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index a9df75327c..e3ba8a328a 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -25,7 +25,7 @@ %% Primitive inet_drv interface --export([open/3, open/4, fdopen/4, close/1]). +-export([open/3, open/4, fdopen/4, fdopen/5, close/1]). -export([bind/3, listen/1, listen/2, peeloff/2]). -export([connect/3, connect/4, async_connect/4]). -export([accept/1, accept/2, async_accept/2]). @@ -70,7 +70,12 @@ open(Protocol, Family, Type, Opts) -> open(Protocol, Family, Type, Opts, ?INET_REQ_OPEN, []). fdopen(Protocol, Family, Type, Fd) when is_integer(Fd) -> - open(Protocol, Family, Type, [], ?INET_REQ_FDOPEN, ?int32(Fd)). + fdopen(Protocol, Family, Type, Fd, true). + +fdopen(Protocol, Family, Type, Fd, Bound) + when is_integer(Fd), Bound == true orelse Bound == false -> + open(Protocol, Family, Type, [], ?INET_REQ_FDOPEN, + [?int32(Fd), enc_value_2(bool, Bound)]). open(Protocol, Family, Type, Opts, Req, Data) -> Drv = protocol2drv(Protocol), diff --git a/erts/vsn.mk b/erts/vsn.mk index 0355901877..af74fb96c9 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -17,7 +17,7 @@ # %CopyrightEnd% # -VSN = 5.10.4 +VSN = 5.10.4.1 SYSTEM_VSN = R16B03-1 # Port number 4365 in 4.2 diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 11a0843c10..3ab459d4ed 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -112,7 +112,12 @@ do_recv(Sock, Bs) ->

If a socket has somehow been connected without using gen_tcp, use this option to pass the file - descriptor for it.

+ descriptor for it. If {ip, ip_address()} + and/or {port, port_number()} is combined with + this option the fd will be bound to the given interface + and port before connecting. If these options are not given + it is assumed that the fd is already bound appropriately. +

inet diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index b1c9d56c2d..74fc7b0ac6 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1246,9 +1246,9 @@ open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module) Error -> Error end; -open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) +open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when is_integer(Fd) -> - fdopen(Fd, Opts, Protocol, Family, Type, Module). + fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module). bindx(S, [Addr], Port0) -> {IP, Port} = set_bindx_port(Addr, Port0), @@ -1287,12 +1287,34 @@ change_bindx_0_port({_IP, _Port}=Addr, _AssignedPort) -> {'ok', socket()} | {'error', posix()}. fdopen(Fd, Opts, Protocol, Family, Type, Module) -> - case prim_inet:fdopen(Protocol, Family, Type, Fd) of + fdopen(Fd, undefined, 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}), + Bound = Port == 0 andalso IsAnyAddr, + case prim_inet:fdopen(Protocol, Family, Type, Fd, Bound) of {ok, S} -> case prim_inet:setopts(S, Opts) of ok -> - inet_db:register_socket(S, Module), - {ok, S}; + 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 + end; Error -> prim_inet:close(S), Error end; diff --git a/lib/kernel/test/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl index a7af00c12a..c27d265550 100644 --- a/lib/kernel/test/gen_tcp_api_SUITE.erl +++ b/lib/kernel/test/gen_tcp_api_SUITE.erl @@ -32,14 +32,16 @@ t_connect_bad/1, t_recv_timeout/1, t_recv_eof/1, t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1, - t_fdopen/1, t_implicit_inet6/1]). + t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]). + +-export([getsockfd/0,closesockfd/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [{group, t_accept}, {group, t_connect}, {group, t_recv}, t_shutdown_write, t_shutdown_both, t_shutdown_error, - t_fdopen, t_implicit_inet6]. + t_fdopen, t_fdconnect, t_implicit_inet6]. groups() -> [{t_accept, [], [t_accept_timeout]}, @@ -185,6 +187,37 @@ t_fdopen(Config) when is_list(Config) -> ?line ok = gen_tcp:close(L), ok. +t_fdconnect(Config) when is_list(Config) -> + Question = "Aaaa... Long time ago in a small town in Germany,", + Question1 = list_to_binary(Question), + Question2 = [<<"Aaaa">>, "... ", $L, <<>>, $o, "ng time ago ", + ["in ", [], <<"a small town">>, [" in Germany,", <<>>]]], + Question1 = iolist_to_binary(Question2), + Answer = "there was a shoemaker, Schumacher was his name.", + Path = ?config(data_dir, Config), + Lib = "gen_tcp_api_SUITE", + ok = erlang:load_nif(filename:join(Path,Lib), []), + {ok, L} = gen_tcp:listen(0, [{active, false}]), + {ok, Port} = inet:port(L), + FD = gen_tcp_api_SUITE:getsockfd(), + {ok, Client} = gen_tcp:connect(localhost, Port, [{fd,FD},{port,20002}, + {active,false}]), + {ok, Server} = gen_tcp:accept(L), + ok = gen_tcp:send(Client, Question), + {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), + ok = gen_tcp:send(Client, Question1), + {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), + ok = gen_tcp:send(Client, Question2), + {ok, Question} = gen_tcp:recv(Server, length(Question), 2000), + ok = gen_tcp:send(Server, Answer), + {ok, Answer} = gen_tcp:recv(Client, length(Answer), 2000), + ok = gen_tcp:close(Client), + FD = gen_tcp_api_SUITE:closesockfd(FD), + {error,closed} = gen_tcp:recv(Server, 1, 2000), + ok = gen_tcp:close(Server), + ok = gen_tcp:close(L), + ok. + %%% implicit inet6 option to api functions @@ -300,3 +333,7 @@ unused_ip(A, B, C, D) -> end. ok({ok,V}) -> V. + + +getsockfd() -> undefined. +closesockfd(_FD) -> undefined. diff --git a/lib/kernel/test/gen_tcp_api_SUITE_data/Makefile.src b/lib/kernel/test/gen_tcp_api_SUITE_data/Makefile.src new file mode 100644 index 0000000000..b6027de0d7 --- /dev/null +++ b/lib/kernel/test/gen_tcp_api_SUITE_data/Makefile.src @@ -0,0 +1,8 @@ + +NIF_LIBS = gen_tcp_api_SUITE@dll@ + +all: $(NIF_LIBS) + +@SHLIB_RULES@ + +$(NIF_LIBS): gen_tcp_api_SUITE.c diff --git a/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c b/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c new file mode 100644 index 0000000000..6be7838363 --- /dev/null +++ b/lib/kernel/test/gen_tcp_api_SUITE_data/gen_tcp_api_SUITE.c @@ -0,0 +1,61 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2009-2013. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +#include "erl_nif.h" + +#include +#include +#include +#include +#include +#include + +#ifdef __WIN32__ +#include +#else + +#endif + +#define sock_open(af, type, proto) socket((af), (type), (proto)) + +static ERL_NIF_TERM getsockfd(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int fd; + + fd = sock_open(AF_INET, SOCK_STREAM, 0); + return enif_make_int(env, fd); +} + +static ERL_NIF_TERM closesockfd(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + int fd; + + enif_get_int(env, argv[0], &fd); + + close(fd); + + return enif_make_int(env, fd); +} + +static ErlNifFunc nif_funcs[] = +{ + {"getsockfd", 0, getsockfd}, + {"closesockfd", 1, closesockfd} +}; + +ERL_NIF_INIT(gen_tcp_api_SUITE,nif_funcs,NULL,NULL,NULL,NULL) diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 12fe0a7637..08f8d99a6e 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 2.16.4 +KERNEL_VSN = 2.16.4.1 -- cgit v1.2.3 From 03a1fcf5346233f76c8dc50844e132f4760154d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Mon, 26 May 2014 19:33:24 +0200 Subject: erts: Fix inet close on prebound fds Inet close must remove fd from select/poll without closing the fd. --- erts/emulator/drivers/common/inet_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 4f0bf1167e..4cc31c1344 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -4053,7 +4053,7 @@ static int erl_inet_close(inet_descriptor* desc) desc_close(desc); desc->state = INET_STATE_CLOSED; } else if (desc->prebound && (desc->s != INVALID_SOCKET)) { - sock_select(desc, FD_READ | FD_WRITE | FD_CLOSE, 0); + sock_select(desc, FD_READ | FD_WRITE | FD_CLOSE | ERL_DRV_USE_NO_CALLBACK, 0); desc->event_mask = 0; #ifdef __WIN32__ desc->forced_events = 0; -- cgit v1.2.3 From a60f3d879dfcdd018f1fea20790be901fbafeae4 Mon Sep 17 00:00:00 2001 From: Henrik Nord Date: Tue, 22 Jul 2014 17:37:52 +0200 Subject: Fix default behaviour for legacy fdopen Update testcase for gen_udp:open/2 with option fd --- lib/kernel/src/inet.erl | 5 +++-- lib/kernel/test/gen_udp_SUITE.erl | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 74fc7b0ac6..35236f4cb3 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1287,10 +1287,11 @@ change_bindx_0_port({_IP, _Port}=Addr, _AssignedPort) -> {'ok', socket()} | {'error', posix()}. fdopen(Fd, Opts, Protocol, Family, Type, Module) -> - fdopen(Fd, undefined, 0, 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}), + IsAnyAddr = (Addr == {0,0,0,0} orelse Addr == {0,0,0,0,0,0,0,0} + orelse Addr == any), Bound = Port == 0 andalso IsAnyAddr, case prim_inet:fdopen(Protocol, Family, Type, Fd, Bound) of {ok, S} -> diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index cd768813cf..8cc811e0b5 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -448,8 +448,8 @@ open_fd(Config) when is_list(Config) -> {ok,S1} = gen_udp:open(0), {ok,P2} = inet:port(S1), {ok,FD} = prim_inet:getfd(S1), - {error,einval} = gen_udp:open(P2, [inet6, {fd,FD}]), - {ok,S2} = gen_udp:open(P2, [{fd,FD}]), + {error,einval} = gen_udp:open(0, [inet6, {fd,FD}]), + {ok,S2} = gen_udp:open(0, [{fd,FD}]), {ok,S3} = gen_udp:open(0), {ok,P3} = inet:port(S3), ok = gen_udp:send(S3, Addr, P2, Msg), -- cgit v1.2.3