From e293ad1b08b2f937555a102e6f3b4336574773c8 Mon Sep 17 00:00:00 2001 From: Serge Aleynikov Date: Wed, 30 Dec 2015 13:29:34 -0500 Subject: Assign externally open fd to gen_tcp (UDS support) When a AF_LOCAL file descriptor is created externally (e.g. Unix Domain Socket) and passed to `gen_tcp:listen(0, [{fd, FD}])`, the implementation incorrectly assigned the address family to be equal to `inet`, which in the inet_drv driver translated to AF_INET instead of AF_LOCAL (or AF_UNIX), and an `einval` error code was returned. This patch fixes this problem such that the file descriptors of the `local` address family are supported in the inet:fdopen/5, gen_tcp:connect/3, gen_tcp:listen/2, gen_udp:open/2 calls --- lib/kernel/doc/src/gen_tcp.xml | 7 +++++++ lib/kernel/doc/src/gen_udp.xml | 7 +++++++ lib/kernel/src/inet.erl | 20 +++++++++++++++----- lib/kernel/src/inet_int.hrl | 6 +++++- lib/kernel/src/inet_tcp.erl | 11 ++++++++--- lib/kernel/src/inet_udp.erl | 8 ++++++-- 6 files changed, 48 insertions(+), 11 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 6a19e76c4f..20a13782ca 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -132,6 +132,13 @@ do_recv(Sock, Bs) ->

Set up the socket for IPv6.

+ local + +

Set up the socket for local address family. This option is only + valid together with {fd, integer()} when the file descriptor + is of local address family (e.g. a Unix Domain Socket)

+
+ {port, Port}

Specify which local port number to use.

diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index 79cd87dcef..72f25d8d3f 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -101,6 +101,13 @@

Set up the socket for IPv4.

+ local + +

Set up the socket for local address family. This option is only + valid together with {fd, integer()} when the file descriptor + is of local address family (e.g. a Unix Domain Socket)

+
+ {udp_module, module()}

