aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
authorSerge Aleynikov <[email protected]>2015-12-30 13:29:34 -0500
committerSerge Aleynikov <[email protected]>2016-01-12 11:18:12 -0500
commite293ad1b08b2f937555a102e6f3b4336574773c8 (patch)
treeb2eb8a92e8386e9d9589503929b2cf8afac13068 /lib/kernel
parent1237669f7c59714f0c27d3df748241dfd655c0be (diff)
downloadotp-e293ad1b08b2f937555a102e6f3b4336574773c8.tar.gz
otp-e293ad1b08b2f937555a102e6f3b4336574773c8.tar.bz2
otp-e293ad1b08b2f937555a102e6f3b4336574773c8.zip
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
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/gen_tcp.xml7
-rw-r--r--lib/kernel/doc/src/gen_udp.xml7
-rw-r--r--lib/kernel/src/inet.erl20
-rw-r--r--lib/kernel/src/inet_int.hrl6
-rw-r--r--lib/kernel/src/inet_tcp.erl11
-rw-r--r--lib/kernel/src/inet_udp.erl8
6 files changed, 48 insertions, 11 deletions
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) ->
<p>Set up the socket for IPv6.</p>
</item>
+ <tag><c>local</c></tag>
+ <item>
+ <p>Set up the socket for local address family. This option is only
+ valid together with <c>{fd, integer()}</c> when the file descriptor
+ is of local address family (e.g. a Unix Domain Socket)</p>
+ </item>
+
<tag><c>{port, Port}</c></tag>
<item>
<p>Specify which local port number to use.</p>
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 @@
<p>Set up the socket for IPv4.</p>
</item>
+ <tag><c>local</c></tag>
+ <item>
+ <p>Set up the socket for local address family. This option is only
+ valid together with <c>{fd, integer()}</c> when the file descriptor
+ is of local address family (e.g. a Unix Domain Socket)</p>
+ </item>
+
<tag><c>{udp_module, module()}</c></tag>
<item> <p>
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.