From 63338250778d2caad08aa3180b372e5260f22aa7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 31 May 2018 10:58:19 +0200 Subject: [socket-nif-doc] Add preliminary doc for socket The doc now builds. Had to update the code (spec and types) to match. Though, te result is less then stellar. OTP-14831 --- erts/doc/src/Makefile | 6 +- erts/doc/src/ref_man.xml | 3 +- erts/doc/src/socket.xml | 308 +++++++++++++++++++ erts/doc/src/specs.xml | 1 + erts/emulator/nifs/common/socket_nif.c | 535 +++++++++++++++++++++++++++------ erts/preloaded/src/socket.erl | 152 +++++----- 6 files changed, 821 insertions(+), 184 deletions(-) create mode 100644 erts/doc/src/socket.xml (limited to 'erts') diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index 21aa3db864..1540344fde 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -52,7 +52,8 @@ XML_REF3_EFILES = \ erlang.xml \ erl_tracer.xml \ init.xml \ - zlib.xml + zlib.xml \ + socket.xml XML_REF3_FILES = \ driver_entry.xml \ @@ -63,7 +64,8 @@ XML_REF3_FILES = \ erlang.xml \ erts_alloc.xml \ init.xml \ - zlib.xml + zlib.xml \ + socket.xml XML_PART_FILES = \ part.xml diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml index 0617463a7b..da099dd5bb 100644 --- a/erts/doc/src/ref_man.xml +++ b/erts/doc/src/ref_man.xml @@ -4,7 +4,7 @@
- 19962016 + 19962018 Ericsson AB. All Rights Reserved. @@ -35,6 +35,7 @@ + diff --git a/erts/doc/src/socket.xml b/erts/doc/src/socket.xml new file mode 100644 index 0000000000..9b487172c5 --- /dev/null +++ b/erts/doc/src/socket.xml @@ -0,0 +1,308 @@ + + + + +
+ + 20182018 + Ericsson AB. All Rights Reserved. + + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + + socket + + + + + socket.xml +
+ socket + Socket interface. + +

This module provides an API for the socket interface. + It is used to create, delete and manipulate sockets.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Accept a connection on a socket. + +

Accept a connection on a socket.

+

This call is used with connection-based socket types + (stream or seqpacket). It extracs the first pending connection + request for the listen socket and returns the (newly) connected + socket.

+
+
+ + + + Bind a name to a socket. + +

Bind a name to a socket.

+

When a socket is created + (with open), + it has no address assigned to it. bind assigns the + address specified by the Addr argument.

+

The rules used for name binding vary between domains.

+
+
+ + + + Close a socket. + +

Closes the socket.

+
+
+ + + + + Initiate a connection on a socket. + +

This function connects the socket to the address + specied by the Addr argument.

+
+
+ + + + + + + + + + + Get an option on a socket. + +

Get an option on a socket.

+

What properties are valid depend on what kind of socket + it is (domain, type and protocol).

+

When specifying Level as an integer, and therefor + using "native mode", it is *currently* up to the caller to + know how to interpret the result.

+ +

Not all options are valid on all platforms. That is, + even if "we" support an option, that does not mean that the + underlying OS does.

+
+
+ + + + + Listen for connections on a socket. + +

Listen for connections on a socket.

+
+
+ + + + + + Create an endpoint for communication. + +

Creates an endpoint (socket) for communication.

+

For some types there is a default protocol, which will + be used if no protocol is specified:

+ + stream + +

tcp

+
+ dgram + +

udp

+
+ seqpacket + +

sctp

+
+
+
+
+ + + + + + Receive a message from a socket. + +

Receive a message from a socket.

+

There is a special case for the argument Length. + If it is set to zero (0), it means "give me everything you + currently have".

+
+
+ + + + + + + Receive a message from a socket. + +

Receive a message from a socket.

+

This function reads "messages", which means that regardless of + how much we want to read, it returns when we get a message.

+

The MaxSize argument basically defines the size of the + receive buffer. By setting the value to zero (0), the configured + size (setopt) is used.

+

It may be impossible to know what (buffer) size is appropriate + "in advance", and in those cases it may be convenient to use the + (recv) 'peek' flag. When this flag is provided the message is *not* + "consumed" from the underlying buffers, so another recvfrom call + is needed, possibly with a then adjusted buffer size.

+
+
+ + + + + + Send a message on a socket. + +

Send a message on a connected socket.

+
+
+ + + + + Send a message on a socket. + +

Send a message on a socket, to the specified destination.

+
+
+ + + + + + + + + + Set options on a socket. + +

Set options on a socket.

+

What properties are valid depend on what kind of socket + it is (domain, type and protocol).

+ +

Not all options are valid on all platforms. That is, + even if "we" support an option, that does not mean that the + underlying OS does.

+ +

Sockets are set 'non-blocking' when created, so this option + is *not* available (as it would adversely effect the Erlang VM + to set a socket 'blocking').

+
+
+ + + + Shut down part of a full-duplex connection. + +

Shut down all or part of a full-duplex connection.

