aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
authorMicael Karlberg <[email protected]>2018-07-04 15:18:18 +0200
committerMicael Karlberg <[email protected]>2018-09-18 14:50:18 +0200
commite39e25d84405e13ca0ce476e3ba473510e5548de (patch)
tree217ac95a480968372496b2b3b5d978826860f076 /erts/emulator
parentdce68cf27f2dd1721bd316594a29ff99a0de7bb9 (diff)
downloadotp-e39e25d84405e13ca0ce476e3ba473510e5548de.tar.gz
otp-e39e25d84405e13ca0ce476e3ba473510e5548de.tar.bz2
otp-e39e25d84405e13ca0ce476e3ba473510e5548de.zip
[socket-nif] Fixed (dgram) recv
Fixed handling of recvfrom (used by dgram sockets). Had forgot to do select(read) when we got block from the call to recvfrom. Argh! Also updated the (simple) test server and client to to be able to use udp (dgram+udp). OTP-14831
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/nifs/common/socket_nif.c323
1 files changed, 307 insertions, 16 deletions
diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c
index 027155fc92..4efca1c72d 100644
--- a/erts/emulator/nifs/common/socket_nif.c
+++ b/erts/emulator/nifs/common/socket_nif.c
@@ -354,6 +354,7 @@ typedef union {
#define SOCKET_OPT_SOCK_RCVBUF 17
#define SOCKET_OPT_SOCK_REUSEADDR 21
#define SOCKET_OPT_SOCK_SNDBUF 27
+#define SOCKET_OPT_SOCK_TYPE 32
#define SOCKET_OPT_IP_RECVTOS 25
#define SOCKET_OPT_IP_ROUTER_ALERT 28
@@ -410,6 +411,7 @@ typedef union {
#define sock_ntohs(x) ntohs((x))
#define sock_open(domain, type, proto) \
make_noninheritable_handle(socket((domain), (type), (proto)))
+#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
#define sock_recvfrom(s,buf,blen,flag,addr,alen) \
recvfrom((s),(buf),(blen),(flag),(addr),(alen))
@@ -448,6 +450,7 @@ static unsigned long one_value = 1;
#define sock_name(s, addr, len) getsockname((s), (addr), (len))
#define sock_ntohs(x) ntohs((x))
#define sock_open(domain, type, proto) socket((domain), (type), (proto))
+#define sock_peer(s, addr, len) getpeername((s), (addr), (len))
#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag))
#define sock_recvfrom(s,buf,blen,flag,addr,alen) \
recvfrom((s),(buf),(blen),(flag),(addr),(alen))
@@ -671,6 +674,12 @@ static ERL_NIF_TERM nif_setopt(ErlNifEnv* env,
static ERL_NIF_TERM nif_getopt(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM nif_sockname(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[]);
+static ERL_NIF_TERM nif_peername(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[]);
static ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env,
int argc,
const ERL_NIF_TERM argv[]);
@@ -950,6 +959,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env,
static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env,
SocketDescriptor* descP);
#endif
+#if defined(SO_TYPE)
+static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env,
+ SocketDescriptor* descP);
+#endif
static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env,
SocketDescriptor* descP,
int eOpt);
@@ -1013,6 +1026,10 @@ static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env,
SocketDescriptor* descP);
#endif
#endif // defined(HAVE_SCTP)
+static ERL_NIF_TERM nsockname(ErlNifEnv* env,
+ SocketDescriptor* descP);
+static ERL_NIF_TERM npeername(ErlNifEnv* env,
+ SocketDescriptor* descP);
static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env,
SocketDescriptor* descP,
@@ -1061,6 +1078,7 @@ static ERL_NIF_TERM recv_check_result(ErlNifEnv* env,
static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
SocketDescriptor* descP,
int read,
+ int saveErrno,
ErlNifBinary* bufP,
SocketAddress* fromAddrP,
unsigned int fromAddrLen,
@@ -1369,6 +1387,7 @@ ERL_NIF_TERM esock_atom_tcp;
ERL_NIF_TERM esock_atom_true;
ERL_NIF_TERM esock_atom_udp;
ERL_NIF_TERM esock_atom_undefined;
+ERL_NIF_TERM esock_atom_unknown;
/* *** "Global" error (=reason) atoms *** */
ERL_NIF_TERM esock_atom_eagain;
@@ -1447,7 +1466,7 @@ static SocketData data;
* nif_listen(Sock, Backlog)
* nif_accept(LSock, Ref)
* nif_send(Sock, SendRef, Data, Flags)
- * nif_sendto(Sock, SendRef, Data, Flags, DstSockAddr)
+ * nif_sendto(Sock, SendRef, Data, Dest, Flags)
* nif_recv(Sock, RecvRef, Length, Flags)
* nif_recvfrom(Sock, Flags)
* nif_close(Sock)
@@ -2629,8 +2648,8 @@ ERL_NIF_TERM nsend(ErlNifEnv* env,
* Socket (ref) - Points to the socket descriptor.
* SendRef - A unique id for this (send) request.
* Data - The data to send in the form of a IOVec.
- * Flags - Send flags.
* Dest - Destination (socket) address.
+ * Flags - Send flags.
*/
static
@@ -2640,24 +2659,37 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
{
SocketDescriptor* descP;
ERL_NIF_TERM sendRef;
- ErlNifBinary data;
+ ErlNifBinary sndData;
unsigned int eflags;
int flags;
ERL_NIF_TERM eSockAddr;
SocketAddress remoteAddr;
unsigned int remoteAddrLen;
char* xres;
+ ERL_NIF_TERM res;
+
+ SGDBG( ("SOCKET", "nif_sendto -> entry with argc: %d\r\n", argc) );
/* Extract arguments and perform preliminary validation */
- if ((argc != 6) ||
+ if ((argc != 5) ||
!enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
- !GET_BIN(env, argv[2], &data) ||
- !GET_UINT(env, argv[3], &eflags)) {
+ !GET_BIN(env, argv[2], &sndData) ||
+ !GET_UINT(env, argv[4], &eflags)) {
return enif_make_badarg(env);
}
sendRef = argv[1];
- eSockAddr = argv[4];
+ eSockAddr = argv[3];
+
+ SSDBG( descP,
+ ("SOCKET", "nif_sendto -> args when sock = %d:"
+ "\r\n Socket: %T"
+ "\r\n sendRef: %T"
+ "\r\n size of data: %d"
+ "\r\n eSockAddr: %T"
+ "\r\n eflags: %d"
+ "\r\n",
+ descP->sock, argv[0], sendRef, sndData.size, eSockAddr, eflags) );
/* THIS TEST IS NOT CORRECT!!! */
if (!IS_OPEN(descP))
@@ -2671,7 +2703,14 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env,
&remoteAddrLen)) != NULL)
return esock_make_error_str(env, xres);
- return nsendto(env, descP, sendRef, &data, flags, &remoteAddr, remoteAddrLen);
+ res = nsendto(env, descP, sendRef, &sndData, flags,
+ &remoteAddr, remoteAddrLen);
+
+ SGDBG( ("SOCKET", "nif_sendto -> done with result: "
+ "\r\n %T"
+ "\r\n", res) );
+
+ return res;
}
@@ -2919,6 +2958,14 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env,
* RecvRef - A unique id for this (send) request.
* BufSz - Size of the buffer into which we put the received message.
* Flags - Receive flags.
+ *
+ * <KOLLA>
+ *
+ * How do we handle if the peek flag is set? We need to basically keep
+ * track of if we expect any data from the read. Regardless of the
+ * number of bytes we try to read.
+ *
+ * </KOLLA>
*/
static
@@ -2933,6 +2980,10 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
int flags;
ERL_NIF_TERM res;
+ SGDBG( ("SOCKET", "nif_recvfrom -> entry with argc: %d\r\n", argc) );
+
+ /* Extract arguments and perform preliminary validation */
+
if ((argc != 4) ||
!enif_get_resource(env, argv[0], sockets, (void**) &descP) ||
!GET_UINT(env, argv[2], &bufSz) ||
@@ -2941,6 +2992,14 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env,
}
recvRef = argv[1];
+ SSDBG( descP,
+ ("SOCKET", "nif_recvfrom -> args when sock = %d:"
+ "\r\n Socket: %T"
+ "\r\n recvRef: %T"
+ "\r\n bufSz: %d"
+ "\r\n eflags: %d"
+ "\r\n", descP->sock, argv[0], recvRef, bufSz, eflags) );
+
/* if (IS_OPEN(descP)) */
/* return esock_make_error(env, atom_enotconn); */
@@ -2982,13 +3041,20 @@ static
ERL_NIF_TERM nrecvfrom(ErlNifEnv* env,
SocketDescriptor* descP,
ERL_NIF_TERM recvRef,
- uint16_t bufSz,
+ uint16_t len,
int flags)
{
SocketAddress fromAddr;
unsigned int addrLen;
ssize_t read;
+ int save_errno;
ErlNifBinary buf;
+ int bufSz = (len ? len : descP->rBufSz);
+
+ SSDBG( descP, ("SOCKET", "nrecvfrom -> entry with"
+ "\r\n len: %d (%d)"
+ "\r\n flags: %d"
+ "\r\n", len, bufSz, flags) );
if (!descP->isReadable)
return enif_make_badarg(env);
@@ -2997,7 +3063,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env,
* Either as much as we want to read or (if zero (0)) use the "default"
* size (what has been configured).
*/
- if (!ALLOC_BIN((bufSz ? bufSz : descP->rBufSz), &buf))
+ if (!ALLOC_BIN(bufSz, &buf))
return esock_make_error(env, atom_exalloc);
/* We ignore the wrap for the moment.
@@ -3010,9 +3076,14 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env,
read = sock_recvfrom(descP->sock, buf.data, buf.size, flags,
&fromAddr.sa, &addrLen);
+ if (IS_SOCKET_ERROR(read))
+ save_errno = sock_errno();
+ else
+ save_errno = -1; // The value does not actually matter in this case
return recvfrom_check_result(env, descP,
read,
+ save_errno,
&buf,
&fromAddr, addrLen,
recvRef);
@@ -4683,6 +4754,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env,
break;
#endif
+#if defined(SO_TYPE)
+ case SOCKET_OPT_SOCK_TYPE:
+ result = ngetopt_lvl_sock_type(env, descP);
+ break;
+#endif
+
default:
result = esock_make_error(env, esock_atom_einval);
break;
@@ -4792,6 +4869,51 @@ ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env,
#endif
+#if defined(SO_TYPE)
+static
+ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env,
+ SocketDescriptor* descP)
+{
+ ERL_NIF_TERM result;
+ int val;
+ SOCKOPTLEN_T valSz = sizeof(val);
+ int res;
+
+ res = sock_getopt(descP->sock, SOL_SOCKET, SO_TYPE, &val, &valSz);
+
+ if (res != 0) {
+ result = esock_make_error_errno(env, res);
+ } else {
+ switch (val) {
+ case SOCK_STREAM:
+ result = esock_make_ok2(env, esock_atom_stream);
+ break;
+ case SOCK_DGRAM:
+ result = esock_make_ok2(env, esock_atom_dgram);
+ break;
+#ifdef HAVE_SCTP
+ case SOCK_SEQPACKET:
+ result = esock_make_ok2(env, esock_atom_seqpacket);
+ break;
+#endif
+ case SOCK_RAW:
+ result = esock_make_ok2(env, esock_atom_raw);
+ break;
+ case SOCK_RDM:
+ result = esock_make_ok2(env, esock_atom_rdm);
+ break;
+ default:
+ result = esock_make_error(env,
+ MKT2(env, esock_atom_unknown, MKI(env, val)));
+ break;
+ }
+ }
+
+ return result;
+}
+#endif
+
+
/* ngetopt_lvl_ip - Level *IP* option(s)
*/
static
@@ -5217,6 +5339,137 @@ ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env,
+
+/* ----------------------------------------------------------------------
+ * nif_sockname - get socket name
+ *
+ * Description:
+ * Returns the current address to which the socket is bound.
+ *
+ * Arguments:
+ * Socket (ref) - Points to the socket descriptor.
+ */
+
+static
+ERL_NIF_TERM nif_sockname(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[])
+{
+ SocketDescriptor* descP;
+ ERL_NIF_TERM res;
+
+ SGDBG( ("SOCKET", "nif_sockname -> entry with argc: %d\r\n", argc) );
+
+ /* Extract arguments and perform preliminary validation */
+
+ if ((argc != 1) ||
+ !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ return enif_make_badarg(env);
+ }
+
+ SSDBG( descP,
+ ("SOCKET", "nif_sockname -> args when sock = %d:"
+ "\r\n Socket: %T"
+ "\r\n", descP->sock, argv[0]) );
+
+ res = nsockname(env, descP);
+
+ SSDBG( descP, ("SOCKET", "nif_sockname -> done with res = %T\r\n", res) );
+
+ return res;
+}
+
+
+
+static
+ERL_NIF_TERM nsockname(ErlNifEnv* env,
+ SocketDescriptor* descP)
+{
+ SocketAddress sa;
+ SocketAddress* saP = &sa;
+ unsigned int sz = sizeof(SocketAddress);
+
+ sys_memzero((char*) saP, sz);
+ if (IS_SOCKET_ERROR(sock_name(descP->sock, (struct sockaddr*) saP, &sz))) {
+ return esock_make_error_errno(env, sock_errno());
+ } else {
+ ERL_NIF_TERM esa;
+ char* xres;
+
+ if ((xres = esock_encode_sockaddr(env, saP, sz, &esa)) != NULL)
+ return esock_make_error_str(env, xres);
+ else
+ return esock_make_ok2(env, esa);
+ }
+}
+
+
+
+/* ----------------------------------------------------------------------
+ * nif_peername - get name of the connected peer socket
+ *
+ * Description:
+ * Returns the address of the peer connected to the socket.
+ *
+ * Arguments:
+ * Socket (ref) - Points to the socket descriptor.
+ */
+
+static
+ERL_NIF_TERM nif_peername(ErlNifEnv* env,
+ int argc,
+ const ERL_NIF_TERM argv[])
+{
+ SocketDescriptor* descP;
+ ERL_NIF_TERM res;
+
+ SGDBG( ("SOCKET", "nif_peername -> entry with argc: %d\r\n", argc) );
+
+ /* Extract arguments and perform preliminary validation */
+
+ if ((argc != 1) ||
+ !enif_get_resource(env, argv[0], sockets, (void**) &descP)) {
+ return enif_make_badarg(env);
+ }
+
+ SSDBG( descP,
+ ("SOCKET", "nif_peername -> args when sock = %d:"
+ "\r\n Socket: %T"
+ "\r\n", descP->sock, argv[0]) );
+
+ res = npeername(env, descP);
+
+ SSDBG( descP, ("SOCKET", "nif_peername -> done with res = %T\r\n", res) );
+
+ return res;
+}
+
+
+
+static
+ERL_NIF_TERM npeername(ErlNifEnv* env,
+ SocketDescriptor* descP)
+{
+ SocketAddress sa;
+ SocketAddress* saP = &sa;
+ unsigned int sz = sizeof(SocketAddress);
+
+ sys_memzero((char*) saP, sz);
+ if (IS_SOCKET_ERROR(sock_peer(descP->sock, (struct sockaddr*) saP, &sz))) {
+ return esock_make_error_errno(env, sock_errno());
+ } else {
+ ERL_NIF_TERM esa;
+ char* xres;
+
+ if ((xres = esock_encode_sockaddr(env, saP, sz, &esa)) != NULL)
+ return esock_make_error_str(env, xres);
+ else
+ return esock_make_ok2(env, esa);
+ }
+}
+
+
+
/* ----------------------------------------------------------------------
* U t i l i t y F u n c t i o n s
* ----------------------------------------------------------------------
@@ -5495,6 +5748,7 @@ static
ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
SocketDescriptor* descP,
int read,
+ int saveErrno,
ErlNifBinary* bufP,
SocketAddress* fromAddrP,
unsigned int fromAddrLen,
@@ -5502,6 +5756,14 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
{
ERL_NIF_TERM data;
+ SSDBG( descP,
+ ("SOCKET", "recvfrom_check_result -> entry with"
+ "\r\n read: %d"
+ "\r\n saveErrno: %d"
+ "\r\n recvRef: %T"
+ "\r\n", read, saveErrno, recvRef) );
+
+
/* There is a special case: If the provided 'to read' value is
* zero (0). That means that we reads as much as we can, using
* the default read buffer size.
@@ -5511,12 +5773,12 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
/* +++ Error handling +++ */
- int save_errno = sock_errno();
-
- if (save_errno == ECONNRESET) {
+ if (saveErrno == ECONNRESET) {
/* +++ Oups - closed +++ */
+ SSDBG( descP, ("SOCKET", "recvfrom_check_result -> closed\r\n") );
+
/* <KOLLA>
* IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING
* PROCESS, WE NEED TO INFORM IT!!!
@@ -5536,11 +5798,22 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env,
return esock_make_error(env, atom_closed);
- } else if ((save_errno == ERRNO_BLOCK) ||
- (save_errno == EAGAIN)) {
+ } else if ((saveErrno == ERRNO_BLOCK) ||
+ (saveErrno == EAGAIN)) {
+
+ SSDBG( descP, ("SOCKET", "recvfrom_check_result -> eagain\r\n") );
+
+ SELECT(env, descP->sock, (ERL_NIF_SELECT_READ),
+ descP, NULL, recvRef);
+
return esock_make_error(env, esock_atom_eagain);
} else {
- return esock_make_error_errno(env, save_errno);
+
+ SSDBG( descP,
+ ("SOCKET",
+ "recvfrom_check_result -> errno: %d\r\n", saveErrno) );
+
+ return esock_make_error_errno(env, saveErrno);
}
} else {
@@ -6598,8 +6871,10 @@ void dec_socket(int domain, int type, int protocol)
cnt_dec(&data.numTypeStreams, 1);
else if (type == SOCK_DGRAM)
cnt_dec(&data.numTypeDGrams, 1);
+#ifdef HAVE_SCTP
else if (type == SOCK_SEQPACKET)
cnt_dec(&data.numTypeSeqPkgs, 1);
+#endif
if (protocol == IPPROTO_IP)
cnt_dec(&data.numProtoIP, 1);
@@ -6639,8 +6914,10 @@ void inc_socket(int domain, int type, int protocol)
cnt_inc(&data.numTypeStreams, 1);
else if (type == SOCK_DGRAM)
cnt_inc(&data.numTypeDGrams, 1);
+#ifdef HAVE_SCTP
else if (type == SOCK_SEQPACKET)
cnt_inc(&data.numTypeSeqPkgs, 1);
+#endif
if (protocol == IPPROTO_IP)
cnt_inc(&data.numProtoIP, 1);
@@ -6729,9 +7006,11 @@ BOOLEAN_T etype2type(int etype, int* type)
*type = SOCK_RAW;
break;
+#ifdef HAVE_SCTP
case SOCKET_TYPE_SEQPACKET:
*type = SOCK_SEQPACKET;
break;
+#endif
default:
return FALSE;
@@ -7718,6 +7997,15 @@ void socket_down(ErlNifEnv* env,
const ErlNifPid* pid,
const ErlNifMonitor* mon)
{
+ SocketDescriptor* descP = (SocketDescriptor*) obj;
+
+ SSDBG( descP,
+ ("SOCKET", "socket_down -> entry when"
+ "\r\n sock: %d"
+ "\r\n pid: %T"
+ "\r\n mon: %T"
+ "\r\n", descP->sock, *pid, *mon) );
+
}
@@ -7751,6 +8039,8 @@ ErlNifFunc socket_funcs[] =
{"nif_shutdown", 2, nif_shutdown, 0},
{"nif_setopt", 5, nif_setopt, 0},
{"nif_getopt", 4, nif_getopt, 0},
+ {"nif_sockname", 1, nif_sockname, 0},
+ {"nif_peername", 1, nif_peername, 0},
/* Misc utility functions */
@@ -7927,6 +8217,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
esock_atom_true = MKA(env, "true");
esock_atom_udp = MKA(env, "udp");
esock_atom_undefined = MKA(env, "undefined");
+ esock_atom_unknown = MKA(env, "unknown");
/* Global error codes */
esock_atom_eafnosupport = MKA(env, ESOCK_STR_EAFNOSUPPORT);