Override which callback module is used. Defaults to diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index b573112445..8840f05fa1 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -40,7 +40,7 @@ -export([i/0, i/1, i/2]). --export([getll/1, getfd/1, open/8, fdopen/6]). +-export([getll/1, getfd/1, open/8, fdopen/6, getfamily/1]). -export([tcp_controlling_process/2, udp_controlling_process/2, tcp_close/1, udp_close/1]). @@ -133,7 +133,7 @@ 'running' | 'multicast' | 'loopback']} | {'hwaddr', ether_address()}. --type address_family() :: 'inet' | 'inet6'. +-type address_family() :: 'inet' | 'inet6' | 'local'. -type socket_protocol() :: 'tcp' | 'udp' | 'sctp'. -type socket_type() :: 'stream' | 'dgram' | 'seqpacket'. -type stat_option() :: @@ -711,6 +711,7 @@ con_opt([Opt | Opts], #connect_opts{} = R, As) -> {tcp_module,_} -> con_opt(Opts, R, As); inet -> con_opt(Opts, R, As); inet6 -> con_opt(Opts, R, As); + local -> con_opt(Opts, R#connect_opts { family = local }, As); {netns,NS} -> BinNS = filename2binary(NS), case prim_inet:is_sockopt_val(netns, BinNS) of @@ -783,6 +784,7 @@ list_opt([Opt | Opts], #listen_opts{} = R, As) -> {tcp_module,_} -> list_opt(Opts, R, As); inet -> list_opt(Opts, R, As); inet6 -> list_opt(Opts, R, As); + local -> list_opt(Opts, R#listen_opts { family = local }, As); {netns,NS} -> BinNS = filename2binary(NS), case prim_inet:is_sockopt_val(netns, BinNS) of @@ -839,8 +841,9 @@ udp_opt([Opt | Opts], #udp_opts{} = R, As) -> binary -> udp_add(mode, binary, R, Opts, As); list -> udp_add(mode, list, R, Opts, As); {udp_module,_} -> udp_opt(Opts, R, As); - inet -> udp_opt(Opts, R, As); - inet6 -> udp_opt(Opts, R, As); + inet -> udp_opt(Opts, R, As); + inet6 -> udp_opt(Opts, R, As); + local -> udp_opt(Opts, R#udp_opts { family = local }, As); {netns,NS} -> BinNS = filename2binary(NS), case prim_inet:is_sockopt_val(netns, BinNS) of @@ -1318,7 +1321,7 @@ fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) -> Bound = Port == 0 andalso IsAnyAddr, case prim_inet:fdopen(Protocol, Family, Type, Fd, Bound) of {ok, S} -> - case prim_inet:setopts(S, Opts) of + case prim_inet:setopts(S, Opts -- [local]) of ok -> case if Bound -> @@ -1345,6 +1348,13 @@ fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) -> Error -> Error end. +-spec getfamily(list()) -> atom(). +getfamily(Options) when is_list(Options) -> + case lists:member(local, Options) of + true -> local; + false -> inet + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% socket stat %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl index e7c6cf8ae2..0a2201298a 100644 --- a/lib/kernel/src/inet_int.hrl +++ b/lib/kernel/src/inet_int.hrl @@ -29,6 +29,7 @@ -define(INET_AF_INET6, 2). -define(INET_AF_ANY, 3). % Fake for ANY in any address family -define(INET_AF_LOOPBACK, 4). % Fake for LOOPBACK in any address family +-define(INET_AF_LOCAL, 5). % For Unix Domain address family %% type codes to open and gettype - INET_REQ_GETTYPE -define(INET_TYPE_STREAM, 1). @@ -378,7 +379,8 @@ { ifaddr = any, %% bind to interface address port = 0, %% bind to port (default is dynamic port) - fd = -1, %% fd >= 0 => already bound + fd = -1, %% fd >= 0 => already bound + family = inet, %% address family opts = [] %% [{active,true}] added in inet:connect_options }). @@ -388,6 +390,7 @@ port = 0, %% bind to port (default is dynamic port) backlog = ?LISTEN_BACKLOG, %% backlog fd = -1, %% %% fd >= 0 => already bound + family = inet, %% address family opts = [] %% [{active,true}] added in %% inet:listen_options }). @@ -397,6 +400,7 @@ ifaddr = any, port = 0, fd = -1, + family = inet, opts = [{active,true}] }). diff --git a/lib/kernel/src/inet_tcp.erl b/lib/kernel/src/inet_tcp.erl index f551af9709..ad0a6159fe 100644 --- a/lib/kernel/src/inet_tcp.erl +++ b/lib/kernel/src/inet_tcp.erl @@ -108,9 +108,10 @@ do_connect({A,B,C,D}, Port, Opts, Time) when ?ip(A,B,C,D), ?port(Port) -> {ok, #connect_opts{fd=Fd, ifaddr=BAddr={Ab,Bb,Cb,Db}, port=BPort, + family=Family, opts=SockOpts}} when ?ip(Ab,Bb,Cb,Db), ?port(BPort) -> - case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of + case inet:open(Fd,BAddr,BPort,SockOpts,tcp,Family,stream,?MODULE) of {ok, S} -> case prim_inet:connect(S, {A,B,C,D}, Port, Time) of ok -> {ok,S}; @@ -130,9 +131,10 @@ listen(Port, Opts) -> {ok, #listen_opts{fd=Fd, ifaddr=BAddr={A,B,C,D}, port=BPort, + family=Family, opts=SockOpts}=R} when ?ip(A,B,C,D), ?port(BPort) -> - case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of + case inet:open(Fd,BAddr,BPort,SockOpts,tcp,Family,stream,?MODULE) of {ok, S} -> case prim_inet:listen(S, R#listen_opts.backlog) of ok -> {ok, S}; @@ -165,4 +167,7 @@ accept(L,Timeout) -> %% Create a port/socket from a file descriptor %% fdopen(Fd, Opts) -> - inet:fdopen(Fd, Opts, tcp, inet, stream, ?MODULE). + fdopen(Fd, inet:getfamily(Opts), Opts). + +fdopen(Fd, Family, Opts) -> + inet:fdopen(Fd, Opts, tcp, Family, stream, ?MODULE). diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl index 5b2e5120c9..74b2874d5b 100644 --- a/lib/kernel/src/inet_udp.erl +++ b/lib/kernel/src/inet_udp.erl @@ -52,8 +52,9 @@ open(Port, Opts) -> {ok, #udp_opts{fd=Fd, ifaddr=BAddr={A,B,C,D}, port=BPort, + family=Family, opts=SockOpts}} when ?ip(A,B,C,D), ?port(BPort) -> - inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,dgram,?MODULE); + inet:open(Fd,BAddr,BPort,SockOpts,udp,Family,dgram,?MODULE); {ok, _} -> exit(badarg) end. @@ -92,9 +93,12 @@ controlling_process(Socket, NewOwner) -> %% Create a port/socket from a file descriptor %% fdopen(Fd, Opts) -> + fdopen(Fd, inet:getfamily(Opts), Opts). + +fdopen(Fd, Family, Opts) -> inet:fdopen(Fd, optuniquify([{recbuf, ?RECBUF} | Opts]), - udp, inet, dgram, ?MODULE). + udp, Family, dgram, ?MODULE). %% Remove all duplicate options from an option list. -- cgit v1.2.3