+
+
+ +
+
+ diff --git a/erts/doc/src/specs.xml b/erts/doc/src/specs.xml index ed6be650e5..4f6951a44b 100644 --- a/erts/doc/src/specs.xml +++ b/erts/doc/src/specs.xml @@ -4,5 +4,6 @@ + diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index c63eff40ec..ed961d691d 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -23,6 +23,8 @@ * */ +#define STATIC_ERLANG_NIF 1 + /* #include */ /* #include */ /* #include */ @@ -425,6 +427,8 @@ typedef union { if (enif_select((E), (FD), (M), (O), (P), (R)) < 0) \ return enif_make_badarg((E)); +#define COMPARE(A, B) enif_compare((A), (B)) + #define IS_ATOM(E, TE) enif_is_atom((E), (TE)) #define IS_BIN(E, TE) enif_is_binary((E), (TE)) #define IS_NUM(E, TE) enif_is_number((E), (TE)) @@ -581,6 +585,7 @@ typedef struct { unsigned int state; SocketAddress remote; + unsigned int addrLen; /* +++ Controller (owner) process +++ */ @@ -710,8 +715,8 @@ static ERL_NIF_TERM nif_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_bind(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); + int argc, + const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_connect(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -760,9 +765,11 @@ static ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, static ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +/* static ERL_NIF_TERM nif_cancel(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +*/ static ERL_NIF_TERM nopen(ErlNifEnv* env, @@ -773,10 +780,8 @@ static ERL_NIF_TERM nopen(ErlNifEnv* env, static ERL_NIF_TERM nbind(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM addr); -static ERL_NIF_TERM nconnect(ErlNifEnv* env, - SocketDescriptor* descP, - const ERL_NIF_TERM* addr, - int port); +static ERL_NIF_TERM nconnect(ErlNifEnv* env, + SocketDescriptor* descP); static ERL_NIF_TERM nlisten(ErlNifEnv* env, SocketDescriptor* descP, int backlog); @@ -1156,6 +1161,43 @@ static ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, SocketDescriptor* descP); +static BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +static BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* eIn4SockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +static BOOLEAN_T decode_in4_sockaddr_atomaddr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +static BOOLEAN_T decode_in4_sockaddr_addr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +static BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* eIn6SockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +static BOOLEAN_T decode_in6_sockaddr_atomaddr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + unsigned int flowInfo, + unsigned int scopeId, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +/* Decode an in6_sockaddr where the address field is a tuple */ +static BOOLEAN_T decode_in6_sockaddr_addr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + unsigned int flowInfo, + unsigned int scopeId, + SocketAddress* sockAddrP, + unsigned int* addrLenP); static char* decode_laddress(ErlNifEnv* env, int domain, ERL_NIF_TERM localAddr, @@ -1184,18 +1226,22 @@ static char* decode_address_atom(ErlNifEnv* env, int port, SocketAddress* localP, unsigned int* addrLenP); +/* static char* decode_send_addr(ErlNifEnv* env, int domain, ERL_NIF_TERM addr, int port, SocketAddress** toAddrP, unsigned int* addrLenP); +*/ +/* static char* decode_send_addr_tuple(ErlNifEnv* env, int domain, ERL_NIF_TERM addr, int port, SocketAddress* toAddrP, unsigned int* addrLenP); +*/ static void encode_address(ErlNifEnv* env, SocketAddress* fromAddrP, unsigned int fromAddrLen, @@ -1278,11 +1324,15 @@ static ERL_NIF_TERM make_error(ErlNifEnv* env, ERL_NIF_TERM reason); static ERL_NIF_TERM make_error1(ErlNifEnv* env, char* reason); static ERL_NIF_TERM make_error2(ErlNifEnv* env, int err); +/* static char* send_msg_error_closed(ErlNifEnv* env, ErlNifPid* pid); +*/ +/* static char* send_msg_error(ErlNifEnv* env, ERL_NIF_TERM reason, ErlNifPid* pid); +*/ static char* send_msg_nif_abort(ErlNifEnv* env, ERL_NIF_TERM ref, ERL_NIF_TERM reason, @@ -1335,22 +1385,26 @@ static const struct in6_addr in6addr_loopback = /* *** String constants *** */ -static char str_close[] = "close"; -static char str_closed[] = "closed"; -static char str_closing[] = "closing"; -static char str_error[] = "error"; -static char str_false[] = "false"; -static char str_nif_abort[] = "nif_abort"; -static char str_ok[] = "ok"; -static char str_select[] = "select"; -static char str_timeout[] = "timeout"; -static char str_true[] = "true"; -static char str_undefined[] = "undefined"; - -static char str_lowdelay[] = "lowdelay"; -static char str_throughput[] = "throughput"; -static char str_reliability[] = "reliability"; -static char str_mincost[] = "mincost"; +static char str_any[] = "any"; +static char str_close[] = "close"; +static char str_closed[] = "closed"; +static char str_closing[] = "closing"; +static char str_error[] = "error"; +static char str_false[] = "false"; +static char str_in4_sockaddr[] = "in4_sockaddr"; +static char str_in6_sockaddr[] = "in6_sockaddr"; +static char str_loopback[] = "loopback"; +static char str_nif_abort[] = "nif_abort"; +static char str_ok[] = "ok"; +static char str_select[] = "select"; +static char str_timeout[] = "timeout"; +static char str_true[] = "true"; +static char str_undefined[] = "undefined"; + +static char str_lowdelay[] = "lowdelay"; +static char str_throughput[] = "throughput"; +static char str_reliability[] = "reliability"; +static char str_mincost[] = "mincost"; /* (special) error string constants */ static char str_eagain[] = "eagain"; @@ -1368,11 +1422,15 @@ static char str_exsend[] = "exsend"; // failed send /* *** Atoms *** */ +static ERL_NIF_TERM atom_any; static ERL_NIF_TERM atom_close; static ERL_NIF_TERM atom_closed; static ERL_NIF_TERM atom_closing; static ERL_NIF_TERM atom_error; static ERL_NIF_TERM atom_false; +static ERL_NIF_TERM atom_in4_sockaddr; +static ERL_NIF_TERM atom_in6_sockaddr; +static ERL_NIF_TERM atom_loopback; static ERL_NIF_TERM atom_nif_abort; static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_select; @@ -1425,11 +1483,11 @@ static SocketData socketData; * ------------------------------ * nif_open(Domain, Type, Protocol, Extra) * nif_bind(Sock, LocalAddr) - * nif_connect(Sock, Addr, Port) + * nif_connect(Sock, SockAddr) * nif_listen(Sock, Backlog) * nif_accept(LSock, Ref) * nif_send(Sock, SendRef, Data, Flags) - * nif_sendto(Sock, SendRef, Data, Flags, DstAddr, DstPort) + * nif_sendto(Sock, SendRef, Data, Flags, DstSockAddr) * nif_recv(Sock, RecvRef, Length, Flags) * nif_recvfrom(Sock, Flags) * nif_close(Sock) @@ -1437,8 +1495,8 @@ static SocketData socketData; * * And some functions to manipulate and retrieve socket options: * ------------------------------------------------------------- - * nif_setopt/3 - * nif_getopt/2 + * nif_setopt/5 + * nif_getopt/4 * * And some utility functions: * ------------------------------------------------------------- @@ -1795,7 +1853,7 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, ERL_NIF_TERM addr) { SocketAddress local; - unsigned int addrLen; + unsigned int addrLen = 0; char* err; int port; @@ -2010,60 +2068,41 @@ char* decode_laddress_tuple(ErlNifEnv* env, * * Arguments: * Socket (ref) - Points to the socket descriptor. - * Addr - Address of "remote" host. - * A tuple of size 4 or 8 (depending on domain) - * Port - Port number of "remote" host. + * SockAddr - Socket Address of "remote" host. + * This is in_sockaddr(), which is either + * in4_sockaddr (#in4_sockaddr{}) or + * in6_sockaddr (#in6_sockaddr{}). */ static ERL_NIF_TERM nif_connect(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - SocketDescriptor* descP; - int addrSz; - const ERL_NIF_TERM* addr; - int port; + SocketDescriptor* descP; + ERL_NIF_TERM eSockAddr; /* Extract arguments and perform preliminary validation */ - if ((argc != 3) || - !enif_get_resource(env, argv[0], sockets, (void**) &descP) || - !GET_TUPLE(env, argv[1], &addrSz, &addr) || - !GET_INT(env, argv[2], &port)) { + if ((argc != 2) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { return enif_make_badarg(env); } + eSockAddr = argv[1]; - switch (descP->domain) { - case AF_INET: - if (addrSz != 4) - return make_error(env, atom_einval); - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - if (addrSz != 8) - return make_error(env, atom_einval); - break; -#endif - - default: - return make_error(env, atom_eafnosupport); - break; - } // switch (descP->domain) + if (!decode_in_sockaddr(env, eSockAddr, + &descP->remote, &descP->addrLen)) { + return enif_make_badarg(env); + } - return nconnect(env, descP, addr, port); + return nconnect(env, descP); } static -ERL_NIF_TERM nconnect(ErlNifEnv* env, - SocketDescriptor* descP, - const ERL_NIF_TERM* addr, - int port) +ERL_NIF_TERM nconnect(ErlNifEnv* env, + SocketDescriptor* descP) { - unsigned int addrLen; - int code; - char* xerr; + int code; /* Verify that we are where in the proper state */ @@ -2076,13 +2115,9 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, if (IS_CONNECTING(descP)) return make_error(env, atom_einval); - if ((xerr = decode_address_tuple(env, - descP->domain, addr, port, - &descP->remote, &addrLen)) != NULL) - return make_error1(env, xerr); - code = sock_connect(descP->sock, - (struct sockaddr*) &descP->remote, addrLen); + (struct sockaddr*) &descP->remote, + descP->addrLen); if (IS_SOCKET_ERROR(code) && ((sock_errno() == ERRNO_BLOCK) || /* Winsock2 */ @@ -2661,8 +2696,7 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, * SendRef - A unique id for this (send) request. * Data - The data to send in the form of a IOVec. * Flags - Send flags. - * DestAddr - Destination address. - * DestPort - Destination Port. + * DestSockAddr - Destination (socket) address. */ static @@ -2675,40 +2709,34 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, ErlNifBinary data; unsigned int eflags; int flags; - ERL_NIF_TERM addr; - int port; + ERL_NIF_TERM eSockAddr; SocketAddress remoteAddr; - SocketAddress* remoteAddrP = &remoteAddr; unsigned int remoteAddrLen; - char* xerr; - // ERL_NIF_TERM res; /* Extract arguments and perform preliminary validation */ if ((argc != 6) || !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !GET_BIN(env, argv[2], &data) || - !GET_UINT(env, argv[3], &eflags) || - !GET_INT(env, argv[5], &port)) { + !GET_UINT(env, argv[3], &eflags)) { return enif_make_badarg(env); } - sendRef = argv[1]; - addr = argv[4]; + sendRef = argv[1]; + eSockAddr = argv[4]; /* THIS TEST IS NOT CORRECT!!! */ if (!IS_OPEN(descP)) return make_error(env, atom_einval); if (!esendflags2sendflags(eflags, &flags)) - return enif_make_badarg(env); + return make_error(env, atom_einval); - if ((xerr = decode_send_addr(env, descP->domain, - addr, port, - &remoteAddrP, - &remoteAddrLen)) != NULL) - return make_error1(env, xerr); + if (!decode_in_sockaddr(env, eSockAddr, + &remoteAddr, + &remoteAddrLen)) + return make_error(env, atom_einval); - return nsendto(env, descP, sendRef, &data, flags, remoteAddrP, remoteAddrLen); + return nsendto(env, descP, sendRef, &data, flags, &remoteAddr, remoteAddrLen); } @@ -5619,6 +5647,7 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, * This function whouls really have a char* return value * type!! */ +/* static char* decode_send_addr(ErlNifEnv* env, int domain, @@ -5631,7 +5660,7 @@ char* decode_send_addr(ErlNifEnv* env, unsigned int len; char a[16]; // Just in case... - /* The only acceptable value is the atom 'null' */ + / * The only acceptable value is the atom 'null' * / if (!(GET_ATOM_LEN(env, addr, &len) && (len > 0) && @@ -5648,15 +5677,16 @@ char* decode_send_addr(ErlNifEnv* env, return str_einval; } else if (IS_TUPLE(env, addr)) { - /* We now know that the we have a proper address. */ + / * We now know that the we have a proper address. * / return decode_send_addr_tuple(env, domain, addr, port, *toAddrP, toAddrLenP); } else { return str_einval; } } +*/ - +/* static char* decode_send_addr_tuple(ErlNifEnv* env, int domain, @@ -5665,10 +5695,10 @@ char* decode_send_addr_tuple(ErlNifEnv* env, SocketAddress* toAddrP, unsigned int* toAddrLenP) { - /* We handle two different tuples: + / * We handle two different tuples: * - size 4 (INET) * - size 8 (INET6) - */ + * / const ERL_NIF_TERM* addrt; int addrtSz; @@ -5699,6 +5729,304 @@ char* decode_send_addr_tuple(ErlNifEnv* env, toAddrP, toAddrLenP); } +*/ + + +/* Decode an in_sockaddr(). This is either a: + * in4_sockaddr: #in4_sockaddr{} = 3 tuple => + * 1: The tag in4_sockaddr + * 2: Port number (an integer()) + * 3: The address: any | loopback | ip4_address() + * in6_sockaddr: #in6_sockaddr{} = 5 tuple => + * 1: The tag in6_sockaddr + * 2: Port number: integer() + * 3: The address: any | loopback | ip6_address() + * 4: Flow info: integer() + * 5: Scope Id: integer() + * + */ +static +BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLenP) +{ + const ERL_NIF_TERM* addrt; + int addrtSz; + ERL_NIF_TERM result; + + if (!GET_TUPLE(env, eSockAddr, &addrtSz, &addrt)) + return FALSE; + + /* + * We use the tuple size to figure out which + * of the records this is. + */ + switch (addrtSz) { + case 3: + result = decode_in4_sockaddr(env, addrt, sockAddrP, addrLenP); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case 5: + result = decode_in6_sockaddr(env, addrt, sockAddrP, addrLenP); + break; +#endif + + default: + result = FALSE; + break; + } + + return result; +} + + + +/* Decode an in4_sockaddr(). + * The first element should be the atom in4_sockaddr + * The second, the port number integer . + * The third and final, the ip4_address tuple. + */ +static +BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* eIn4SockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLenP) +{ + int port; + + /* 1: Ensure that the tuple has the correct tag: in4_sockaddr */ + if (COMPARE(atom_in4_sockaddr, eIn4SockAddr[0]) != 0) + return FALSE; + + /* 2: Get the port number */ + if (!GET_INT(env, eIn4SockAddr[1], &port)) + return FALSE; + + /* 3: Get the address. + * It can either be the atoms: any | loopback, + * or the IPv4 address tuple (size 4). + */ + if (IS_ATOM(env, eIn4SockAddr[2])) { + return decode_in4_sockaddr_atomaddr(env, eIn4SockAddr[2], port, + sockAddrP, addrLenP); + } else if (IS_TUPLE(env, eIn4SockAddr[2])) { + return decode_in4_sockaddr_addr(env, eIn4SockAddr[2], port, + sockAddrP, addrLenP); + } else { + return FALSE; + } +} + + + +static +BOOLEAN_T decode_in4_sockaddr_atomaddr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + SocketAddress* sockAddrP, + unsigned int* addrLenP) +{ + struct in_addr addr; + + if (COMPARE(atom_loopback, eAddr) == 0) { + addr.s_addr = sock_htonl(INADDR_LOOPBACK); + } else if (COMPARE(atom_any, eAddr) == 0) { + addr.s_addr = sock_htonl(INADDR_ANY); + } else { + return FALSE; + } + + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); +#ifndef NO_SA_LEN + sockAddrP->sai.sin_len = sizeof(struct sockaddr_in6); +#endif + sockAddrP->sai.sin_family = AF_INET; + sockAddrP->sai.sin_port = sock_htons(port); + sockAddrP->sai.sin_addr.s_addr = addr.s_addr; + *addrLenP = sizeof(struct sockaddr_in); + + return TRUE; +} + + +/* Decode an in4_sockaddr where the address field is a tuple. + * Its *supposed* to be an ip4_address (tuple). + */ +static +BOOLEAN_T decode_in4_sockaddr_addr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + SocketAddress* sockAddrP, + unsigned int* addrLenP) +{ + const ERL_NIF_TERM* ip4AddrT; + int ip4AddrTSz; + int a, v; + char addr[4]; + + /* This shall be a 4 tuple */ + if (!GET_TUPLE(env, eAddr, &ip4AddrTSz, &ip4AddrT)) + return FALSE; + + if (ip4AddrTSz != 4) + return FALSE; + + sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in)); +#ifndef NO_SA_LEN + sockAddrP->sai.sin_len = sizeof(struct sockaddr_in); +#endif + sockAddrP->sai.sin_family = AF_INET; + sockAddrP->sai.sin_port = sock_htons(port); + for (a = 0; a < 4; a++) { + if (!GET_INT(env, ip4AddrT[a], &v)) + return FALSE; + addr[a] = v; + } + sys_memcpy(&sockAddrP->sai.sin_addr, &addr, sizeof(addr)); + *addrLenP = sizeof(struct sockaddr_in); + + return TRUE; +} + + + +/* Decode an in6_sockaddr(). + * The first element should be the atom in4_sockaddr + * The second, the port number integer . + * The third, the ip4_address tuple. + * The forth, the flowinfo integer. + * The fifth and final, the scope_id integer. + */ +#if defined(HAVE_IN6) && defined(AF_INET6) +static +BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* eIn6SockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLenP) +{ + int port; + unsigned int flowInfo, scopeId; + + /* 1: Ensure that the tuple has the correct tag: in6_sockaddr */ + if (COMPARE(atom_in6_sockaddr, eIn6SockAddr[0]) != 0) + return FALSE; + + /* 2: Get the port number */ + if (!GET_INT(env, eIn6SockAddr[1], &port)) + return FALSE; + + /* 4: Get the flowinfo */ + if (!GET_UINT(env, eIn6SockAddr[3], &flowInfo)) + return FALSE; + + /* 5: Get the scope_id */ + if (!GET_UINT(env, eIn6SockAddr[4], &scopeId)) + return FALSE; + + /* 3: Get the address. + * It can either be the atoms: any | loopback, + * or the IPv6 address tuple (size 8). + */ + if (IS_ATOM(env, eIn6SockAddr[2])) { + return decode_in6_sockaddr_atomaddr(env, eIn6SockAddr[2], port, + flowInfo, scopeId, + sockAddrP, addrLenP); + } else if (IS_TUPLE(env, eIn6SockAddr[2])) { + return decode_in6_sockaddr_addr(env, eIn6SockAddr[2], port, + flowInfo, scopeId, + sockAddrP, addrLenP); + } else { + return FALSE; + } +} +#endif + + +#if defined(HAVE_IN6) && defined(AF_INET6) +static +BOOLEAN_T decode_in6_sockaddr_atomaddr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + unsigned int flowInfo, + unsigned int scopeId, + SocketAddress* sockAddrP, + unsigned int* addrLenP) +{ + const struct in6_addr* addr; + + if (COMPARE(atom_loopback, eAddr) == 0) { + addr = &in6addr_loopback; + } else if (COMPARE(atom_any, eAddr) == 0) { + addr = &in6addr_any; + } else { + return FALSE; + } + + sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + sockAddrP->sai6.sin6_len = sizeof(struct sockaddr_in6); +#endif + sockAddrP->sai6.sin6_family = AF_INET6; + sockAddrP->sai6.sin6_port = sock_htons(port); + sockAddrP->sai6.sin6_flowinfo = flowInfo; + sockAddrP->sai6.sin6_scope_id = scopeId; + sockAddrP->sai6.sin6_addr = *addr; + *addrLenP = sizeof(struct sockaddr_in6); + + return TRUE; +} +#endif + + + +#if defined(HAVE_IN6) && defined(AF_INET6) +/* Decode an in6_sockaddr where the address field is a tuple */ +static +BOOLEAN_T decode_in6_sockaddr_addr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + unsigned int flowInfo, + unsigned int scopeId, + SocketAddress* sockAddrP, + unsigned int* addrLenP) +{ + const ERL_NIF_TERM* ip6AddrT; + int ip6AddrTSz; + int a, v; + char addr[16]; + + /* This shall be a 8 tuple */ + if (!GET_TUPLE(env, eAddr, &ip6AddrTSz, &ip6AddrT)) + return FALSE; + + if (ip6AddrTSz != 8) + return FALSE; + + sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + sockAddrP->sai6.sin6_len = sizeof(struct sockaddr_in6); +#endif + sockAddrP->sai6.sin6_family = AF_INET6; + sockAddrP->sai6.sin6_port = sock_htons(port); + sockAddrP->sai6.sin6_flowinfo = flowInfo; + sockAddrP->sai6.sin6_scope_id = scopeId; + /* The address tuple is of size 8 + * and each element is a two byte integer + */ + for (a = 0; a < 8; a++) { + if (!GET_INT(env, ip6AddrT[a], &v)) + return FALSE; + addr[a*2 ] = ((v >> 8) & 0xFF); + addr[a*2+1] = (v & 0xFF); + } + sys_memcpy(&sockAddrP->sai6.sin6_addr, &addr, sizeof(addr)); + *addrLenP = sizeof(struct sockaddr_in6); + + return TRUE; +} +#endif /* Decode the 4- or 8-element address tuple @@ -6651,13 +6979,14 @@ ERL_NIF_TERM make_error2(ErlNifEnv* env, int err) * This message is for processes that are waiting in the * erlang API functions for a select message. */ +/* static char* send_msg_error_closed(ErlNifEnv* env, ErlNifPid* pid) { return send_msg_error(env, atom_closed, pid); } - +*/ /* Send an error message to the specified process: * A message in the form: @@ -6667,6 +6996,7 @@ char* send_msg_error_closed(ErlNifEnv* env, * This message is for processes that are waiting in the * erlang API functions for a select message. */ +/* static char* send_msg_error(ErlNifEnv* env, ERL_NIF_TERM reason, @@ -6676,6 +7006,7 @@ char* send_msg_error(ErlNifEnv* env, return send_msg(env, msg, pid); } +*/ /* Send an (nif-) abort message to the specified process: @@ -6985,18 +7316,18 @@ ErlNifFunc socket_funcs[] = // The proper "socket" interface {"nif_open", 4, nif_open, 0}, - {"nif_bind", 3, nif_bind, 0}, - {"nif_connect", 3, nif_connect, 0}, + {"nif_bind", 2, nif_bind, 0}, + {"nif_connect", 2, nif_connect, 0}, {"nif_listen", 2, nif_listen, 0}, {"nif_accept", 2, nif_accept, 0}, {"nif_send", 4, nif_send, 0}, - {"nif_sendto", 6, nif_sendto, 0}, + {"nif_sendto", 5, nif_sendto, 0}, {"nif_recv", 4, nif_recv, 0}, - {"nif_recvfrom", 2, nif_recvfrom, 0}, + {"nif_recvfrom", 4, nif_recvfrom, 0}, {"nif_close", 1, nif_close, 0}, {"nif_shutdown", 2, nif_shutdown, 0}, - {"nif_setopt", 3, nif_setopt, 0}, - {"nif_getopt", 2, nif_getopt, 0}, + {"nif_setopt", 5, nif_setopt, 0}, + {"nif_getopt", 4, nif_getopt, 0}, /* Misc utility functions */ {"nif_link_if2idx", 1, nif_link_if2idx, 0}, @@ -7008,7 +7339,7 @@ ErlNifFunc socket_funcs[] = * is called after the connect *select* has "completed". */ {"nif_finalize_connection", 1, nif_finalize_connection, 0}, - {"nif_cancel", 2, nif_cancel, 0}, + // {"nif_cancel", 2, nif_cancel, 0}, {"nif_finalize_close", 1, nif_finalize_close, ERL_NIF_DIRTY_JOB_IO_BOUND} }; @@ -7125,6 +7456,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) socketData.numProtoSCTP = 0; /* +++ Misc atoms +++ */ + atom_any = MKA(env, str_any); // atom_active = MKA(env, str_active); // atom_active_n = MKA(env, str_active_n); // atom_active_once = MKA(env, str_active_once); @@ -7135,6 +7467,9 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_closing = MKA(env, str_closing); atom_error = MKA(env, str_error); atom_false = MKA(env, str_false); + atom_in4_sockaddr = MKA(env, str_in4_sockaddr); + atom_in6_sockaddr = MKA(env, str_in6_sockaddr); + atom_loopback = MKA(env, str_loopback); // atom_list = MKA(env, str_list); // atom_mode = MKA(env, str_mode); atom_nif_abort = MKA(env, str_nif_abort); diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl index a67a019b80..2942f26505 100644 --- a/erts/preloaded/src/socket.erl +++ b/erts/preloaded/src/socket.erl @@ -30,13 +30,13 @@ -export([ open/2, open/3, open/4, - bind/2, bind/3, - connect/3, + bind/2, + connect/2, connect/3, listen/1, listen/2, accept/1, accept/2, send/2, send/3, send/4, - sendto/5, + sendto/4, sendto/5, %% sendmsg/4, %% writev/4, OR SENDV? It will be strange for recv then: recvv (instead of readv) @@ -125,6 +125,8 @@ %% If we do we may need to include the family (domain) in the %% map (as the native type do. See struct sockaddr_in6). %% +-type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). + -record(in4_sockaddr, {port = 0 :: port_number(), addr = any :: any | loopback | ip4_address()}). -type in4_sockaddr() :: #in4_sockaddr{}. @@ -134,8 +136,6 @@ scope_id = 0 :: in6_scope_id()}). -type in6_sockaddr() :: #in6_sockaddr{}. --type in_sockaddr() :: in4_sockaddr() | in6_sockaddr(). - -type port_number() :: 0..65535. %% otp - The option is internal to our (OTP) imeplementation. @@ -369,7 +369,7 @@ %% Optional address %% On an unconnected socket this is used to specify the target %% address for a datagram. - %% For a connected socket, this field should be specified []. + %% For a connected socket, this field should be specifiedset to []. name :: list(), %% Scatter/gather array @@ -495,7 +495,8 @@ on_load(Path, Extra) when is_list(Path) andalso is_map(Extra) -> on_load(true, _Path, _Extra) -> ok; on_load(false, Path, Extra) -> - ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)). + %% ok = erlang:load_nif(Path, maps:put(timestamp, formated_timestamp(), Extra)). + ok = erlang:load_nif(Path, Extra). @@ -582,7 +583,7 @@ default_protocol(Protocol, _) -> Protocol. -spec bind(Socket, FileOrAddr) -> ok | {error, Reason} when Socket :: socket(), - FileOrAddr :: binary() | string() | ip_address() | any | loopback, + FileOrAddr :: binary() | string() | in_sockaddr() | any | loopback, Reason :: term(). bind(Socket, File) when is_binary(File) -> @@ -602,26 +603,11 @@ bind(Socket, File) when is_list(File) andalso (File =/= []) -> true -> {error, einval} end; -bind(Socket, Addr) when is_tuple(Addr) orelse - (Addr =:= any) orelse - (Addr =:= loopback) -> - bind(Socket, Addr, 0). - - --spec bind(Socket, Address, Port) -> ok | {ok, NewPort} | {error, Reason} when - Socket :: socket(), - Address :: ip_address() | any | loopback, - Port :: port_number(), - NewPort :: port_number(), - Reason :: term(). - -%% Shall we keep info about domain so that we can verify address? -bind(#socket{ref = SockRef}, Addr, Port) - when (is_tuple(Addr) andalso - ((size(Addr) =:= 4) orelse (size(Addr) =:= 8))) orelse - ((Addr =:= any) orelse (Addr =:= loopback)) andalso - (is_integer(Port) andalso (Port >= 0)) -> - nif_bind(SockRef, {Addr, Port}). +bind(#socket{ref = SockRef} = _Socket, SockAddr) + when is_record(SockAddr, in4_sockaddr) orelse + is_record(SockAddr, in6_sockaddr) orelse + (SockAddr =:= any) orelse (SockAddr =:= loopback) -> + nif_bind(SockRef, SockAddr). @@ -630,32 +616,29 @@ bind(#socket{ref = SockRef}, Addr, Port) %% connect - initiate a connection on a socket %% --spec connect(Socket, Addr, Port) -> ok | {error, Reason} when - Socket :: socket(), - Addr :: ip_address(), - Port :: port_number(), - Reason :: term(). +-spec connect(Socket, SockAddr) -> ok | {error, Reason} when + Socket :: socket(), + SockAddr :: in_sockaddr(), + Reason :: term(). -connect(Socket, Addr, Port) -> - connect(Socket, Addr, Port, infinity). +connect(Socket, SockAddr) -> + connect(Socket, SockAddr, infinity). --spec connect(Socket, Addr, Port, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - Addr :: ip_address(), - Port :: port_number(), - Timeout :: timeout(), - Reason :: term(). +-spec connect(Socket, SockAddr, Timeout) -> ok | {error, Reason} when + Socket :: socket(), + SockAddr :: in_sockaddr(), + Timeout :: timeout(), + Reason :: term(). -connect(_Socket, _Addr, _Port, Timeout) +connect(_Socket, _SockAddr, Timeout) when (is_integer(Timeout) andalso (Timeout =< 0)) -> {error, timeout}; -connect(#socket{ref = SockRef}, Addr, Port, Timeout) - when (is_tuple(Addr) andalso - ((size(Addr) =:= 4) orelse (size(Addr) =:= 8))) andalso - (is_integer(Port) andalso (Port >= 0)) andalso +connect(#socket{ref = SockRef}, SockAddr, Timeout) + when (is_record(SockAddr, in4_sockaddr) orelse + is_record(SockAddr, in6_sockaddr)) andalso ((Timeout =:= infinity) orelse is_integer(Timeout)) -> TS = timestamp(Timeout), - case nif_connect(SockRef, Addr, Port) of + case nif_connect(SockRef, SockAddr) of ok -> %% Connected! ok; @@ -680,14 +663,18 @@ connect(#socket{ref = SockRef}, Addr, Port, Timeout) %% listen - listen for connections on a socket %% --spec listen(Socket, Backlog) -> ok | {error, Reason} when +-spec listen(Socket) -> ok | {error, Reason} when Socket :: socket(), - Backlog :: pos_integer(), Reason :: term(). listen(Socket) -> listen(Socket, ?SOCKET_LISTEN_BACKLOG_DEFAULT). +-spec listen(Socket, Backlog) -> ok | {error, Reason} when + Socket :: socket(), + Backlog :: pos_integer(), + Reason :: term(). + listen(#socket{ref = SockRef}, Backlog) when (is_integer(Backlog) andalso (Backlog >= 0)) -> nif_listen(SockRef, Backlog). @@ -700,15 +687,20 @@ listen(#socket{ref = SockRef}, Backlog) %% accept, accept4 - accept a connection on a socket %% --spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when +-spec accept(LSocket) -> {ok, Socket} | {error, Reason} when LSocket :: socket(), - Timeout :: timeout(), Socket :: socket(), Reason :: term(). accept(Socket) -> accept(Socket, ?SOCKET_ACCEPT_TIMEOUT_DEFAULT). +-spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when + LSocket :: socket(), + Timeout :: timeout(), + Socket :: socket(), + Reason :: term(). + %% Do we really need this optimization? accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) -> {error, timeout}; @@ -824,35 +816,35 @@ do_send(SockRef, Data, EFlags, Timeout) -> %% --------------------------------------------------------------------------- %% -sendto(Socket, Data, Flags, DestAddr, DestPort) -> - sendto(Socket, Data, Flags, DestAddr, DestPort, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). +sendto(Socket, Data, Flags, DestSockAddr) -> + sendto(Socket, Data, Flags, DestSockAddr, ?SOCKET_SENDTO_TIMEOUT_DEFAULT). --spec sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) -> +-spec sendto(Socket, Data, Flags, DestSockAddr, Timeout) -> ok | {error, Reason} when - Socket :: socket(), - Data :: binary(), - Flags :: send_flags(), - DestAddr :: null | ip_address(), - DestPort :: port_number(), - Timeout :: timeout(), - Reason :: term(). - -sendto(Socket, Data, Flags, DestAddr, DestPort, Timeout) when is_list(Data) -> + Socket :: socket(), + Data :: binary(), + Flags :: send_flags(), + DestSockAddr :: null | in_sockaddr(), + Timeout :: timeout(), + Reason :: term(). + +sendto(Socket, Data, Flags, DestSockAddr, Timeout) when is_list(Data) -> Bin = erlang:list_to_binary(Data), - sendto(Socket, Bin, Flags, DestAddr, DestPort, Timeout); -sendto(#socket{ref = SockRef}, Data, Flags, DestAddr, DestPort, Timeout) + sendto(Socket, Bin, Flags, DestSockAddr, Timeout); +sendto(#socket{ref = SockRef}, Data, Flags, DestSockAddr, Timeout) when is_binary(Data) andalso is_list(Flags) andalso - (is_tuple(DestAddr) orelse (DestAddr =:= null)) andalso - is_integer(DestPort) andalso + (is_record(DestSockAddr, in4_sockaddr) orelse + is_record(DestSockAddr, in6_sockaddr) orelse + (DestSockAddr =:= null)) andalso (is_integer(Timeout) orelse (Timeout =:= infinity)) -> EFlags = enc_send_flags(Flags), - do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout). + do_sendto(SockRef, Data, EFlags, DestSockAddr, Timeout). -do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout) -> +do_sendto(SockRef, Data, EFlags, DestSockAddr, Timeout) -> TS = timestamp(Timeout), SendRef = make_ref(), - case nif_sendto(SockRef, SendRef, Data, EFlags, DestAddr, DestPort) of + case nif_sendto(SockRef, SendRef, Data, EFlags, DestSockAddr) of ok -> %% We are done ok; @@ -862,10 +854,10 @@ do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout) -> receive {select, SockRef, SendRef, ready_output} when (Written > 0) -> <<_:Written/binary, Rest/binary>> = Data, - do_sendto(SockRef, Rest, EFlags, DestAddr, DestPort, + do_sendto(SockRef, Rest, EFlags, DestSockAddr, next_timeout(TS, Timeout)); {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, + do_sendto(SockRef, Data, EFlags, DestSockAddr, next_timeout(TS, Timeout)); {nif_abort, SendRef, Reason} -> @@ -880,7 +872,7 @@ do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, Timeout) -> {error, eagain} -> receive {select, SockRef, SendRef, ready_output} -> - do_sendto(SockRef, Data, EFlags, DestAddr, DestPort, + do_sendto(SockRef, Data, EFlags, DestSockAddr, next_timeout(TS, Timeout)) after Timeout -> nif_cancel(SockRef, sendto, SendRef), @@ -1158,13 +1150,12 @@ recvfrom(Socket, BufSz, Flags) when is_list(Flags) -> recvfrom(Socket, BufSz, Timeout) -> recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout). --spec recvfrom(Socket, BufSz, Flags, Timeout) -> {ok, {SrcDomain, Source, Data}} | {error, Reason} when +-spec recvfrom(Socket, BufSz, Flags, Timeout) -> {ok, {Source, Data}} | {error, Reason} when Socket :: socket(), BufSz :: non_neg_integer(), Flags :: recv_flags(), Timeout :: timeout(), - SrcDomain :: domain() | undefined, - Source :: {ip_address(), port_number()} | string() | undefined, + Source :: in_sockaddr() | string() | undefined, Data :: binary(), Reason :: term(). @@ -1179,7 +1170,7 @@ do_recvfrom(SockRef, BufSz, EFlags, Timeout) -> TS = timestamp(Timeout), RecvRef = make_ref(), case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of - {ok, {_Domain, _Source, _NewData}} = OK -> + {ok, {_Source, _NewData}} = OK -> OK; {error, eagain} -> @@ -1473,7 +1464,6 @@ link_if2idx(If) when is_list(If) -> nif_link_if2idx(If). - %% =========================================================================== %% %% link_idx2if - Mappings between network interface names and indexes: idx -> if @@ -2138,10 +2128,10 @@ nif_info() -> nif_open(_Domain, _Type, _Protocol, _Extra) -> erlang:error(badarg). -nif_bind(_SRef, _LocalAddr) -> +nif_bind(_SRef, _SockAddr) -> erlang:error(badarg). -nif_connect(_SRef, _Addr, _Port) -> +nif_connect(_SRef, _SockAddr) -> erlang:error(badarg). nif_finalize_connection(_SRef) -> @@ -2156,7 +2146,7 @@ nif_accept(_SRef, _Ref) -> nif_send(_SockRef, _SendRef, _Data, _Flags) -> erlang:error(badarg). -nif_sendto(_SRef, _SendRef, _Data, _Flags, _Dest, _Port) -> +nif_sendto(_SRef, _SendRef, _Data, _Flags, _DestSockAddr) -> erlang:error(badarg). nif_recv(_SRef, _RecvRef, _Length, _Flags) -> -- cgit v1.2.3