diff options
Diffstat (limited to 'erts/emulator/nifs/common/socket_nif.c')
-rw-r--r-- | erts/emulator/nifs/common/socket_nif.c | 323 |
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); |