aboutsummaryrefslogtreecommitdiffstats
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
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
-rw-r--r--erts/configure.in2
-rw-r--r--erts/emulator/drivers/common/inet_drv.c161
-rw-r--r--erts/preloaded/src/prim_inet.erl17
-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
9 files changed, 210 insertions, 29 deletions
diff --git a/erts/configure.in b/erts/configure.in
index 4fb725ff00..68ead3285b 100644
--- a/erts/configure.in
+++ b/erts/configure.in
@@ -2238,7 +2238,7 @@ fi
dnl Need by run_erl.
AC_CHECK_FUNCS([openpty])
-AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h)
+AC_CHECK_HEADERS(net/if_dl.h ifaddrs.h netpacket/packet.h sys/un.h)
AC_CHECK_FUNCS([getifaddrs])
dnl Checks for variables in6addr_any and in6addr_loopback,
diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c
index 89011d89ad..bd4543f831 100644
--- a/erts/emulator/drivers/common/inet_drv.c
+++ b/erts/emulator/drivers/common/inet_drv.c
@@ -58,6 +58,9 @@
#ifdef HAVE_NETPACKET_PACKET_H
#include <netpacket/packet.h>
#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
/* All platforms fail on malloc errors. */
#define FATAL_MALLOC
@@ -747,6 +750,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define INET_AF_INET6 2
#define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */
#define INET_AF_LOOPBACK 4 /* INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT */
+#define INET_AF_LOCAL 5
/* open and INET_REQ_GETTYPE enumeration */
#define INET_TYPE_STREAM 1
@@ -1032,19 +1036,29 @@ typedef union {
#ifdef HAVE_IN6
struct sockaddr_in6 sai6;
#endif
+#ifdef HAVE_SYS_UN_H
+ struct sockaddr_un sal;
+#endif
} inet_address;
/* for AF_INET & AF_INET6 */
#define inet_address_port(x) ((x)->sai.sin_port)
+#ifdef HAVE_SYS_UN_H
+#define localaddrlen(family, data) \
+ ((family == AF_LOCAL) ? *(unsigned char*)(data) : 0)
+#else
+ 0
+#endif
+
#if defined(HAVE_IN6) && defined(AF_INET6)
-#define addrlen(family) \
+#define addrlen(family, data) \
((family == AF_INET) ? sizeof(struct in_addr) : \
- ((family == AF_INET6) ? sizeof(struct in6_addr) : 0))
+ ((family == AF_INET6) ? sizeof(struct in6_addr) : localaddrlen(family, data)))
#else
-#define addrlen(family) \
- ((family == AF_INET) ? sizeof(struct in_addr) : 0)
+#define addrlen(family, data) \
+ ((family == AF_INET) ? sizeof(struct in_addr) : localaddrlen(family, data))
#endif
typedef struct _multi_timer_data {
@@ -1728,6 +1742,12 @@ static int load_ip_address(ErlDrvTermData* spec, int i, int family, char* buf)
spec[i++] = 8;
}
#endif
+#ifdef HAVE_SYS_UN_H
+ else if (family == AF_LOCAL) {
+ int len = *(unsigned char*)buf++;
+ i = LOAD_STRING(spec, i, buf, len);
+ }
+#endif
else {
spec[i++] = ERL_DRV_TUPLE;
spec[i++] = 0;
@@ -3573,10 +3593,11 @@ static int tcp_error_message(tcp_descriptor* desc, int err)
#ifdef HAVE_UDP
/*
** active mode message:
-** {udp, S, IP, Port, [H1,...Hsz | Data]} or
-** {sctp, S, IP, Port, {[AncilData], Event_or_Data}}
+** {udp, S, IP, Port, [H1,...Hsz | Data]} or
+** {sctp, S, IP, Port, {[AncilData], Event_or_Data}}
** where
** [H1,...,HSz] are msg headers (without IP/Port, UDP only),
+** [AddrLen, H2,...,HSz] are msg headers for UDP AF_LOCAL only
** Data : List() | Binary()
*/
static int packet_binary_message
@@ -3586,6 +3607,7 @@ static int packet_binary_message
ErlDrvTermData spec [PACKET_ERL_DRV_TERM_DATA_LEN];
int i = 0;
int alen;
+ char* data = bin->orig_bytes+offs;
DEBUGF(("packet_binary_message(%ld): len = %d\r\n",
(long)desc->port, len));
@@ -3596,10 +3618,15 @@ static int packet_binary_message
# endif
i = LOAD_PORT(spec, i, desc->dport); /* S */
- alen = addrlen(desc->sfamily);
- i = load_ip_address(spec, i, desc->sfamily, bin->orig_bytes+offs+3);
- i = load_ip_port(spec, i, bin->orig_bytes+offs+1); /* IP, Port */
+ alen = addrlen(desc->sfamily, data+3);
+ i = load_ip_address(spec, i, desc->sfamily, data+3);
+ i = load_ip_port(spec, i, data+1); /* IP, Port */
+# ifdef HAVE_SYS_UN_H
+ /* AF_LOCAL addresses have a prefix byte containing address length */
+ if (desc->sfamily == AF_LOCAL)
+ alen++;
+# endif
offs += (alen + 3);
len -= (alen + 3);
@@ -4152,6 +4179,16 @@ static char* inet_set_address(int family, inet_address* dst,
return src + 2+16;
}
#endif
+#ifdef HAVE_SYS_UN_H
+ else if ((family == AF_LOCAL) && (*len >= 3+sizeof(struct sockaddr_un))) {
+ int n = *((unsigned char*)src+2);
+ dst->sal.sun_family = family;
+ sys_memcpy(dst->sal.sun_path, src+3, n);
+ dst->sal.sun_path[n-1] = '\0';
+ *len = n;
+ return src + 3 + n;
+ }
+#endif
return NULL;
}
@@ -4160,7 +4197,7 @@ static char* inet_set_address(int family, inet_address* dst,
** or from argument if source data specifies constant address.
**
** src = [TAG,P1,P0] when TAG = INET_AF_ANY | INET_AF_LOOPBACK
-** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6
+** src = [TAG,P1,P0,X1,X2,...] when TAG = INET_AF_INET | INET_AF_INET6 | INET_AF_LOCAL
*/
static char *inet_set_faddress(int family, inet_address* dst,
char *src, ErlDrvSizeT* len) {
@@ -4178,6 +4215,21 @@ static char *inet_set_faddress(int family, inet_address* dst,
family = AF_INET6;
break;
# endif
+# ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL: {
+ int n;
+ if (*len || *len < 3) return NULL;
+ family = AF_LOCAL;
+ /* Next two bytes are the length of the local path (< 256) */
+ src++;
+ n = *(unsigned char*)src++;
+ if (n+3 > *len) return NULL;
+ dst->sal.sun_family = family;
+ sys_memcpy(dst->sal.sun_path, src, n);
+ *len = n;
+ break;
+ }
+# endif
case INET_AF_ANY:
case INET_AF_LOOPBACK: {
int port;
@@ -4241,7 +4293,6 @@ static char *inet_set_faddress(int family, inet_address* dst,
return inet_set_address(family, dst, src, len);
}
-
/* Get a inaddr structure
** src = inaddr structure
** *len is the lenght of structure
@@ -4274,9 +4325,54 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len)
return 0;
}
#endif
+#ifdef HAVE_SYS_UN_H
+ else if ((family == AF_LOCAL) && *len > 0) {
+ int n = *len - 4;
+ dst[0] = INET_AF_LOCAL;
+ put_int16(0, dst+1);
+ if (n == 0 || n >= sizeof(src->sal.sun_path)) {
+ *(dst+3) = 0;
+ *len = 3+1;
+ } else {
+ *(dst+3) = n;
+ sys_memcpy(dst+4, src->sal.sun_path, n);
+ *len = 3+1+n;
+ }
+ return 0;
+ }
+#endif
return -1;
}
+static int inet_family_get_address(inet_descriptor* desc, char* dst, inet_address* src, unsigned int* len)
+{
+#ifdef HAVE_SYS_UN_H
+ if (desc->sfamily == AF_LOCAL) {
+ int n = *len - 4;
+ dst[0] = INET_AF_LOCAL;
+ put_int16(0, dst+1);
+ if (n <= 0 || n >= sizeof(src->sal.sun_path)) {
+ if (desc->name_ptr) {
+ char* p = desc->name_ptr->sal.sun_path;
+ n = strlen(p);
+ *(dst+3) = n;
+ sys_memcpy(dst+4, p, n);
+ *len = 3+1+n;
+ } else {
+ *(dst+3) = 0;
+ *len = 3+1;
+ }
+ } else {
+ *(dst+3) = n;
+ sys_memcpy(dst+4, src->sal.sun_path, n);
+ *len = 3+1+n;
+ }
+ return 0;
+ }
+#endif
+ return inet_get_address(dst, src, len);
+}
+
/* Same as the above, but take family from the address structure,
** and advance the address pointer to the next address
** according to the size of the current,
@@ -4307,6 +4403,19 @@ static int inet_address_to_erlang(char *dst, inet_address **src) {
(*src) = (inet_address *) (&(*src)->sai6 + 1);
return 1 + 2 + 16;
#endif
+#ifdef HAVE_SYS_UN_H
+ case AF_LOCAL: {
+ int n = strlen((*src)->sal.sun_path);
+ if (dst) {
+ dst[0] = INET_AF_LOCAL;
+ put_int16(0, dst+1);
+ *(dst+3) = n;
+ sys_memcpy(dst+1+2+1, (*src)->sal.sun_path, n);
+ }
+ (*src) = (inet_address *) (&(*src)->sal + 1);
+ return 1+2+1+n;
+ }
+#endif
default:
return -1;
}
@@ -4402,7 +4511,8 @@ static void desc_close_read(inet_descriptor* desc)
static int erl_inet_close(inet_descriptor* desc)
{
free_subscribers(&desc->empty_out_q_subs);
- if ((desc->prebound == 0) && (desc->state & INET_F_OPEN)) {
+ if ((desc->prebound == 0 || desc->sfamily == AF_LOCAL) &&
+ (desc->state & INET_F_OPEN)) {
desc_close(desc);
desc->state = INET_STATE_CLOSED;
} else if (desc->prebound && (desc->s != INVALID_SOCKET)) {
@@ -4582,6 +4692,13 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type,
return ctl_error(sock_errno(), rbuf, rsize);
if (name.sa.sa_family != domain)
return ctl_error(EINVAL, rbuf, rsize);
+#ifdef HAVE_SYS_UN_H
+ if (domain == AF_LOCAL) {
+ sys_memcpy(&desc->name_addr, &name, sizeof(desc->name_addr));
+ if (desc->name_ptr == NULL)
+ desc->name_ptr = &desc->name_addr;
+ }
+#endif
}
#ifdef __OSE__
/* for fdopen duplicating the sd will allow to uniquely identify
@@ -8571,6 +8688,11 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf,
put_int32(INET_AF_INET6, &tbuf[0]);
}
#endif
+#ifdef HAVE_SYS_UN_H
+ else if (desc->sfamily == AF_LOCAL) {
+ put_int32(INET_AF_LOCAL, &tbuf[0]);
+ }
+#endif
else
return ctl_error(EINVAL, rbuf, rsize);
@@ -9256,6 +9378,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_xerror("eafnosupport", rbuf, rsize);
break;
#endif
+#ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL:
+ domain = AF_LOCAL;
+ break;
+#endif
default:
return ctl_error(EINVAL, rbuf, rsize);
}
@@ -9282,6 +9409,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd,
return ctl_xerror("eafnosupport", rbuf, rsize);
break;
#endif
+#ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL:
+ domain = AF_LOCAL;
+ break;
+#endif
default:
return ctl_error(EINVAL, rbuf, rsize);
}
@@ -11344,6 +11476,9 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf,
return ctl_xerror("eafnosupport", rbuf, rsize);
break;
#endif
+#ifdef HAVE_SYS_UN_H
+ case INET_AF_LOCAL: af = AF_LOCAL; break;
+#endif
default:
return ctl_error(EINVAL, rbuf, rsize);
}
@@ -11955,7 +12090,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event)
inet_input_count(desc, n);
udesc->i_ptr += n;
- inet_get_address(abuf, &other, &len);
+ inet_family_get_address(desc, abuf, &other, &len);
/* Copy formatted address to the buffer allocated; "len" is the
actual length which must be <= than the original reserved.
This means that the addr + data in the buffer are contiguous,
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index bd74831bb7..4659448221 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -70,6 +70,8 @@ open(Protocol, Family, Type) ->
open(Protocol, Family, Type, Opts) ->
open(Protocol, Family, Type, Opts, ?INET_REQ_OPEN, []).
+%% FDOPEN(tcp|udp|sctp, inet|inet6|local, stream|dgram|seqpacket, integer())
+
fdopen(Protocol, Family, Type, Fd) when is_integer(Fd) ->
fdopen(Protocol, Family, Type, Fd, true).
@@ -104,8 +106,9 @@ open(Protocol, Family, Type, Opts, Req, Data) ->
error:system_limit -> {error, system_limit}
end.
-enc_family(inet) -> ?INET_AF_INET;
-enc_family(inet6) -> ?INET_AF_INET6.
+enc_family(inet) -> ?INET_AF_INET;
+enc_family(inet6) -> ?INET_AF_INET6;
+enc_family(local) -> ?INET_AF_LOCAL.
enc_type(stream) -> ?INET_TYPE_STREAM;
enc_type(dgram) -> ?INET_TYPE_DGRAM;
@@ -1619,6 +1622,8 @@ enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 4 ->
[?INET_AF_INET,?int16(Port)|ip4_to_bytes(IP)];
enc_value_2(addr, {IP,Port}) when tuple_size(IP) =:= 8 ->
[?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)];
+enc_value_2(addr, {File,0}) when is_list(File) ->
+ [?INET_AF_LOCAL,0,0,length(File)|File];
enc_value_2(ether, [_,_,_,_,_,_]=Xs) -> Xs;
enc_value_2(sockaddr, any) ->
[?INET_AF_ANY];
@@ -1628,6 +1633,8 @@ enc_value_2(sockaddr, IP) when tuple_size(IP) =:= 4 ->
[?INET_AF_INET|ip4_to_bytes(IP)];
enc_value_2(sockaddr, IP) when tuple_size(IP) =:= 8 ->
[?INET_AF_INET6|ip6_to_bytes(IP)];
+enc_value_2(sockaddr, File) when is_list(File) ->
+ [?INET_AF_LOCAL,0,0,length(File)|File];
enc_value_2(linkaddr, Linkaddr) ->
[?int16(length(Linkaddr)),Linkaddr];
enc_value_2(sctp_assoc_id, Val) -> ?int32(Val);
@@ -2265,8 +2272,10 @@ get_addrs([F,P1,P0|Addr]) ->
{IP,Addrs} = get_ip(F, Addr),
[{IP,?u16(P1, P0)}|get_addrs(Addrs)].
-get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr);
-get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr).
+get_ip(?INET_AF_INET, Addr) -> get_ip4(Addr);
+get_ip(?INET_AF_INET6, Addr) -> get_ip6(Addr);
+get_ip(?INET_AF_LOCAL, [0]) -> {[], []};
+get_ip(?INET_AF_LOCAL, [N | Addr]) -> lists:split(N, Addr).
get_ip4([A,B,C,D | T]) -> {{A,B,C,D},T}.
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.