From 3ca71520bfb664f0ea809ffdf41505936e4d5e90 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 22 Mar 2018 17:57:15 +0100 Subject: [socket-nif] preliminary version of the new socket interface (nififying) --- erts/emulator/nifs/common/socket_nif.c | 1927 ++++++++++++++++++++++++++++++++ 1 file changed, 1927 insertions(+) create mode 100644 erts/emulator/nifs/common/socket_nif.c (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c new file mode 100644 index 0000000000..2c51ff2000 --- /dev/null +++ b/erts/emulator/nifs/common/socket_nif.c @@ -0,0 +1,1927 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. 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. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : The NIF (C) part of the socket interface + * ---------------------------------------------------------------------- + * + */ + +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* If we HAVE_SCTP_H and Solaris, we need to define the following in + * order to get SCTP working: + */ +#if (defined(HAVE_SCTP_H) && defined(__sun) && defined(__SVR4)) +#define SOLARIS10 1 +/* WARNING: This is not quite correct, it may also be Solaris 11! */ +#define _XPG4_2 +#define __EXTENSIONS__ +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_NET_IF_DL_H +#include +#endif + +#ifdef HAVE_IFADDRS_H +#include +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +#include +#endif + +#ifdef HAVE_SYS_UN_H +#include +#endif + +/* SENDFILE STUFF HERE IF WE NEED IT... */ + +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define __DARWIN__ 1 +#endif + + +#ifdef __WIN32__ +#define STRNCASECMP strncasecmp +#define INCL_WINSOCK_API_TYPEDEFS 1 + +#ifndef WINDOWS_H_INCLUDES_WINSOCK2_H +#include +#endif +#include +#include /* NEED VC 6.0 or higher */ + +/* Visual studio 2008+: NTDDI_VERSION needs to be set for iphlpapi.h + * to define the right structures. It needs to be set to WINXP (or LONGHORN) + * for IPV6 to work and it's set lower by default, so we need to change it. + */ +#ifdef HAVE_SDKDDKVER_H +# include +# ifdef NTDDI_VERSION +# undef NTDDI_VERSION +# endif +# define NTDDI_VERSION NTDDI_WINXP +#endif +#include + +#undef WANT_NONBLOCKING +#include "sys.h" + +#else /* !__WIN32__ */ + +#include +#ifdef NETDB_H_NEEDS_IN_H +#include +#endif +#include + +#include +#include + +#ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H +#include +#endif + +#include +#include + +#include +#ifdef HAVE_ARPA_NAMESER_H +#include +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#include + +#ifdef HAVE_SCHED_H +#include +#endif + +#ifdef HAVE_SETNS_H +#include +#endif + +#define HAVE_UDP + +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif +#include "sys.h" + +#endif + +#include + + +/* All platforms fail on malloc errors. */ +#define FATAL_MALLOC + + + +/* *** Boolean *type* stuff... *** */ +typedef unsigned int BOOLEAN_T; +#define TRUE 1 +#define FALSE 0 +#define BOOL2STR(__B__) ((__B__) ? "true" : "false") +#define BOOL2ATOM(__B__) ((__B__) ? atom_true : atom_false) + + +/* Debug stuff... */ +#define SOCKET_NIF_DEBUG_DEFAULT TRUE +#define SOCKET_DEBUG_DEFAULT TRUE + +/* Used in debug printouts */ +#ifdef __WIN32__ +#define LLU "%I64u" +#else +#define LLU "%llu" +#endif +typedef unsigned long long llu_t; + + + +/* Socket stuff */ +#define INVALID_SOCKET -1 +#define INVALID_EVENT -1 +#define SOCKET_ERROR -1 + +#define SOCKET int +#define HANDLE long int + + +/* ============================================================================== + * The IS_SOCKET_ERROR macro below is used for portability reasons. + * While POSIX specifies that errors from socket-related system calls + * should be indicated with a -1 return value, some users have experienced + * non-Windows OS kernels that return negative values other than -1. + * While one can argue that such kernels are technically broken, comparing + * against values less than 0 covers their out-of-spec return values without + * imposing incorrect semantics on systems that manage to correctly return -1 + * for errors, thus increasing Erlang's portability. + */ +#ifdef __WIN32__ +#define IS_SOCKET_ERROR(val) ((val) == SOCKET_ERROR) +#else +#define IS_SOCKET_ERROR(val) ((val) < 0) +#endif + + +/* *** Misc macros and defines *** */ +#define MALLOC(SZ) enif_alloc(SZ) +#define FREE(P) enif_free(P) +#define MKA(E,S) enif_make_atom(E, S) +#define MKT2(E,E1,E2) enif_make_tuple2(E, E1, E2) +#define MCREATE(N) enif_mutex_create(N) + + +/* *** Socket state defs *** */ + +#define SOCKET_FLAG_OPEN 0x0001 +#define SOCKET_FLAG_ACTIVE 0x0004 +#define SOCKET_FLAG_LISTEN 0x0008 +#define SOCKET_FLAG_CON 0x0010 +#define SOCKET_FLAG_ACC 0x0020 +#define SOCKET_FLAG_BUSY 0x0040 +#define SOCKET_FLAG_MULTI_CLIENT 0x0100 /* Multiple clients for one descriptor, * + * i.e. multi-accept */ +#define SOCKET_STATE_CLOSED (0) +#define SOCKET_STATE_OPEN (SOCKET_FLAG_OPEN) +#define SOCKET_STATE_CONNECTED (SOCKET_STATE_OPEN | SOCKET_FLAG_ACTIVE) +#define SOCKET_STATE_LISTENING (SOCKET_STATE_OPEN | SOCKET_FLAG_LISTEN) +#define SOCKET_STATE_CONNECTING (SOCKET_STATE_OPEN | SOCKET_FLAG_CON) +#define SOCKET_STATE_ACCEPTING (SOCKET_STATE_LISTENING | SOCKET_FLAG_ACC) +#define SOCKET_STATE_MULTI_ACCEPTING (SOCKET_STATE_ACCEPTING | SOCKET_FLAG_MULTI_CLIENT) + +#define IS_OPEN(d) \ + (((d)->state & SOCKET_FLAG_OPEN) == SOCKET_FLAG_OPEN) + +#define IS_CONNECTED(d) \ + (((d)->state & SOCKET_STATE_CONNECTED) == SOCKET_STATE_CONNECTED) + +#define IS_CONNECTING(d) \ + (((d)->state & SOCKET_FLAG_CON) == SOCKET_FLAG_CON) + +#define IS_BUSY(d) \ + (((d)->state & SOCKET_FLAG_BUSY) == SOCKET_FLAG_BUSY) + + +/*---------------------------------------------------------------------------- + * Interface constants. + * + * This section must be "identical" to the corresponding socket.hrl + */ + +/* domain */ +#define SOCKET_DOMAIN_LOCAL 1 +#define SOCKET_DOMAIN_INET 2 +#define SOCKET_DOMAIN_INET6 3 + +/* type */ +#define SOCKET_TYPE_STREAM 1 +#define SOCKET_TYPE_DGRAM 2 +#define SOCKET_TYPE_RAW 3 +// #define SOCKET_TYPE_RDM 4 +#define SOCKET_TYPE_SEQPACKET 5 + +/* protocol */ +#define SOCKET_PROTOCOL_IP 1 +#define SOCKET_PROTOCOL_TCP 2 +#define SOCKET_PROTOCOL_UDP 3 +#define SOCKET_PROTOCOL_SCTP 4 + + +/* =================================================================== * + * * + * Various enif macros * + * * + * =================================================================== */ + +#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)) +#define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) + +#define GET_ATOM_LEN(E, TE, LP) \ + enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) +#define GET_ATOM(E, TE, BP, MAX) \ + enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) +#define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) +#define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) +#define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) + + +/* =================================================================== * + * * + * Basic socket operations * + * * + * =================================================================== */ + +#ifdef __WIN32__ + +/* *** Windown macros *** */ + +#define sock_bind(s, addr, len) bind((s), (addr), (len)) +#define sock_name(s, addr, len) getsockname((s), (addr), (len)) +#define sock_open(domain, type, proto) \ + make_noninheritable_handle(socket((domain), (type), (proto))) +#define sock_htons(x) htons((x)) +#define sock_htonl(x) htonl((x)) + +#define sock_errno() WSAGetLastError() +#define sock_create_event(s) WSACreateEvent() + +#define SET_BLOCKING(s) ioctlsocket(s, FIONBIO, &zero_value) +#define SET_NONBLOCKING(s) ioctlsocket(s, FIONBIO, &one_value) +static unsigned long zero_value = 0; +static unsigned long one_value = 1; + +#else /* !__WIN32__ */ + +#define sock_bind(s, addr, len) bind((s), (addr), (len)) +#define sock_name(s, addr, len) getsockname((s), (addr), (len)) +#define sock_open(domain, type, proto) socket((domain), (type), (proto)) +#define sock_htons(x) htons((x)) +#define sock_htonl(x) htonl((x)) + +#define sock_errno() errno +#define sock_create_event(s) (s) /* return file descriptor */ + + +#endif /* !__WIN32__ */ + +#ifdef HAVE_SOCKLEN_T +# define SOCKLEN_T socklen_t +#else +# define SOCKLEN_T size_t +#endif + + +/* The general purpose sockaddr */ +typedef struct { + union { + struct sockaddr sa; + struct sockaddr_in sai; + +#ifdef HAVE_IN6 + struct sockaddr_in6 sai6; +#endif + +#ifdef HAVE_SYS_UN_H + struct sockaddr_un sal; +#endif + + } u; + unsigned int len; +} SocketAddress; + +#define which_address_port(sap) \ + ((((sap)->u.sai.sin_family == AF_INET) || \ + ((sap)->u.sai.sin_family == AF_INET6)) ? \ + ((sap)->u.sai.sin_port) : -1) + + +typedef struct { + // The actual socket + SOCKET sock; + HANDLE event; + + /* "Stuff" about the socket */ + int domain; + int type; + int protocol; + + unsigned int state; + SocketAddress remote; + + + // Controller (owner) process + ErlNifPid ctrlPid; + ErlNifMonitor ctrlMon; + + // Write + ErlNifMutex* writeMtx; + BOOLEAN_T isWritable; + unsigned int writePkgCnt; + unsigned int writeByteCnt; + unsigned int writeTries; + unsigned int writeWaits; + + // Read + ErlNifMutex* readMtx; + BOOLEAN_T isReadable; + ErlNifBinary rbuffer; + unsigned int readCapacity; + unsigned int readPkgCnt; + unsigned int readByteCnt; + unsigned int readTries; + unsigned int readWaits; + + + /* We need to keep track of the "request(s)" we have pending. + * If for instance an accept takes to long, the issuer may + * decide to "cancel" the accept (actually the select). This + * is done by calling the *nif_cancel* function with the request + * ref as argument. + * We also need to keep track of requests so that if a new + * request is issued before the current has completed, we + * reply with e.g. ebusy (or something to that effect). + * Or do we? Can the caller actually do that? + */ + + + /* Misc stuff */ + BOOLEAN_T dbg; +} SocketDescriptor; + + +/* Global stuff (do we really need to "collect" + * these things?) + */ +typedef struct { + /* These are for debugging, testing and the like */ + ERL_NIF_TERM version; + ERL_NIF_TERM buildDate; + BOOLEAN_T dbg; +} SocketData; + + +/* ---------------------------------------------------------------------- + * F o r w a r d s + * ---------------------------------------------------------------------- + */ + +/* THIS IS JUST TEMPORARY */ +extern char* erl_errno_id(int error); + + +static ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_info(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +/* +This is a *global* debug function (enable or disable for all +operations and all sockets. +static ERL_NIF_TERM nif_debug(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +*/ +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[]); +static ERL_NIF_TERM nif_connect(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_listen(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_accept(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_accept4(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_send(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_sendto(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_recv(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_close(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_setopt(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_getopt(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 char* decode_laddress(ErlNifEnv* env, + int domain, + ERL_NIF_TERM localAddr, + SocketAddress* localP); +static char* decode_laddress_binary(ErlNifEnv* env, + int domain, + ERL_NIF_TERM localAddr, + SocketAddress* localP); +static char* decode_laddress_tuple(ErlNifEnv* env, + int domain, + ERL_NIF_TERM laddr, + SocketAddress* localP); +static char* decode_address_tuple(ErlNifEnv* env, + int domain, + const ERL_NIF_TERM* addrt, + int port, + SocketAddress* localP); +static char* decode_address_atom(ErlNifEnv* env, + int domain, + char* addr, + int addrLen, + int port, + SocketAddress* localP); + +static ERL_NIF_TERM nopen(ErlNifEnv* env, + int domain, + int type, + int protocol, + char* netns); +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 BOOLEAN_T edomain2domain(int edomain, int* domain); +static BOOLEAN_T etype2type(int etype, int* type); +static BOOLEAN_T eproto2proto(int eproto, int* proto); +#ifdef HAVE_SETNS +static BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns); +static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err); +static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err); +#endif + + +static void socket_dtor(ErlNifEnv* env, void* obj); +static void socket_stop(ErlNifEnv* env, + void* obj, + int fd, + int is_direct_call); +static void socket_down(ErlNifEnv* env, + void* obj, + const ErlNifPid* pid, + const ErlNifMonitor* mon); + +static ERL_NIF_TERM make_ok(ErlNifEnv* env, ERL_NIF_TERM any); +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 BOOLEAN_T extract_item_on_load(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ERL_NIF_TERM* val); + +static BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, + ERL_NIF_TERM map, + BOOLEAN_T def); + +static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); + + +#if HAVE_IN6 +# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY +# if HAVE_DECL_IN6ADDR_ANY_INIT +static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } }; +# else +static const struct in6_addr in6addr_any = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; +# endif /* HAVE_IN6ADDR_ANY_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_ANY */ + +# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK +# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT +static const struct in6_addr in6addr_loopback = + { { IN6ADDR_LOOPBACK_INIT } }; +# else +static const struct in6_addr in6addr_loopback = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }; +# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */ +#endif /* HAVE_IN6 */ + + + +/* *** String constants *** */ +static char str_false[] = "false"; +static char str_error[] = "error"; +static char str_ok[] = "ok"; +static char str_true[] = "true"; +static char str_undefined[] = "undefined"; + +/* (special) error string constants */ +static char str_eagain[] = "eagain"; +static char str_eafnosupport[] = "eafnosupport"; +static char str_einval[] = "einval"; +static char str_eisconn[] = "eisconn"; +static char str_exbadstate[] = "exbadstate"; +static char str_exmon[] = "exmonitor"; // failed monitor +static char str_exself[] = "exself"; // failed self + + +/* *** Atoms *** */ +static ERL_NIF_TERM atom_false; +static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_ok; +static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_undefined; + +static ERL_NIF_TERM atom_eagain; +static ERL_NIF_TERM atom_eafnosupport; +static ERL_NIF_TERM atom_einval; +static ERL_NIF_TERM atom_eisconn; +static ERL_NIF_TERM atom_exbadstate; +static ERL_NIF_TERM atom_exmon; +static ERL_NIF_TERM atom_exself; + + +/* *** Sockets *** */ +static ErlNifResourceType* sockets; +static ErlNifResourceTypeInit socketInit = { + socket_dtor, + socket_stop, + (ErlNifResourceDown*) socket_down +}; + +// Initiated when the nif is loaded +static SocketData socketData; + + +/* ---------------------------------------------------------------------- + * N I F F u n c t i o n s + * ---------------------------------------------------------------------- + * + * Utility and admin functions: + * ---------------------------- + * nif_is_loaded/0 + * nif_info/0 + * (nif_debug/1) + * + * The "proper" socket functions: + * ------------------------------ + * nif_open(Domain, Type, Protocol, Extra) + * nif_bind(Sock, LocalAddr) + * nif_connect(Sock, Addr, Port) + * nif_listen(Sock, Backlog) + * nif_accept(LSock, Ref) + * nif_accept4(LSock, Ref) + * nif_send(Sock, Data, Flags) + * nif_sendto(Sock, Data, Flags, DstAddr, DstPort) + * nif_recv(Sock, Flags) + * nif_recvfrom(Sock, Flags) + * nif_close(Sock) + * + * And some functions to manipulate and retrieve socket options: + * ------------------------------------------------------------- + * nif_setopt/3 + * nif_getopt/2 + * + * And some socket admin functions: + * ------------------------------------------------------------- + * nif_cancel(Sock, Ref) + */ + + +/* ---------------------------------------------------------------------- + * nif_is_loaded + * + * Description: + * This functions only purpose is to return the atom 'true'. + * This will happen *if* the (socket) nif library is loaded. + * If its not, the erlang (nif_is_loaded) will instead return + * 'false'. + */ +static +ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 0) + return enif_make_badarg(env); + + return atom_true; +} + + +/* ---------------------------------------------------------------------- + * nif_info + * + * Description: + * This is currently just a placeholder... + */ +static +ERL_NIF_TERM nif_info(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM info = enif_make_new_map(env); + return info; +} + + +/* ---------------------------------------------------------------------- + * nif_open + * + * Description: + * Create an endpoint for communication. + * + * Arguments: + * Domain + * Type + * Protocol + * Extra - A map with obscure options. + * Currently the only allowed option is netns (network namespace). + * This is *only* allowed on linux! + */ +static +ERL_NIF_TERM nif_open(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + int edomain, etype, eproto; + int domain, type, proto; + char* netns; + ERL_NIF_TERM emap; + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 4) || + !enif_get_int(env, argv[0], &edomain) || + !enif_get_int(env, argv[1], &etype) || + !enif_get_int(env, argv[2], &eproto) || + !enif_is_map(env, argv[3])) { + return enif_make_badarg(env); + } + emap = argv[3]; + + if (!edomain2domain(edomain, &domain)) + return enif_make_badarg(env); + + if (!etype2type(etype, &type)) + return enif_make_badarg(env); + + if (!eproto2proto(eproto, &proto)) + return enif_make_badarg(env); + +#ifdef HAVE_SETNS + /* We *currently* only support one extra option: netns */ + if (!emap2netns(env, emap, &netns)) + return enif_make_badarg(env); +#else + netns = NULL; +#endif + + return nopen(env, domain, type, proto, netns); +} + + +/* nopen - create an endpoint for communication + * + * Assumes the input has been validated. + */ +static +ERL_NIF_TERM nopen(ErlNifEnv* env, + int domain, int type, int protocol, + char* netns) +{ + SocketDescriptor* descP; + ERL_NIF_TERM res; + int save_errno = 0; + SOCKET sock; + HANDLE event; +#ifdef HAVE_SETNS + int current_ns; +#endif + + +#ifdef HAVE_SETNS + if (!change_network_namespace(netns, ¤t_ns, &save_errno)) + return make_error2(env, save_errno); +#endif + + if ((sock = sock_open(domain, type, protocol)) == INVALID_SOCKET) + return make_error2(env, sock_errno()); + +#ifdef HAVE_SETNS + if (!restore_network_namespace(current_ns, sock, &save_errno)) + return make_error2(env, save_errno); + + if (netns != NULL) + FREE(netns); +#endif + + + if ((event = sock_create_event(sock)) == INVALID_EVENT) { + save_errno = sock_errno(); + while ((close(sock) == INVALID_SOCKET) && (sock_errno() == EINTR)); + return make_error2(env, save_errno); + } + + + SET_NONBLOCKING(sock); + + + /* Create and initiate the socket "descriptor" */ + descP = enif_alloc_resource(sockets, sizeof(SocketDescriptor)); + { + char buf[64]; /* Buffer used for building the mutex name */ + sprintf(buf, "socket[w,%d]", sock); + descP->writeMtx = MCREATE(buf); + sprintf(buf, "socket[r,%d]", sock); + descP->readMtx = MCREATE(buf); + } + descP->isWritable = TRUE; + descP->isReadable = TRUE; + descP->writePkgCnt = 0; + descP->writeByteCnt = 0; + descP->writeTries = 0; + descP->writeWaits = 0; + descP->readPkgCnt = 0; + descP->readByteCnt = 0; + descP->readTries = 0; + descP->readWaits = 0; + descP->dbg = SOCKET_DEBUG_DEFAULT; + descP->state = SOCKET_STATE_OPEN; + descP->domain = domain; + descP->type = type; + descP->protocol = protocol; + descP->sock = sock; + descP->event = event; + + res = enif_make_resource(env, descP); + enif_release_resource(descP); // We should really store a reference ... + + + /* Keep track of the creator + * This should not be a problem but just in case + * the *open* function is used with the wrong kind + * of environment... + */ + if (enif_self(env, &descP->ctrlPid) == NULL) + return make_error(env, atom_exself); + + if (enif_monitor_process(env, descP, + &descP->ctrlPid, + &descP->ctrlMon) > 0) + return make_error(env, atom_exmon); + + +#ifdef __WIN32__ + /* What is the point of this? + * And how do we handle it? + * Since the select message will be delivered to the controlling + * process, which has no idea what to do with this... + * + * TODO! + */ + enif_select(env, + event, + ERL_NIF_SELECT_READ, + descP, NULL, atom_undefined); +#endif + + + return make_ok(env, res); +} + + + +#ifdef HAVE_SETNS +/* We should really have another API, so that we can return errno... */ + +/* *** change network namespace *** + * Retreive the current namespace and set the new. + * Return result and previous namespace if successfull. + */ +static +BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err) +{ + int save_errno; + int current_ns = 0; + int new_ns = 0; + + if (netns != NULL) { + current_ns = open("/proc/self/ns/net", O_RDONLY); + if (current_ns == INVALID_SOCKET) { + *cns = current_ns; + *err = sock_errno(); + return FALSE; + } + new_ns = open(netns, O_RDONLY); + if (new_ns == INVALID_SOCKET) { + save_errno = sock_errno(); + while (close(current_ns) == INVALID_SOCKET && + sock_errno() == EINTR); + *cns = -1; + *err = save_errno; + return FALSE; + } + if (setns(new_ns, CLONE_NEWNET) != 0) { + save_errno = sock_errno(); + while ((close(new_ns) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + while ((close(current_ns) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + *cns = -1; + *err = save_errno; + return FALSE; + } else { + while ((close(new_ns) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + *cns = current_ns; + *err = 0; + return TRUE; + } + } else { + *cns = INVALID_SOCKET; + *err = 0; + return TRUE; + } +} + + +/* *** restore network namespace *** + * Restore the previous namespace (see above). + */ +static +BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err) +{ + int save_errno; + if (ns != INVALID_SOCKET) { + if (setns(ns, CLONE_NEWNET) != 0) { + /* XXX Failed to restore network namespace. + * What to do? Tidy up and return an error... + * Note that the thread now might still be in the namespace. + * Can this even happen? Should the emulator be aborted? + */ + if (sock != INVALID_SOCKET) + save_errno = sock_errno(); + while (close(sock) == INVALID_SOCKET && + sock_errno() == EINTR); + sock = INVALID_SOCKET; + while (close(ns) == INVALID_SOCKET && + sock_errno() == EINTR); + *err = save_errno; + return FALSE; + } else { + while (close(ns) == INVALID_SOCKET && + sock_errno() == EINTR); + *err = 0; + return TRUE; + } + } + + *err = 0; + return TRUE; +} +#endif + + + +/* ---------------------------------------------------------------------- + * nif_bind + * + * Description: + * Bind a name to a socket. + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * LocalAddr - Local address is either: + * - a binary - when the socket domain = local + * - a tuple of size 2 with + * - first element is the address + * - second element is the port number + * The address can be in the form of either: + * - A tuple of size 4 or 8 (depending on domain) + * - The atom 'any' + * - The atom 'loopback' + */ +static +ERL_NIF_TERM nif_bind(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 2) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + + /* Basic arg validation: + * - if binary domain must be local (unix) + * - if tuple domain must be either inet or inet6 + */ + if (IS_BIN(env, argv[1]) && (descP->domain != AF_UNIX)) { + return enif_make_badarg(env); + } else if (IS_TUPLE(env, argv[1]) && + (descP->domain != AF_INET) && + (descP->domain != AF_INET6)) { + return enif_make_badarg(env); + } + + + /* Make sure we are ready + * Not sure how this would even happen, but... + */ + if (descP->state != SOCKET_STATE_OPEN) + return make_error(env, atom_exbadstate); + + return nbind(env, descP, argv[1]); +} + + +static +ERL_NIF_TERM nbind(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM addr) +{ + SocketAddress local; + char* err; + int port; + + if ((err = decode_laddress(env, descP->domain, addr, &local)) != NULL) + return make_error1(env, err); + + if (IS_SOCKET_ERROR(sock_bind(descP->sock, + (struct sockaddr*) &local.u, local.len))) { + return make_error2(env, sock_errno()); + } + + port = which_address_port(&local); + if (port == 0) { + SOCKLEN_T addrLen = sizeof(local.u); + sys_memzero((char *) &local.u, addrLen); + sock_name(descP->sock, &local.u.sa, &addrLen); + port = which_address_port(&local); + } else if (port == -1) { + port = 0; + } + + return make_ok(env, enif_make_int(env, port)); + +} + + +/* Decode the (local) address. The format of the address should + * either be an binary (domain = local) or a two tuple (first + * part the actual address tuple and the second part the port + * number) if the domain is either INET or INET6. + */ +static +char* decode_laddress(ErlNifEnv* env, + int domain, + ERL_NIF_TERM localAddr, + SocketAddress* localP) +{ + if (IS_BIN(env, localAddr)) { + return decode_laddress_binary(env, domain, localAddr, localP); + } else if (IS_TUPLE(env, localAddr)) { + return decode_laddress_tuple(env, domain, localAddr, localP); + } else { + return str_einval; + } + +} + + +/* Only for domain = local (unix) + * The erlang interface module ensures that the size of the + * binary is > 0, so we need not do that here. + */ +static +char* decode_laddress_binary(ErlNifEnv* env, + int domain, + ERL_NIF_TERM localAddr, + SocketAddress* localP) +{ +#ifdef HAVE_SYS_UN_H + ErlNifBinary bin; + + if (domain != AF_UNIX) + return str_einval; + + if (!GET_BIN(env, localAddr, &bin)) + return str_einval; + + if ((bin.size + +#ifdef __linux__ + /* Make sure the address gets zero terminated + * except when the first byte is \0 because then it is + * sort of zero terminated although the zero termination + * comes before the address... + * This fix handles Linux's nonportable + * abstract socket address extension. + */ + (bin.data[0] == '\0' ? 0 : 1) +#else + 1 +#endif + ) > sizeof(localP->u.sal.sun_path)) + return str_einval; + + sys_memzero((char*)&localP->u, sizeof(struct sockaddr_un)); + localP->u.sal.sun_family = domain; + sys_memcpy(localP->u.sal.sun_path, bin.data, bin.size); + localP->len = offsetof(struct sockaddr_un, sun_path) + bin.size; +#ifndef NO_SA_LEN + localP->u.sal.sun_len = localP->len; +#endif + return NULL; + +#else // HAVE_SYS_UN_H + return str_eafnosupport; +#endif + +} + + +/* Only for domain = INET and INET6 + * The (local) address here is a two tuple: + * {Addr, Port} + * where + * Addr: + * - a tuple of size 4 (INET) or size 8 (INET6) + * - the atoms 'any' or 'loopback' + * Port: + * - the port number (int) + * + */ +static +char* decode_laddress_tuple(ErlNifEnv* env, + int domain, + ERL_NIF_TERM laddr, + SocketAddress* localP) +{ + const ERL_NIF_TERM* laddrt; + int laddrtSz; + int port; + + /* First, get the tuple and verify its size (2) */ + + if (!GET_TUPLE(env, laddr, &laddrtSz, &laddrt)) + return str_einval; + + if (laddrtSz != 2) + return str_einval; + + /* So far so good. The first element is either a tuple or an atom */ + + if (IS_TUPLE(env, laddrt[0]) && + IS_NUM(env, laddrt[1])) { + + /* We handle two different tuples: + * - size 4 (INET) + * - size 8 (INET6) + */ + + const ERL_NIF_TERM* addrt; + int addrtSz; + + if (!GET_TUPLE(env, laddrt[0], &addrtSz, &addrt)) + return str_einval; // PLACEHOLDER + + if (!GET_INT(env, laddrt[1], &port)) + return str_einval; // PLACEHOLDER + + switch (domain) { + case AF_INET: + if (addrtSz != 4) + return str_einval; + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + if (addrtSz != 8) + return str_einval; + break; +#endif + + default: + return str_eafnosupport; + break; + } + + return decode_address_tuple(env, + domain, addrt, port, + localP); + + } else if (IS_ATOM(env, laddrt[0]) && + IS_NUM(env, laddrt[1])) { + + /* There are only two atoms we handle: + * - any + * - loopback + */ + + unsigned int len; + char a[16]; // Just in case... + + if (!(GET_ATOM_LEN(env, laddrt[1], &len) && + (len > 0) && + (len <= (sizeof("loopback"))))) + return str_einval; + + if (!GET_ATOM(env, laddrt[0], a, sizeof(a))) + return str_einval; + + return decode_address_atom(env, + domain, a, len, port, + localP); + + } else { + return str_einval; + } + +} + + +/* Decode the 4- or 8-element address tuple + * and initiate the socket address structure. + */ +static +char* decode_address_tuple(ErlNifEnv* env, + int domain, + const ERL_NIF_TERM* addrt, + int port, + SocketAddress* addrP) +{ + + /* We now *know* that the size of the tuple is correct, + * so we don't need to check anything here, just unpack. + */ + + switch (domain) { + case AF_INET: + { + int a, v; + char laddr[4]; + + sys_memzero((char*)&addrP->u, sizeof(struct sockaddr_in)); +#ifndef NO_SA_LEN + addrP->u.sai.sin_len = sizeof(struct sockaddr_in); +#endif + addrP->u.sai.sin_family = domain; + addrP->u.sai.sin_port = sock_htons(port); + for (a = 0; a < 4; a++) { + if (!GET_INT(env, addrt[a], &v)) + return str_einval; + laddr[a] = v; + } + sys_memcpy(&localP->u.sai.sin_addr, &laddr, sizeof(laddr)); + addrP->len = sizeof(struct sockaddr_in); + return NULL; + } + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + { + int a, v; + char laddr[16]; + + sys_memzero((char*)&addrP->u, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + addrP->u.sai6.sin6_len = sizeof(struct sockaddr_in6); +#endif + addrP->u.sai6.sin6_family = domain; + addrP->u.sai6.sin6_port = sock_htons(port); + addrP->u.sai6.sin6_flowinfo = 0; + /* 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, addrt[a], &v)) + return str_einval; + laddr[a*2 ] = ((v >> 8) & 0xFF); + laddr[a*2+1] = (v & 0xFF); + } + sys_memcpy(&localP->u.sai6.sin6_addr, &laddr, sizeof(laddr)); + addrP->len = sizeof(struct sockaddr_in6); + return NULL; + } + break; +#endif + + } /* switch (domain) */ + + return str_eafnosupport; + +} + + +/* Decode the address when its an atom. + * Currently we only accept two atoms: 'any' and 'loopback' + */ +static +char* decode_address_atom(ErlNifEnv* env, + int domain, + char* addr, + int addrLen, + int port, + SocketAddress* localP) +{ + BOOLEAN_T any; + + if (strncmp(addr, "any", addrLen) == 0) { + any = TRUE; + } if (strncmp(addr, "loopback", addrLen) == 0) { + any = FALSE; + } else { + return str_einval; + } + + /* If we get this far, we *know* its either 'any' or 'loopback' */ + + switch (domain) { + case AF_INET: + { + struct in_addr addr; + if (any) { + addr.s_addr = sock_htonl(INADDR_ANY); + } else { + addr.s_addr = sock_htonl(INADDR_LOOPBACK); + } + sys_memzero((char*) localP, sizeof(struct sockaddr_in)); +#ifndef NO_SA_LEN + localP->u.sai.sin_len = sizeof(struct sockaddr_in6); +#endif + localP->u.sai.sin_family = domain; + localP->u.sai.sin_port = sock_htons(port); + localP->u.sai.sin_addr.s_addr = addr.s_addr; + localP->len = sizeof(struct sockaddr_in); + } + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + { + const struct in6_addr* paddr; + if (any) { + paddr = &in6addr_any; + } else { + paddr = &in6addr_loopback; + } + sys_memzero((char*)localP, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + localP->u.sai6.sin6_len = sizeof(struct sockaddr_in6); +#endif + localP->u.sai6.sin6_family = domain; + localP->u.sai6.sin6_port = sock_htons(port); + localP->u.sai6.sin6_flowinfo = 0; + localP->u.sai6.sin6_addr = *paddr; + localP->len = sizeof(struct sockaddr_in6); + } + break; +#endif + + default: + return str_einval; + break; + } + + return NULL; +} + + + +/* ---------------------------------------------------------------------- + * nif_connect + * + * Description: + * Initiate a connection on a socket + * + * 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. + */ +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; + + /* 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)) { + return enif_make_badarg(env); + } + + 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) + + return nconnect(env, descP, addr, port); +} + + +static +ERL_NIF_TERM nconnect(ErlNifEnv* env, + SocketDescriptor* descP, + const ERL_NIF_TERM* addr, + int port) +{ + /* Verify that we are where in the proper state */ + + if (!IS_OPEN(descP)) + return make_error(env, atom_exbadstate); + + if (IS_CONNECTED(descP)) + return make_error(env, atom_eisconn); + + if (IS_CONNECTING(descP)) + return make_error(env, atom_einval); + + if ((xerr = decode_address_tuple(env, + descP->domain, addr, port, + &descP->remote)) != NULL) + return make_error1(env, xerr); + + code = sock_connect(descP->sock, + (struct sockaddr*) &descP->remote.u, descP->remote.len); + + if (IS_SOCKET_ERROR(code) && + ((sock_errno() == ERRNO_BLOCK) || /* Winsock2 */ + (sock_errno() == EINPROGRESS))) { /* Unix & OSE!! */ + ERL_NIF_TERM ref = MKREF(env); + descP->state = INET_STATE_CONNECTING; + enif_select(env, + descP->sock, + (ERL_NIF_SELECT_WRITE), + descP, NULL, ref); + return make_ok(env, ref); + } else if (code == 0) { /* ok we are connected */ + descP->state = SOCKET_STATE_CONNECTED; + /* Do we need to do somthing for "active" mode? + * Is there even such a thing *here*? + */ + return atom_ok; + } else { + return make_error2(env, sock_errno()); + } + +} + + +/* ---------------------------------------------------------------------- + * nif_finalize_connection + * + * Description: + * Make socket ready for input and output. + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + */ +static +ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 1) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + + return nfinalize_connection(descP); + +} + + +static +ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, + SocketDescriptor* descP) +{ + int error; + + if (descP->state != INET_STATE_CONNECTING) + return make_error(env, atom_exisnconn); + + if (!is_connected(descP, &error)) + return make_error2(env, error); + + descP->state = INET_STATE_CONNECTED; + + return make_ok1(env); +} + + + +/* ---------------------------------------------------------------------- + * U t i l i t y F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* edomain2domain - convert internal (erlang) domain to (proper) domain + * + * Note that only a subset is supported. + */ +static +BOOLEAN_T edomain2domain(int edomain, int* domain) +{ + switch (edomain) { + case SOCKET_DOMAIN_INET: + *domain = AF_INET; + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case SOCKET_DOMAIN_INET6: + *domain = AF_INET6; + break; +#endif +#ifdef HAVE_SYS_UN_H + case SOCKET_DOMAIN_LOCAL: + *domain = AF_UNIX; + break; +#endif + + default: + return FALSE; + } + + return TRUE; +} + + + +/* etype2type - convert internal (erlang) type to (proper) type + * + * Note that only a subset is supported. + */ +static +BOOLEAN_T etype2type(int etype, int* type) +{ + switch (etype) { + case SOCKET_TYPE_STREAM: + *type = SOCK_STREAM; + break; + + case SOCKET_TYPE_DGRAM: + *type = SOCK_DGRAM; + break; + + case SOCKET_TYPE_RAW: + *type = SOCK_RAW; + break; + + case SOCKET_TYPE_SEQPACKET: + *type = SOCK_SEQPACKET; + break; + + default: + return FALSE; + } + + return TRUE; +} + + + +/* eproto2proto - convert internal (erlang) protocol to (proper) protocol + * + * Note that only a subset is supported. + */ +static +BOOLEAN_T eproto2proto(int eproto, int* proto) +{ + switch (eproto) { + case SOCKET_PROTOCOL_IP: + *proto = IPPROTO_IP; + break; + + case SOCKET_PROTOCOL_TCP: + *proto = IPPROTO_TCP; + break; + + case SOCKET_PROTOCOL_UDP: + *proto = IPPROTO_UDP; + break; + + case SOCKET_PROTOCOL_SCTP: + *proto = IPPROTO_SCTP; + break; + + default: + return FALSE; + } + + return TRUE; +} + + +#ifdef HAVE_SETNS + /* emap2netns - extract the netns field from the extra map + * + * Note that currently we only support one extra option, the netns. + */ +static +BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns) +{ + size_t sz; + ERL_NIF_TERM key; + ERL_NIF_TERM value; + unsigned int len; + char* buf; + int written; + + /* Note that its acceptable that the extra map is empty */ + if (!enif_get_map_size(env, map, &sz) || + (sz != 1)) { + *netns = NULL; + return TRUE; + } + + /* The currently only supported extra option is: netns */ + key = enif_make_atom(env, "netns"); + if (!enif_get_map_value(env, map, key, &value)) { + *netns = NULL; // Just in case... + return FALSE; + } + + /* So far so good. The value should be a string, check. */ + if (!enif_is_list(env, value)) { + *netns = NULL; // Just in case... + return FALSE; + } + + if (!enif_get_list_length(env, value, &len)) { + *netns = NULL; // Just in case... + return FALSE; + } + + if ((buf = MALLOC(len+1)) == NULL) { + *netns = NULL; // Just in case... + return FALSE; + } + + written = enif_get_string(env, value, buf, len+1, ERL_NIF_LATIN1); + if (written == (len+1)) { + *netns = buf; + return TRUE; + } else { + *netns = NULL; // Just in case... + return FALSE; + } +} +#endif + + +/* Create an ok two (2) tuple in the form: {ok, Any}. + * The second element (Any) is already in the form of an + * ERL_NIF_TERM so all we have to do is create the tuple. + */ +static +ERL_NIF_TERM make_ok(ErlNifEnv* env, ERL_NIF_TERM any) +{ + return MKT2(env, atom_ok, any); +} + + +/* Create an error two (2) tuple in the form: {error, Reason}. + * The second element (Reason) is already in the form of an + * ERL_NIF_TERM so all we have to do is create the tuple. + */ +static +ERL_NIF_TERM make_error(ErlNifEnv* env, ERL_NIF_TERM reason) +{ + return MKT2(env, atom_error, reason); +} + + +/* Create an error two (2) tuple in the form: {error, Reason}. + * The second element, Reason, is a string to be converted into + * an atom. + */ +static +ERL_NIF_TERM make_error1(ErlNifEnv* env, char* reason) +{ + return make_error(env, MKA(env, reason)); +} + + +/* Create an error two (2) tuple in the form: {error, Reason}. + * The second element, Reason, is the errno value in its + * basic form (integer) which will (eventually) be converted + * into an atom. + */ +static +ERL_NIF_TERM make_error2(ErlNifEnv* env, int err) +{ + return make_error1(env, erl_errno_id(err)); +} + + + +/* ---------------------------------------------------------------------- + * C a l l b a c k F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* ========================================================================= + * socket_dtor - Callback function for resource destructor + * + */ +static +void socket_dtor(ErlNifEnv* env, void* obj) +{ +} + + +/* ========================================================================= + * socket_stop - Callback function for resource stop + * + */ +static +void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) +{ +} + + +/* ========================================================================= + * socket_down - Callback function for resource down (monitored processes) + * + */ +static +void socket_down(ErlNifEnv* env, + void* obj, + const ErlNifPid* pid, + const ErlNifMonitor* mon) +{ +} + + + +/* ---------------------------------------------------------------------- + * L o a d / u n l o a d / u p g r a d e F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +static +ErlNifFunc socket_funcs[] = +{ + // Some utility functions + {"nif_is_loaded", 0, nif_is_loaded}, + {"nif_info", 0, nif_info}, + // {"nif_debug", 1, nif_debug_}, + + // The proper "socket" interface + {"nif_open", 4, nif_open}, + {"nif_bind", 3, nif_bind}, + {"nif_connect", 3, nif_connect}, + {"nif_listen", 2, nif_listen}, + {"nif_accept", 2, nif_accept}, + {"nif_accept4", 3, nif_accept4}, + {"nif_send", 3, nif_send}, + {"nif_sendto", 5, nif_sendto}, + {"nif_recv", 2, nif_recv}, + {"nif_recvfrom", 2, nif_recvfrom}, + {"nif_close", 1, nif_close}, + {"nif_setopt", 3, nif_setopt}, + {"nif_getopt", 2, nif_getopt}, + + /* "Extra" functions to "complete" the socket interface. + * For instance, the function nif_finalyze_connection + * is called after the connect select has "completed". + */ + {"nif_finalize_connection", 1, nif_finalyze_connection}, + {"nif_cancel", 2, nif_cancel}, +}; + + +static +BOOLEAN_T extract_item_on_load(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + ERL_NIF_TERM* val) +{ + if (!enif_is_map(env, map)) + return FALSE; + + if (!enif_get_map_value(env, map, key, val)) + return FALSE; + + return TRUE; +} + +static +BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) +{ + ERL_NIF_TERM dbgKey = enif_make_atom(env, "debug"); + ERL_NIF_TERM dbgVal; + unsigned int len; + char d[16]; // Just in case... + + /* Extra the value of the debug property */ + if (!extract_item_on_load(env, map, dbgKey, &dbgVal)) + return def; + + /* Verify that the value is actually an atom */ + if (!enif_is_atom(env, dbgVal)) + return def; + + /* Verify that the value is of acceptable length */ + if (!(GET_ATOM_LEN(env, dbgVal, &len) && + (len > 0) && + (len <= sizeof("false")))) + return def; + + /* And finally try to extract the value */ + if (!GET_ATOM(env, dbgVal, d, sizeof(d))) + return def; + + if (strncmp(d, "true", len) == 0) + return TRUE; + else + return FALSE; + +} + +/* ======================================================================= + * load_info - A map of misc info (e.g global debug) + */ + +static +int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + socketData.dbg = extract_debug_on_load(env, load_info, + SOCKET_NIF_DEBUG_DEFAULT); + + /* Misc atoms */ + // atom_active = MKA(env, str_active); + // atom_active_n = MKA(env, str_active_n); + // atom_active_once = MKA(env, str_active_once); + // atom_binary = MKA(env, str_binary); + // atom_buildDate = MKA(env, str_buildDate); + // atom_closed = MKA(env, str_closed); + atom_error = MKA(env, str_error); + atom_false = MKA(env, str_false); + // atom_list = MKA(env, str_list); + // atom_mode = MKA(env, str_mode); + atom_ok = MKA(env, str_ok); + // atom_once = MKA(env, str_once); + // atom_passive = MKA(env, str_passive); + // atom_receiver = MKA(env, str_receiver); + // atom_tcp_closed = MKA(env, str_tcp_closed); + atom_true = MKA(env, str_true); + atom_undefined = MKA(env, str_undefined); + // atom_version = MKA(env, str_version); + + /* Error codes */ + atom_eagain = MKA(env, str_eagain); + atom_eafnosupport = MKA(env, str_eafnosupport); + atom_einval = MKA(env, str_einval); + atom_eisconn = MKA(env, str_eisconn); + // atom_exalloc = MKA(env, str_exalloc); + atom_exbadstate = MKA(env, str_exbadstate); + // atom_exnotopen = MKA(env, str_exnotopen); + atom_exmon = MKA(env, str_exmon); + atom_exself = MKA(env, str_exself); + // atom_exsend = MKA(env, str_exsend); + // atom_exisnconning = MKA(env, str_exisnconning); + + // For storing "global" things... + // socketData.env = enif_alloc_env(); // We should really check + // socketData.version = MKA(env, ERTS_VERSION); + // socketData.buildDate = MKA(env, ERTS_BUILD_DATE); + + sockets = enif_open_resource_type_x(env, + "sockets", + &socketInit, + ERL_NIF_RT_CREATE, + NULL); + + return !sockets; +} + +ERL_NIF_INIT(socket, socket_funcs, on_load, NULL, NULL, NULL) -- cgit v1.2.3 From e752b4a8187903da609004c56f0f019b1d7b7605 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Apr 2018 12:29:25 +0200 Subject: [socket-nif] Completed connect Still not implemented the 'cancel' operation. This will be used when, for instance, we need to cancel an ongoing connect that has taken to long time to complete (the select). The idea is to use select(STOP). --- erts/emulator/nifs/common/socket_nif.c | 110 +++++++++++++++++++++++++++------ 1 file changed, 91 insertions(+), 19 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 2c51ff2000..a702e35a0d 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -226,6 +226,7 @@ typedef unsigned long long llu_t; #define MALLOC(SZ) enif_alloc(SZ) #define FREE(P) enif_free(P) #define MKA(E,S) enif_make_atom(E, S) +#define MKREF(E) enif_make_ref(E) #define MKT2(E,E1,E2) enif_make_tuple2(E, E1, E2) #define MCREATE(N) enif_mutex_create(N) @@ -317,11 +318,13 @@ typedef unsigned long long llu_t; /* *** Windown macros *** */ #define sock_bind(s, addr, len) bind((s), (addr), (len)) +#define sock_connect(s, addr, len) connect((s), (addr), (len)) +#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) +#define sock_htons(x) htons((x)) +#define sock_htonl(x) htonl((x)) #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_open(domain, type, proto) \ make_noninheritable_handle(socket((domain), (type), (proto))) -#define sock_htons(x) htons((x)) -#define sock_htonl(x) htonl((x)) #define sock_errno() WSAGetLastError() #define sock_create_event(s) WSACreateEvent() @@ -334,10 +337,12 @@ static unsigned long one_value = 1; #else /* !__WIN32__ */ #define sock_bind(s, addr, len) bind((s), (addr), (len)) -#define sock_name(s, addr, len) getsockname((s), (addr), (len)) -#define sock_open(domain, type, proto) socket((domain), (type), (proto)) +#define sock_connect(s, addr, len) connect((s), (addr), (len)) +#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) #define sock_htons(x) htons((x)) #define sock_htonl(x) htonl((x)) +#define sock_name(s, addr, len) getsockname((s), (addr), (len)) +#define sock_open(domain, type, proto) socket((domain), (type), (proto)) #define sock_errno() errno #define sock_create_event(s) (s) /* return file descriptor */ @@ -502,6 +507,9 @@ 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_finalize_connection(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_cancel(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -543,6 +551,10 @@ static ERL_NIF_TERM nconnect(ErlNifEnv* env, SocketDescriptor* descP, const ERL_NIF_TERM* addr, int port); +static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, + SocketDescriptor* descP); + +static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err); static BOOLEAN_T edomain2domain(int edomain, int* domain); static BOOLEAN_T etype2type(int etype, int* type); @@ -617,6 +629,7 @@ static char str_eagain[] = "eagain"; static char str_eafnosupport[] = "eafnosupport"; static char str_einval[] = "einval"; static char str_eisconn[] = "eisconn"; +static char str_eisnconn[] = "eisnconn"; static char str_exbadstate[] = "exbadstate"; static char str_exmon[] = "exmonitor"; // failed monitor static char str_exself[] = "exself"; // failed self @@ -633,6 +646,7 @@ static ERL_NIF_TERM atom_eagain; static ERL_NIF_TERM atom_eafnosupport; static ERL_NIF_TERM atom_einval; static ERL_NIF_TERM atom_eisconn; +static ERL_NIF_TERM atom_eisnconn; static ERL_NIF_TERM atom_exbadstate; static ERL_NIF_TERM atom_exmon; static ERL_NIF_TERM atom_exself; @@ -1273,8 +1287,8 @@ char* decode_address_tuple(ErlNifEnv* env, return str_einval; laddr[a] = v; } - sys_memcpy(&localP->u.sai.sin_addr, &laddr, sizeof(laddr)); - addrP->len = sizeof(struct sockaddr_in); + sys_memcpy(&addrP->u.sai.sin_addr, &laddr, sizeof(laddr)); + addrP->len = sizeof(struct sockaddr_in); return NULL; } break; @@ -1301,8 +1315,8 @@ char* decode_address_tuple(ErlNifEnv* env, laddr[a*2 ] = ((v >> 8) & 0xFF); laddr[a*2+1] = (v & 0xFF); } - sys_memcpy(&localP->u.sai6.sin6_addr, &laddr, sizeof(laddr)); - addrP->len = sizeof(struct sockaddr_in6); + sys_memcpy(&addrP->u.sai6.sin6_addr, &laddr, sizeof(laddr)); + addrP->len = sizeof(struct sockaddr_in6); return NULL; } break; @@ -1449,6 +1463,9 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, const ERL_NIF_TERM* addr, int port) { + int code; + char* xerr; + /* Verify that we are where in the proper state */ if (!IS_OPEN(descP)) @@ -1472,7 +1489,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, ((sock_errno() == ERRNO_BLOCK) || /* Winsock2 */ (sock_errno() == EINPROGRESS))) { /* Unix & OSE!! */ ERL_NIF_TERM ref = MKREF(env); - descP->state = INET_STATE_CONNECTING; + descP->state = SOCKET_STATE_CONNECTING; enif_select(env, descP->sock, (ERL_NIF_SELECT_WRITE), @@ -1514,26 +1531,81 @@ ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, return enif_make_badarg(env); } - return nfinalize_connection(descP); + return nfinalize_connection(env, descP); } +/* *** nfinalize_connection *** + * Perform the final check to verify a connection. + */ static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP) { int error; - if (descP->state != INET_STATE_CONNECTING) - return make_error(env, atom_exisnconn); + if (descP->state != SOCKET_STATE_CONNECTING) + return make_error(env, atom_eisnconn); - if (!is_connected(descP, &error)) + if (!verify_is_connected(descP, &error)) { + descP->state = SOCKET_STATE_OPEN; /* restore state */ return make_error2(env, error); + } + + descP->state = SOCKET_STATE_CONNECTED; + + return atom_ok; +} + + +/* *** verify_is_connected *** + * Check if a connection has been established. + */ +static +BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err) +{ + /* + * *** This is strange *** + * + * This *should* work on Windows NT too, but doesn't. + * An bug in Winsock 2.0 for Windows NT? + * + * See "Unix Netwok Programming", W.R.Stevens, p 412 for a + * discussion about Unix portability and non blocking connect. + */ + +#ifndef SO_ERROR + int sz, code; + + sz = sizeof(descP->inet.remote); + sys_memzero((char *) &descP->inet.remote, sz); + code = sock_peer(desc->sock, + (struct sockaddr*) &descP->remote, &sz); + + if (IS_SOCKET_ERROR(code)) { + *err = sock_errno(); + return FALSE; + } + +#else - descP->state = INET_STATE_CONNECTED; + int error = 0; /* Has to be initiated, we check it */ + unsigned int sz = sizeof(error); /* even if we get -1 */ + int code = sock_getopt(descP->sock, + SOL_SOCKET, SO_ERROR, + (void *)&error, &sz); - return make_ok1(env); + if ((code < 0) || error) { + *err = error; + return FALSE; + } + +#endif /* SO_ERROR */ + + *err = 0; + + return TRUE; } @@ -1811,10 +1883,10 @@ ErlNifFunc socket_funcs[] = {"nif_getopt", 2, nif_getopt}, /* "Extra" functions to "complete" the socket interface. - * For instance, the function nif_finalyze_connection - * is called after the connect select has "completed". + * For instance, the function nif_finalize_connection + * is called after the connect *select* has "completed". */ - {"nif_finalize_connection", 1, nif_finalyze_connection}, + {"nif_finalize_connection", 1, nif_finalize_connection}, {"nif_cancel", 2, nif_cancel}, }; @@ -1902,13 +1974,13 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_eafnosupport = MKA(env, str_eafnosupport); atom_einval = MKA(env, str_einval); atom_eisconn = MKA(env, str_eisconn); + atom_eisnconn = MKA(env, str_eisnconn); // atom_exalloc = MKA(env, str_exalloc); atom_exbadstate = MKA(env, str_exbadstate); // atom_exnotopen = MKA(env, str_exnotopen); atom_exmon = MKA(env, str_exmon); atom_exself = MKA(env, str_exself); // atom_exsend = MKA(env, str_exsend); - // atom_exisnconning = MKA(env, str_exisnconning); // For storing "global" things... // socketData.env = enif_alloc_env(); // We should really check -- cgit v1.2.3 From 9e6fda01b1af0c42cee9f983d5bddecc7eb7e240 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Apr 2018 12:54:52 +0200 Subject: [socket-nif] Completed listen --- erts/emulator/nifs/common/socket_nif.c | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index a702e35a0d..88fb2206e4 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -322,6 +322,7 @@ typedef unsigned long long llu_t; #define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) #define sock_htons(x) htons((x)) #define sock_htonl(x) htonl((x)) +#define sock_listen(s, b) listen((s), (b)) #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_open(domain, type, proto) \ make_noninheritable_handle(socket((domain), (type), (proto))) @@ -341,6 +342,7 @@ static unsigned long one_value = 1; #define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) #define sock_htons(x) htons((x)) #define sock_htonl(x) htonl((x)) +#define sock_listen(s, b) listen((s), (b)) #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_open(domain, type, proto) socket((domain), (type), (proto)) @@ -551,6 +553,9 @@ static ERL_NIF_TERM nconnect(ErlNifEnv* env, SocketDescriptor* descP, const ERL_NIF_TERM* addr, int port); +static ERL_NIF_TERM nlisten(ErlNifEnv* env, + SocketDescriptor* descP, + int backlog); static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP); @@ -1610,6 +1615,59 @@ BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err) +/* ---------------------------------------------------------------------- + * nif_listen + * + * Description: + * Listen for connections on a socket. + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * Backlog - The maximum length to which the queue of pending + * connections for socket may grow. + */ +static +ERL_NIF_TERM nif_listen(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + int backlog; + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 2) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP) || + !GET_INT(env, argv[1], &backlog)) { + return enif_make_badarg(env); + } + + return nlisten(env, descP, backlog); +} + + + +static +ERL_NIF_TERM nlisten(ErlNifEnv* env, + SocketDescriptor* descP, + int backlog) +{ + if (descP->state == SOCKET_STATE_CLOSED) + return make_error(env, atom_exbadstate); + + if (!IS_OPEN(descP)) + return make_error(env, atom_exbadstate); + + if (IS_SOCKET_ERROR(sock_listen(descP->sock, backlog))) + return make_error2(env, sock_errno()); + + descP->state = SOCKET_STATE_LISTENING; + + return atom_ok; +} + + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- -- cgit v1.2.3 From c5c8da4ecb985837817e60738811793754c679a0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 Apr 2018 12:38:58 +0200 Subject: [socket-nif] Completed accept --- erts/emulator/nifs/common/socket_nif.c | 432 ++++++++++++++++++++++++++++++--- 1 file changed, 399 insertions(+), 33 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 88fb2206e4..3e8fe7061a 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -229,6 +229,8 @@ typedef unsigned long long llu_t; #define MKREF(E) enif_make_ref(E) #define MKT2(E,E1,E2) enif_make_tuple2(E, E1, E2) #define MCREATE(N) enif_mutex_create(N) +#define MLOCK(M) enif_mutex_lock(M) +#define MUNLOCK(M) enif_mutex_unlock(M) /* *** Socket state defs *** */ @@ -239,15 +241,13 @@ typedef unsigned long long llu_t; #define SOCKET_FLAG_CON 0x0010 #define SOCKET_FLAG_ACC 0x0020 #define SOCKET_FLAG_BUSY 0x0040 -#define SOCKET_FLAG_MULTI_CLIENT 0x0100 /* Multiple clients for one descriptor, * - * i.e. multi-accept */ + #define SOCKET_STATE_CLOSED (0) #define SOCKET_STATE_OPEN (SOCKET_FLAG_OPEN) #define SOCKET_STATE_CONNECTED (SOCKET_STATE_OPEN | SOCKET_FLAG_ACTIVE) #define SOCKET_STATE_LISTENING (SOCKET_STATE_OPEN | SOCKET_FLAG_LISTEN) #define SOCKET_STATE_CONNECTING (SOCKET_STATE_OPEN | SOCKET_FLAG_CON) #define SOCKET_STATE_ACCEPTING (SOCKET_STATE_LISTENING | SOCKET_FLAG_ACC) -#define SOCKET_STATE_MULTI_ACCEPTING (SOCKET_STATE_ACCEPTING | SOCKET_FLAG_MULTI_CLIENT) #define IS_OPEN(d) \ (((d)->state & SOCKET_FLAG_OPEN) == SOCKET_FLAG_OPEN) @@ -317,7 +317,10 @@ typedef unsigned long long llu_t; /* *** Windown macros *** */ +#define sock_accept(s, addr, len) \ + make_noninheritable_handle(accept((s), (addr), (len))) #define sock_bind(s, addr, len) bind((s), (addr), (len)) +#define sock_close(s) closesocket((s)) #define sock_connect(s, addr, len) connect((s), (addr), (len)) #define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) #define sock_htons(x) htons((x)) @@ -337,7 +340,14 @@ static unsigned long one_value = 1; #else /* !__WIN32__ */ +#ifdef HAS_ACCEPT4 +// We have to figure out what the flags are... +#define sock_accept(s, addr, len) accept4((s), (addr), (len), (SOCK_CLOEXEC)) +#else +#define sock_accept(s, addr, len) accept((s), (addr), (len)) +#endif #define sock_bind(s, addr, len) bind((s), (addr), (len)) +#define sock_close(s) close((s)) #define sock_connect(s, addr, len) connect((s), (addr), (len)) #define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) #define sock_htons(x) htons((x)) @@ -383,6 +393,12 @@ typedef struct { ((sap)->u.sai.sin_port) : -1) +typedef struct { + ErlNifPid pid; // PID of the acceptor + ErlNifMonitor mon; // Monitor for the acceptor + ERL_NIF_TERM ref; // The (unique) reference of the (accept) request +} SocketAcceptor; + typedef struct { // The actual socket SOCKET sock; @@ -419,6 +435,13 @@ typedef struct { unsigned int readTries; unsigned int readWaits; + /* Accept + * We also need a queue for waiting acceptors... + * Lets see if this can be a common "request" queue... + */ + ErlNifMutex* accMtx; + SocketAcceptor acceptor; + /* We need to keep track of the "request(s)" we have pending. * If for instance an accept takes to long, the issuer may @@ -485,9 +508,6 @@ static ERL_NIF_TERM nif_listen(ErlNifEnv* env, static ERL_NIF_TERM nif_accept(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM nif_accept4(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -556,11 +576,26 @@ static ERL_NIF_TERM nconnect(ErlNifEnv* env, static ERL_NIF_TERM nlisten(ErlNifEnv* env, SocketDescriptor* descP, int backlog); +static ERL_NIF_TERM naccept_listening(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref); static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP); static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err); +static SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event); + +static int compare_pids(ErlNifEnv* env, + const ErlNifPid* pid1, + const ErlNifPid* pid2); + static BOOLEAN_T edomain2domain(int edomain, int* domain); static BOOLEAN_T etype2type(int etype, int* type); static BOOLEAN_T eproto2proto(int eproto, int* proto); @@ -636,6 +671,7 @@ static char str_einval[] = "einval"; static char str_eisconn[] = "eisconn"; static char str_eisnconn[] = "eisnconn"; static char str_exbadstate[] = "exbadstate"; +static char str_exbusy[] = "exbusy"; static char str_exmon[] = "exmonitor"; // failed monitor static char str_exself[] = "exself"; // failed self @@ -653,6 +689,7 @@ static ERL_NIF_TERM atom_einval; static ERL_NIF_TERM atom_eisconn; static ERL_NIF_TERM atom_eisnconn; static ERL_NIF_TERM atom_exbadstate; +static ERL_NIF_TERM atom_exbusy; static ERL_NIF_TERM atom_exmon; static ERL_NIF_TERM atom_exself; @@ -686,7 +723,6 @@ static SocketData socketData; * nif_connect(Sock, Addr, Port) * nif_listen(Sock, Backlog) * nif_accept(LSock, Ref) - * nif_accept4(LSock, Ref) * nif_send(Sock, Data, Flags) * nif_sendto(Sock, Data, Flags, DstAddr, DstPort) * nif_recv(Sock, Flags) @@ -835,7 +871,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, if ((event = sock_create_event(sock)) == INVALID_EVENT) { save_errno = sock_errno(); - while ((close(sock) == INVALID_SOCKET) && (sock_errno() == EINTR)); + while ((sock_close(sock) == INVALID_SOCKET) && (sock_errno() == EINTR)); return make_error2(env, save_errno); } @@ -844,31 +880,16 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, /* Create and initiate the socket "descriptor" */ - descP = enif_alloc_resource(sockets, sizeof(SocketDescriptor)); - { - char buf[64]; /* Buffer used for building the mutex name */ - sprintf(buf, "socket[w,%d]", sock); - descP->writeMtx = MCREATE(buf); - sprintf(buf, "socket[r,%d]", sock); - descP->readMtx = MCREATE(buf); + if ((descP = alloc_descriptor(sock, event)) == NULL) { + sock_close(sock); + // Not sure if this is really the proper error, but... + return enif_make_badarg(env); } - descP->isWritable = TRUE; - descP->isReadable = TRUE; - descP->writePkgCnt = 0; - descP->writeByteCnt = 0; - descP->writeTries = 0; - descP->writeWaits = 0; - descP->readPkgCnt = 0; - descP->readByteCnt = 0; - descP->readTries = 0; - descP->readWaits = 0; - descP->dbg = SOCKET_DEBUG_DEFAULT; - descP->state = SOCKET_STATE_OPEN; - descP->domain = domain; - descP->type = type; - descP->protocol = protocol; - descP->sock = sock; - descP->event = event; + + descP->state = SOCKET_STATE_OPEN; + descP->domain = domain; + descP->type = type; + descP->protocol = protocol; res = enif_make_resource(env, descP); enif_release_resource(descP); // We should really store a reference ... @@ -1668,6 +1689,351 @@ ERL_NIF_TERM nlisten(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * nif_accept + * + * Description: + * Accept a connection on a socket. + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * Request ref - Unique "id" of this request + * (used for the select, if none is in queue). + */ +static +ERL_NIF_TERM nif_accept(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + ERL_NIF_TERM ref; + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 2) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + ref = argv[1]; + + return naccept(env, descP, ref); +} + + +static +ERL_NIF_TERM naccept(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref) +{ + ERL_NIF_TERM res; + + switch (descP->state) { + case SOCKET_STATE_LISTENING: + MLOCK(descP->accMtx); + res = naccept_listening(env, descP, ref); + MUNLOCK(descP->accMtx); + break; + + case SOCKET_STATE_ACCEPTING: + MLOCK(descP->accMtx); + res = naccept_accepting(env, descP, ref); + MUNLOCK(descP->accMtx); + break; + + default: + res = make_error(env, atom_einval); + break; + } + + return res; +} + + +/* *** naccept_listening *** + * We have no active acceptor and no acceptors in queue. + */ +static +ERL_NIF_TERM naccept_listening(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref) +{ + SocketAddress remote; + unsigned int n; + SOCKET accSock; + HANDLE accEvent; + int save_errno; + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) + return make_error(env, atom_exself); + + n = sizeof(descP->remote.u); + sys_memzero((char *) &remote, n); + accSock = sock_accept(descP->sock, (struct sockaddr*) &remote, &n); + if (accSock == INVALID_SOCKET) { + save_errno = sock_errno(); + if (save_errno == ERRNO_BLOCK) { + + /* *** Try again later *** */ + + descP->acceptor.pid = caller; + if (enif_monitor_process(env, descP, + &descP->acceptor.pid, + &descP->acceptor.mon) > 0) + return make_error(env, atom_exmon); + + descP->acceptor.ref = ref; + + enif_select(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, NULL, ref); + + /* Shall we really change state? + * The ready event is sent directly to the calling + * process, which simply calls this function again. + * Basically, state accepting means that we have + * an "outstanding" accept. + * Shall we store the pid of the calling process? + * And if someone else calls accept, return with ebusy? + * Can any process call accept or just the controlling + * process? + * We also need a monitor it case the calling process is + * called before we are done! + * + * Change state (to accepting) and store pid of the acceptor + * (current process). Only accept calls from the acceptor + * process (ebusy) and once we have a successful accept, + * change state back to listening. If cancel is called instead + * (only accepted from the acceptor process), we reset + * state to listening and also resets the pid to "null" + * (is there such a value?). + * Need a mutex to secure that we don't test and change the + * pid at the same time. + */ + + descP->state = SOCKET_STATE_ACCEPTING; + + return make_error(env, atom_eagain); + + } else { + return make_error2(env, save_errno); + } + + } else { + SocketDescriptor* accDescP; + ERL_NIF_TERM accRef; + + /* + * We got one + */ + + if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) { + save_errno = sock_errno(); + while ((sock_close(accSock) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + return make_error2(env, save_errno); + } + + if ((accDescP = alloc_descriptor(accSock, accEvent)) == NULL) { + sock_close(accSock); + return enif_make_badarg(env); + } + + accDescP->domain = descP->domain; + accDescP->type = descP->type; + accDescP->protocol = descP->protocol; + + accRef = enif_make_resource(env, accDescP); + enif_release_resource(accDescP); // We should really store a reference ... + + accDescP->ctrlPid = caller; + if (enif_monitor_process(env, accDescP, + &accDescP->ctrlPid, + &accDescP->ctrlMon) > 0) { + sock_close(accSock); + return make_error(env, atom_exmon); + } + + accDescP->remote = remote; + SET_NONBLOCKING(accDescP->sock); + +#ifdef __WIN32__ + /* See 'What is the point of this?' above */ + enif_select(env, + (ERL_NIF_SELECT_READ), + descP, NULL, atom_undefined); +#endif + + accDescP->state = SOCKET_STATE_CONNECTED; + + return make_ok(env, accRef); + } +} + + +/* *** naccept_accepting *** + * We have an active acceptor and possibly acceptors waiting in queue. + * At the moment the queue is *not* implemented. + */ +static +ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref) +{ + SocketAddress remote; + unsigned int n; + SOCKET accSock; + HANDLE accEvent; + ErlNifPid caller; + int save_errno; + + if (enif_self(env, &caller) == NULL) + return make_error(env, atom_exself); + + if (compare_pids(env, &descP->acceptor.pid, &caller) != 0) { + /* This will have to do until we implement the queue. + * When we have the queue, we should simply push this request, + * and instead return with eagain (the caller will then wait + * for the select message). + */ + return make_error(env, atom_exbusy); + } + + n = sizeof(descP->remote.u); + sys_memzero((char *) &remote, n); + accSock = sock_accept(descP->sock, (struct sockaddr*) &remote, &n); + if (accSock == INVALID_SOCKET) { + save_errno = sock_errno(); + if (save_errno == ERRNO_BLOCK) { + + /* + * Just try again, no real error, just a ghost trigger from poll, + */ + + enif_select(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, NULL, ref); + + return make_error(env, atom_eagain); + } else { + return make_error2(env, save_errno); + } + } else { + SocketDescriptor* accDescP; + ERL_NIF_TERM accRef; + + /* + * We got one + */ + + if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) { + save_errno = sock_errno(); + while ((sock_close(accSock) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + return make_error2(env, save_errno); + } + + if ((accDescP = alloc_descriptor(accSock, accEvent)) == NULL) { + sock_close(accSock); + return enif_make_badarg(env); + } + + accDescP->domain = descP->domain; + accDescP->type = descP->type; + accDescP->protocol = descP->protocol; + + accRef = enif_make_resource(env, accDescP); + enif_release_resource(accDescP); // We should really store a reference ... + + accDescP->ctrlPid = caller; + if (enif_monitor_process(env, accDescP, + &accDescP->ctrlPid, + &accDescP->ctrlMon) > 0) { + sock_close(accSock); + return make_error(env, atom_exmon); + } + + accDescP->remote = remote; + SET_NONBLOCKING(accDescP->sock); + +#ifdef __WIN32__ + /* See 'What is the point of this?' above */ + enif_select(env, + (ERL_NIF_SELECT_READ), + descP, NULL, atom_undefined); +#endif + + accDescP->state = SOCKET_STATE_CONNECTED; + + + /* Here we should have the test if we have something in the queue. + * And if so, pop it and copy the (waiting) acceptor, and then + * make a new select with that info). + */ + descP->state = SOCKET_STATE_LISTENING; + + return make_ok(env, accRef); + } +} + + + +/* *** alloc_descriptor *** + * Allocate and perform basic initialization of a socket descriptor. + * + */ +static +SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) +{ + SocketDescriptor* descP; + + if ((descP = enif_alloc_resource(sockets, sizeof(SocketDescriptor))) != NULL) { + char buf[64]; /* Buffer used for building the mutex name */ + + sprintf(buf, "socket[w,%d]", sock); + descP->writeMtx = MCREATE(buf); + + sprintf(buf, "socket[r,%d]", sock); + descP->readMtx = MCREATE(buf); + + sprintf(buf, "socket[acc,%d]", sock); + descP->accMtx = MCREATE(buf); + + descP->dbg = SOCKET_DEBUG_DEFAULT; + descP->isWritable = TRUE; + descP->isReadable = TRUE; + descP->writePkgCnt = 0; + descP->writeByteCnt = 0; + descP->writeTries = 0; + descP->writeWaits = 0; + descP->readPkgCnt = 0; + descP->readByteCnt = 0; + descP->readTries = 0; + descP->readWaits = 0; + + descP->sock = sock; + descP->event = event; + + } + + return descP; +} + + +static +int compare_pids(ErlNifEnv* env, + const ErlNifPid* pid1, + const ErlNifPid* pid2) +{ + ERL_NIF_TERM p1 = enif_make_pid(env, pid1); + ERL_NIF_TERM p2 = enif_make_pid(env, pid2); + + return enif_is_identical(p1, p2); +} + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -1931,7 +2297,6 @@ ErlNifFunc socket_funcs[] = {"nif_connect", 3, nif_connect}, {"nif_listen", 2, nif_listen}, {"nif_accept", 2, nif_accept}, - {"nif_accept4", 3, nif_accept4}, {"nif_send", 3, nif_send}, {"nif_sendto", 5, nif_sendto}, {"nif_recv", 2, nif_recv}, @@ -2035,6 +2400,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_eisnconn = MKA(env, str_eisnconn); // atom_exalloc = MKA(env, str_exalloc); atom_exbadstate = MKA(env, str_exbadstate); + atom_exbusy = MKA(env, str_exbusy); // atom_exnotopen = MKA(env, str_exnotopen); atom_exmon = MKA(env, str_exmon); atom_exself = MKA(env, str_exself); -- cgit v1.2.3 From 2d57ebfc6fb723a476fdcffbb366558a6fa18844 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Apr 2018 18:24:00 +0200 Subject: [socket-nif] Completed send We still need to handle simultaneous ops. That is, handle if two different procs tries to send at the same time. Or a recv and send at the same time. Ops queue? --- erts/emulator/nifs/common/socket_nif.c | 387 +++++++++++++++++++++++++++++---- 1 file changed, 345 insertions(+), 42 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 3e8fe7061a..bf9179d857 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -231,6 +231,9 @@ typedef unsigned long long llu_t; #define MCREATE(N) enif_mutex_create(N) #define MLOCK(M) enif_mutex_lock(M) #define MUNLOCK(M) enif_mutex_unlock(M) +#define SELECT(E,FD,M,O,P,R) \ + if (enif_select((E), (FD), (M), (O), (P), (R)) < 0) \ + return enif_make_badarg((E)); /* *** Socket state defs *** */ @@ -262,6 +265,39 @@ typedef unsigned long long llu_t; (((d)->state & SOCKET_FLAG_BUSY) == SOCKET_FLAG_BUSY) +#define SOCKET_SEND_FLAG_CONFIRM 0 +#define SOCKET_SEND_FLAG_DONTROUTE 1 +#define SOCKET_SEND_FLAG_DONTWAIT 2 +#define SOCKET_SEND_FLAG_EOR 3 +#define SOCKET_SEND_FLAG_MORE 4 +#define SOCKET_SEND_FLAG_NOSIGNAL 5 +#define SOCKET_SEND_FLAG_OOB 6 +#define SOCKET_SEND_FLAG_LOW SOCKET_SEND_FLAG_CONFIRM +#define SOCKET_SEND_FLAG_HIGH SOCKET_SEND_FLAG_OOB + +typedef union { + struct { + unsigned int open:1; + // 0 = not conn, 1 = connecting, 2 = connected + unsigned int connect:2; + // unsigned int connecting:1; + // unsigned int connected:1; + // 0 = not listen, 1 = listening, 2 = accepting + unsigned int listen:2; + // unsigned int listening:1; + // unsigned int accepting:1; + /* Room for more... */ + } flags; + unsigned int field; // Make it easy to reset all flags... +} SocketState; + +/* +#define IS_OPEN(d) ((d)->state.flags.open) +#define IS_CONNECTED(d) ((d)->state.flags.connect == SOCKET_STATE_CONNECTED) +#define IS_CONNECTING(d) ((d)->state.flags.connect == SOCKET_STATE_CONNECTING) +*/ + + /*---------------------------------------------------------------------------- * Interface constants. * @@ -329,6 +365,7 @@ typedef unsigned long long llu_t; #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_open(domain, type, proto) \ make_noninheritable_handle(socket((domain), (type), (proto))) +#define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) #define sock_errno() WSAGetLastError() #define sock_create_event(s) WSACreateEvent() @@ -355,6 +392,7 @@ static unsigned long one_value = 1; #define sock_listen(s, b) listen((s), (b)) #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_open(domain, type, proto) socket((domain), (type), (proto)) +#define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag)) #define sock_errno() errno #define sock_create_event(s) (s) /* return file descriptor */ @@ -420,20 +458,21 @@ typedef struct { // Write ErlNifMutex* writeMtx; BOOLEAN_T isWritable; - unsigned int writePkgCnt; - unsigned int writeByteCnt; - unsigned int writeTries; - unsigned int writeWaits; + uint32_t writePkgCnt; + uint32_t writeByteCnt; + uint32_t writeTries; + uint32_t writeWaits; + uint32_t writeFails; // Read ErlNifMutex* readMtx; BOOLEAN_T isReadable; ErlNifBinary rbuffer; - unsigned int readCapacity; - unsigned int readPkgCnt; - unsigned int readByteCnt; - unsigned int readTries; - unsigned int readWaits; + uint32_t readCapacity; + uint32_t readPkgCnt; + uint32_t readByteCnt; + uint32_t readTries; + uint32_t readWaits; /* Accept * We also need a queue for waiting acceptors... @@ -585,6 +624,11 @@ static ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, static ERL_NIF_TERM naccept(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM ref); +static ERL_NIF_TERM nsend(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sendRef, + ErlNifBinary* dataP, + int flags); static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP); @@ -599,12 +643,14 @@ static int compare_pids(ErlNifEnv* env, static BOOLEAN_T edomain2domain(int edomain, int* domain); static BOOLEAN_T etype2type(int etype, int* type); static BOOLEAN_T eproto2proto(int eproto, int* proto); +static BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags); #ifdef HAVE_SETNS static BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns); static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err); static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err); #endif +static BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc); static void socket_dtor(ErlNifEnv* env, void* obj); static void socket_stop(ErlNifEnv* env, @@ -669,7 +715,7 @@ static char str_eagain[] = "eagain"; static char str_eafnosupport[] = "eafnosupport"; static char str_einval[] = "einval"; static char str_eisconn[] = "eisconn"; -static char str_eisnconn[] = "eisnconn"; +static char str_enotconn[] = "enotconn"; static char str_exbadstate[] = "exbadstate"; static char str_exbusy[] = "exbusy"; static char str_exmon[] = "exmonitor"; // failed monitor @@ -687,7 +733,7 @@ static ERL_NIF_TERM atom_eagain; static ERL_NIF_TERM atom_eafnosupport; static ERL_NIF_TERM atom_einval; static ERL_NIF_TERM atom_eisconn; -static ERL_NIF_TERM atom_eisnconn; +static ERL_NIF_TERM atom_enotconn; static ERL_NIF_TERM atom_exbadstate; static ERL_NIF_TERM atom_exbusy; static ERL_NIF_TERM atom_exmon; @@ -917,10 +963,10 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, * * TODO! */ - enif_select(env, - event, - ERL_NIF_SELECT_READ, - descP, NULL, atom_undefined); + SELECT(env, + event, + ERL_NIF_SELECT_READ, + descP, NULL, atom_undefined); #endif @@ -1516,10 +1562,10 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, (sock_errno() == EINPROGRESS))) { /* Unix & OSE!! */ ERL_NIF_TERM ref = MKREF(env); descP->state = SOCKET_STATE_CONNECTING; - enif_select(env, - descP->sock, - (ERL_NIF_SELECT_WRITE), - descP, NULL, ref); + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_WRITE), + descP, NULL, ref); return make_ok(env, ref); } else if (code == 0) { /* ok we are connected */ descP->state = SOCKET_STATE_CONNECTED; @@ -1572,7 +1618,7 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, int error; if (descP->state != SOCKET_STATE_CONNECTING) - return make_error(env, atom_eisnconn); + return make_error(env, atom_enotconn); if (!verify_is_connected(descP, &error)) { descP->state = SOCKET_STATE_OPEN; /* restore state */ @@ -1784,10 +1830,10 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, descP->acceptor.ref = ref; - enif_select(env, - descP->sock, - (ERL_NIF_SELECT_READ), - descP, NULL, ref); + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, NULL, ref); /* Shall we really change state? * The ready event is sent directly to the calling @@ -1860,9 +1906,10 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, #ifdef __WIN32__ /* See 'What is the point of this?' above */ - enif_select(env, - (ERL_NIF_SELECT_READ), - descP, NULL, atom_undefined); + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, NULL, atom_undefined); #endif accDescP->state = SOCKET_STATE_CONNECTED; @@ -1911,10 +1958,10 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, * Just try again, no real error, just a ghost trigger from poll, */ - enif_select(env, - descP->sock, - (ERL_NIF_SELECT_READ), - descP, NULL, ref); + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, NULL, ref); return make_error(env, atom_eagain); } else { @@ -1960,9 +2007,10 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, #ifdef __WIN32__ /* See 'What is the point of this?' above */ - enif_select(env, - (ERL_NIF_SELECT_READ), - descP, NULL, atom_undefined); + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, NULL, atom_undefined); #endif accDescP->state = SOCKET_STATE_CONNECTED; @@ -2008,6 +2056,7 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->writeByteCnt = 0; descP->writeTries = 0; descP->writeWaits = 0; + descP->writeFails = 0; descP->readPkgCnt = 0; descP->readByteCnt = 0; descP->readTries = 0; @@ -2022,6 +2071,190 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) } + +/* ---------------------------------------------------------------------- + * nif_send + * + * Description: + * Send a message on a socket + * + * Arguments: + * 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. + */ + +static +ERL_NIF_TERM nif_send(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + ERL_NIF_TERM sendRef; + ErlNifBinary data; + unsigned int eflags; + int flags; + ERL_NIF_TERM res; + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 4) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP) || + !enif_inspect_iolist_as_binary(env, argv[2], &data) || + !enif_get_uint(env, argv[3], &eflags)) { + return enif_make_badarg(env); + } + sendRef = argv[1]; + + if (!IS_CONNECTED(descP)) + return make_error(env, atom_enotconn); + + if (!esendflags2sendflags(eflags, &flags)) + return enif_make_badarg(env); + + MLOCK(descP->writeMtx); + + res = nsend(env, descP, sendRef, &data, flags); + + MUNLOCK(descP->writeMtx); + + return res; +} + + +/* What do we do when another process tries to write + * when the current writer has a select already waiting? + * Queue it? And what about simultaneous read and write? + * Queue up all operations towards the socket? + * + * We (may) need a currentOp field and an ops queue field. + */ +static +ERL_NIF_TERM nsend(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sendRef, + ErlNifBinary* dataP, + int flags) +{ + int save_errno; + ssize_t written; + + if (!descP->isWritable) + return enif_make_badarg(env); + + /* We ignore the wrap for the moment. + * Maybe we should issue a wrap-message to controlling process... + */ + cnt_inc(&descP->writeTries, 1); + + written = sock_send(descP->sock, dataP->data, dataP->size, flags); + + if (written == dataP->size) { + + cnt_inc(&descP->writePkgCnt, 1); + cnt_inc(&descP->writeByteCnt, written); + + return atom_ok; + + } else if (written < 0) { + + /* Ouch, check what kind of failure */ + save_errno = sock_errno(); + if ((save_errno != EAGAIN) && + (save_errno != EINTR)) { + + cnt_inc(&descP->writeFails, 1); + + return make_error2(env, save_errno); + + } else { + + /* Ok, try again later */ + + written = 0; + + } + } + + /* We failed to write the *entire* packet (anything less then size + * of the packet, which is 0 <= written < sizeof packet), + * so schedule the rest for later. + */ + + cnt_inc(&descP->writeWaits, 1); + + SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), + descP, NULL, sendRef); + + return make_ok(env, enif_make_int(env, written)); + +} + + +#ifdef FOBAR +static +ERL_NIF_TERM nwritev(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sendRef, + ERL_NIF_TERM data) +{ + ERL_NIF_TERM tail; + ErlNifIOVec vec; + ErlNifIOVec* iovec = &vec; + SysIOVec* sysiovec; + int save_errno; + int iovcnt, n; + + if (!enif_inspect_iovec(env, MAX_VSZ, data, &tail, &iovec)) + return enif_make_badarg(env); + + if (enif_ioq_size(descP->outQ) > 0) { + /* If the I/O queue contains data we enqueue the iovec + * and then peek the data to write out of the queue. + */ + if (!enif_ioq_enqv(q, iovec, 0)) + return -3; + + sysiovec = enif_ioq_peek(descP->outQ, &iovcnt); + + } else { + /* If the I/O queue is empty we skip the trip through it. */ + iovcnt = iovec->iovcnt; + sysiovec = iovec->iov; + } + + /* Attempt to write the data */ + n = writev(fd, sysiovec, iovcnt); + saved_errno = errno; + + if (enif_ioq_size(descP->outQ) == 0) { + /* If the I/O queue was initially empty we enqueue any + remaining data into the queue for writing later. */ + if (n >= 0 && !enif_ioq_enqv(descP->outQ, iovec, n)) + return -3; + } else { + /* Dequeue any data that was written from the queue. */ + if (n > 0 && !enif_ioq_deq(descP->outQ, n, NULL)) + return -4; + } + /* return n, which is either number of bytes written or -1 if + some error happened */ + errno = saved_errno; + return n; +} +#endif + + + +/* ---------------------------------------------------------------------- + * U t i l i t y F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* compare_pids - Test if two pids are equal + * + */ static int compare_pids(ErlNifEnv* env, const ErlNifPid* pid1, @@ -2034,11 +2267,6 @@ int compare_pids(ErlNifEnv* env, } -/* ---------------------------------------------------------------------- - * U t i l i t y F u n c t i o n s - * ---------------------------------------------------------------------- - */ - /* edomain2domain - convert internal (erlang) domain to (proper) domain * * Note that only a subset is supported. @@ -2070,7 +2298,6 @@ BOOLEAN_T edomain2domain(int edomain, int* domain) } - /* etype2type - convert internal (erlang) type to (proper) type * * Note that only a subset is supported. @@ -2103,7 +2330,6 @@ BOOLEAN_T etype2type(int etype, int* type) } - /* eproto2proto - convert internal (erlang) protocol to (proper) protocol * * Note that only a subset is supported. @@ -2193,6 +2419,58 @@ BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns) #endif +/* esendflags2sendflags - convert internal (erlang) send flags to (proper) + * send flags. + */ +static +BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags) +{ + unsigned int ef; + int tmp = 0; + + for (ef = SOCKET_SEND_FLAG_LOW; ef <= SOCKET_SEND_FLAG_HIGH; ef++) { + switch (ef) { + case SOCKET_SEND_FLAG_CONFIRM: + tmp |= MSG_CONFIRM; + break; + + case SOCKET_SEND_FLAG_DONTROUTE: + tmp |= MSG_DONTROUTE; + break; + + case SOCKET_SEND_FLAG_DONTWAIT: + tmp |= MSG_DONTWAIT; + break; + + case SOCKET_SEND_FLAG_EOR: + tmp |= MSG_EOR; + break; + + case SOCKET_SEND_FLAG_MORE: + tmp |= MSG_MORE; + break; + + case SOCKET_SEND_FLAG_NOSIGNAL: + tmp |= MSG_NOSIGNAL; + break; + + case SOCKET_SEND_FLAG_OOB: + tmp |= MSG_OOB; + break; + + default: + return FALSE; + } + + } + + *sendflags = tmp; + + return TRUE; +} + + + /* Create an ok two (2) tuple in the form: {ok, Any}. * The second element (Any) is already in the form of an * ERL_NIF_TERM so all we have to do is create the tuple. @@ -2239,6 +2517,31 @@ ERL_NIF_TERM make_error2(ErlNifEnv* env, int err) +/* ---------------------------------------------------------------------- + * C o u n t e r F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +static +BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc) +{ + BOOLEAN_T wrap; + uint32_t max = 0xFFFFFFFF; + uint32_t current = *cnt; + + if ((max - inc) >= current) { + *cnt += inc; + wrap = FALSE; + } else { + *cnt = inc - (max - current) - 1; + wrap = TRUE; + } + + return (wrap); +} + + + /* ---------------------------------------------------------------------- * C a l l b a c k F u n c t i o n s * ---------------------------------------------------------------------- @@ -2397,7 +2700,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_eafnosupport = MKA(env, str_eafnosupport); atom_einval = MKA(env, str_einval); atom_eisconn = MKA(env, str_eisconn); - atom_eisnconn = MKA(env, str_eisnconn); + atom_enotconn = MKA(env, str_enotconn); // atom_exalloc = MKA(env, str_exalloc); atom_exbadstate = MKA(env, str_exbadstate); atom_exbusy = MKA(env, str_exbusy); -- cgit v1.2.3 From 5920705deb70a44311e1b7552cfa73553f284164 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 Apr 2018 20:50:42 +0200 Subject: [socket-nif] Implemented sendto Still not handling queue'ing of multiple send requests. --- erts/emulator/nifs/common/socket_nif.c | 777 ++++++++++++++++++++++----------- 1 file changed, 531 insertions(+), 246 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index bf9179d857..46c5c696e2 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -340,6 +340,7 @@ typedef union { enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) #define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) #define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) +#define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) @@ -366,6 +367,8 @@ typedef union { #define sock_open(domain, type, proto) \ make_noninheritable_handle(socket((domain), (type), (proto))) #define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) +#define sock_sendto(s,buf,blen,flag,addr,alen) \ + sendto((s),(buf),(blen),(flag),(addr),(alen)) #define sock_errno() WSAGetLastError() #define sock_create_event(s) WSACreateEvent() @@ -393,6 +396,8 @@ static unsigned long one_value = 1; #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_open(domain, type, proto) socket((domain), (type), (proto)) #define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag)) +#define sock_sendto(s,buf,blen,flag,addr,alen) \ + sendto((s),(buf),(blen),(flag),(addr),(alen)) #define sock_errno() errno #define sock_create_event(s) (s) /* return file descriptor */ @@ -510,6 +515,39 @@ typedef struct { } SocketData; +/* typedef struct socket_queue_element { */ +/* struct socket_queue_element* next; */ +/* /\* */ +/* unsigned int tag; */ +/* union { */ +/* SocketAcceptor acceptor; */ +/* } u; */ +/* *\/ */ +/* SocketAcceptor acceptor; */ +/* } SocketQueueElement; */ + +/* typedef struct socket_queue { */ +/* SocketQueueElement* first; */ +/* SocketQueueElement* last; */ +/* } SocketQueue; */ + +/* Macros for defining the various queues (accept, send receive) */ +#define SOCKET_QUEUE_ELEMENT(QE,QEP) \ + typedef struct socket_queue_element_##QEP { \ + struct socket_queue_element_##QEP* next; \ + QE elem; \ + } QE##Element; + +#define SOCKET_QUEUE(QE,Q) \ + typedef struct { \ + QE* first; \ + QE* last; \ + } Q; + +/* The Acceptor Queue types */ +SOCKET_QUEUE_ELEMENT(SocketAcceptor, acceptor); +SOCKET_QUEUE(SocketAcceptorElement, SocketAcceptQueue); + /* ---------------------------------------------------------------------- * F o r w a r d s * ---------------------------------------------------------------------- @@ -576,30 +614,6 @@ static ERL_NIF_TERM nif_cancel(ErlNifEnv* env, const ERL_NIF_TERM argv[]); -static char* decode_laddress(ErlNifEnv* env, - int domain, - ERL_NIF_TERM localAddr, - SocketAddress* localP); -static char* decode_laddress_binary(ErlNifEnv* env, - int domain, - ERL_NIF_TERM localAddr, - SocketAddress* localP); -static char* decode_laddress_tuple(ErlNifEnv* env, - int domain, - ERL_NIF_TERM laddr, - SocketAddress* localP); -static char* decode_address_tuple(ErlNifEnv* env, - int domain, - const ERL_NIF_TERM* addrt, - int port, - SocketAddress* localP); -static char* decode_address_atom(ErlNifEnv* env, - int domain, - char* addr, - int addrLen, - int port, - SocketAddress* localP); - static ERL_NIF_TERM nopen(ErlNifEnv* env, int domain, int type, @@ -629,9 +643,56 @@ static ERL_NIF_TERM nsend(ErlNifEnv* env, ERL_NIF_TERM sendRef, ErlNifBinary* dataP, int flags); +static ERL_NIF_TERM nsendto(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sendRef, + ErlNifBinary* dataP, + int flags, + SocketAddress* toAddrP); + +static ERL_NIF_TERM send_check_result(ErlNifEnv* env, + SocketDescriptor* descP, + ssize_t written, + ssize_t dataSize, + ERL_NIF_TERM sendRef); + static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP); + +static char* decode_laddress(ErlNifEnv* env, + int domain, + ERL_NIF_TERM localAddr, + SocketAddress* localP); +static char* decode_laddress_binary(ErlNifEnv* env, + int domain, + ERL_NIF_TERM localAddr, + SocketAddress* localP); +static char* decode_laddress_tuple(ErlNifEnv* env, + int domain, + ERL_NIF_TERM laddr, + SocketAddress* localP); +static char* decode_address_tuple(ErlNifEnv* env, + int domain, + const ERL_NIF_TERM* addrt, + int port, + SocketAddress* localP); +static char* decode_address_atom(ErlNifEnv* env, + int domain, + char* addr, + int addrLen, + int port, + SocketAddress* localP); +static char* decode_send_addr(ErlNifEnv* env, + int domain, + ERL_NIF_TERM addr, + int port, + SocketAddress** toAddrP); +static char* decode_send_addr_tuple(ErlNifEnv* env, + int domain, + ERL_NIF_TERM addr, + int port, + SocketAddress* toAddrP); static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err); static SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event); @@ -640,6 +701,8 @@ static int compare_pids(ErlNifEnv* env, const ErlNifPid* pid1, const ErlNifPid* pid2); + + static BOOLEAN_T edomain2domain(int edomain, int* domain); static BOOLEAN_T etype2type(int etype, int* type); static BOOLEAN_T eproto2proto(int eproto, int* proto); @@ -965,7 +1028,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, */ SELECT(env, event, - ERL_NIF_SELECT_READ, + (ERL_NIF_SELECT_READ), descP, NULL, atom_undefined); #endif @@ -1115,6 +1178,7 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, /* Make sure we are ready * Not sure how this would even happen, but... */ + /* WHY NOT !IS_OPEN(...) */ if (descP->state != SOCKET_STATE_OPEN) return make_error(env, atom_exbadstate); @@ -1308,7 +1372,7 @@ char* decode_laddress_tuple(ErlNifEnv* env, unsigned int len; char a[16]; // Just in case... - if (!(GET_ATOM_LEN(env, laddrt[1], &len) && + if (!(GET_ATOM_LEN(env, laddrt[0], &len) && (len > 0) && (len <= (sizeof("loopback"))))) return str_einval; @@ -1327,154 +1391,6 @@ char* decode_laddress_tuple(ErlNifEnv* env, } -/* Decode the 4- or 8-element address tuple - * and initiate the socket address structure. - */ -static -char* decode_address_tuple(ErlNifEnv* env, - int domain, - const ERL_NIF_TERM* addrt, - int port, - SocketAddress* addrP) -{ - - /* We now *know* that the size of the tuple is correct, - * so we don't need to check anything here, just unpack. - */ - - switch (domain) { - case AF_INET: - { - int a, v; - char laddr[4]; - - sys_memzero((char*)&addrP->u, sizeof(struct sockaddr_in)); -#ifndef NO_SA_LEN - addrP->u.sai.sin_len = sizeof(struct sockaddr_in); -#endif - addrP->u.sai.sin_family = domain; - addrP->u.sai.sin_port = sock_htons(port); - for (a = 0; a < 4; a++) { - if (!GET_INT(env, addrt[a], &v)) - return str_einval; - laddr[a] = v; - } - sys_memcpy(&addrP->u.sai.sin_addr, &laddr, sizeof(laddr)); - addrP->len = sizeof(struct sockaddr_in); - return NULL; - } - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - { - int a, v; - char laddr[16]; - - sys_memzero((char*)&addrP->u, sizeof(struct sockaddr_in6)); -#ifndef NO_SA_LEN - addrP->u.sai6.sin6_len = sizeof(struct sockaddr_in6); -#endif - addrP->u.sai6.sin6_family = domain; - addrP->u.sai6.sin6_port = sock_htons(port); - addrP->u.sai6.sin6_flowinfo = 0; - /* 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, addrt[a], &v)) - return str_einval; - laddr[a*2 ] = ((v >> 8) & 0xFF); - laddr[a*2+1] = (v & 0xFF); - } - sys_memcpy(&addrP->u.sai6.sin6_addr, &laddr, sizeof(laddr)); - addrP->len = sizeof(struct sockaddr_in6); - return NULL; - } - break; -#endif - - } /* switch (domain) */ - - return str_eafnosupport; - -} - - -/* Decode the address when its an atom. - * Currently we only accept two atoms: 'any' and 'loopback' - */ -static -char* decode_address_atom(ErlNifEnv* env, - int domain, - char* addr, - int addrLen, - int port, - SocketAddress* localP) -{ - BOOLEAN_T any; - - if (strncmp(addr, "any", addrLen) == 0) { - any = TRUE; - } if (strncmp(addr, "loopback", addrLen) == 0) { - any = FALSE; - } else { - return str_einval; - } - - /* If we get this far, we *know* its either 'any' or 'loopback' */ - - switch (domain) { - case AF_INET: - { - struct in_addr addr; - if (any) { - addr.s_addr = sock_htonl(INADDR_ANY); - } else { - addr.s_addr = sock_htonl(INADDR_LOOPBACK); - } - sys_memzero((char*) localP, sizeof(struct sockaddr_in)); -#ifndef NO_SA_LEN - localP->u.sai.sin_len = sizeof(struct sockaddr_in6); -#endif - localP->u.sai.sin_family = domain; - localP->u.sai.sin_port = sock_htons(port); - localP->u.sai.sin_addr.s_addr = addr.s_addr; - localP->len = sizeof(struct sockaddr_in); - } - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - { - const struct in6_addr* paddr; - if (any) { - paddr = &in6addr_any; - } else { - paddr = &in6addr_loopback; - } - sys_memzero((char*)localP, sizeof(struct sockaddr_in6)); -#ifndef NO_SA_LEN - localP->u.sai6.sin6_len = sizeof(struct sockaddr_in6); -#endif - localP->u.sai6.sin6_family = domain; - localP->u.sai6.sin6_port = sock_htons(port); - localP->u.sai6.sin6_flowinfo = 0; - localP->u.sai6.sin6_addr = *paddr; - localP->len = sizeof(struct sockaddr_in6); - } - break; -#endif - - default: - return str_einval; - break; - } - - return NULL; -} - - /* ---------------------------------------------------------------------- * nif_connect @@ -2028,50 +1944,6 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, -/* *** alloc_descriptor *** - * Allocate and perform basic initialization of a socket descriptor. - * - */ -static -SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) -{ - SocketDescriptor* descP; - - if ((descP = enif_alloc_resource(sockets, sizeof(SocketDescriptor))) != NULL) { - char buf[64]; /* Buffer used for building the mutex name */ - - sprintf(buf, "socket[w,%d]", sock); - descP->writeMtx = MCREATE(buf); - - sprintf(buf, "socket[r,%d]", sock); - descP->readMtx = MCREATE(buf); - - sprintf(buf, "socket[acc,%d]", sock); - descP->accMtx = MCREATE(buf); - - descP->dbg = SOCKET_DEBUG_DEFAULT; - descP->isWritable = TRUE; - descP->isReadable = TRUE; - descP->writePkgCnt = 0; - descP->writeByteCnt = 0; - descP->writeTries = 0; - descP->writeWaits = 0; - descP->writeFails = 0; - descP->readPkgCnt = 0; - descP->readByteCnt = 0; - descP->readTries = 0; - descP->readWaits = 0; - - descP->sock = sock; - descP->event = event; - - } - - return descP; -} - - - /* ---------------------------------------------------------------------- * nif_send * @@ -2080,7 +1952,7 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) * * Arguments: * Socket (ref) - Points to the socket descriptor. - * Sendref - A unique id for this (send) request. + * SendRef - A unique id for this (send) request. * Data - The data to send in the form of a IOVec. * Flags - Send flags. */ @@ -2101,8 +1973,8 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, if ((argc != 4) || !enif_get_resource(env, argv[0], sockets, (void**) &descP) || - !enif_inspect_iolist_as_binary(env, argv[2], &data) || - !enif_get_uint(env, argv[3], &eflags)) { + !GET_BIN(env, argv[2], &data) || + !GET_UINT(env, argv[3], &eflags)) { return enif_make_badarg(env); } sendRef = argv[1]; @@ -2115,6 +1987,19 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, MLOCK(descP->writeMtx); + /* We need to handle the case when another process tries + * to write at the same time. + * If the current write could not write its entire package + * this time (resulting in an select). The write of the + * other process must be made to wait until current + * is done! + * Basically, we need a write queue! + * + * A 'writing' field (boolean), which is set if we did + * not manage to write the entire message and reset every + * time we do. + */ + res = nsend(env, descP, sendRef, &data, flags); MUNLOCK(descP->writeMtx); @@ -2137,7 +2022,6 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, ErlNifBinary* dataP, int flags) { - int save_errno; ssize_t written; if (!descP->isWritable) @@ -2150,48 +2034,117 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, written = sock_send(descP->sock, dataP->data, dataP->size, flags); - if (written == dataP->size) { + return send_check_result(env, descP, written, dataP->size, sendRef); - cnt_inc(&descP->writePkgCnt, 1); - cnt_inc(&descP->writeByteCnt, written); +} - return atom_ok; - } else if (written < 0) { +/* ---------------------------------------------------------------------- + * nif_sendto + * + * Description: + * Send a message on a socket + * + * Arguments: + * 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. + * DestAddr - Destination address. + * DestPort - Destination Port. + */ - /* Ouch, check what kind of failure */ - save_errno = sock_errno(); - if ((save_errno != EAGAIN) && - (save_errno != EINTR)) { +static +ERL_NIF_TERM nif_sendto(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + ERL_NIF_TERM sendRef; + ErlNifBinary data; + unsigned int eflags; + int flags; + ERL_NIF_TERM addr; + int port; + SocketAddress remoteAddr; + SocketAddress* remoteAddrP = &remoteAddr; + char* xerr; + // ERL_NIF_TERM res; - cnt_inc(&descP->writeFails, 1); + /* Extract arguments and perform preliminary validation */ - return make_error2(env, save_errno); + 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)) { + return enif_make_badarg(env); + } + sendRef = argv[1]; + addr = argv[4]; - } else { + /* THIS TEST IS NOT CORRECT!!! */ + if (!IS_OPEN(descP)) + return make_error(env, atom_einval); - /* Ok, try again later */ + if (!esendflags2sendflags(eflags, &flags)) + return enif_make_badarg(env); - written = 0; + if ((xerr = decode_send_addr(env, descP->domain, + addr, port, + &remoteAddrP)) != NULL) + return make_error1(env, xerr); - } - } + return nsendto(env, descP, sendRef, &data, flags, remoteAddrP); +} - /* We failed to write the *entire* packet (anything less then size - * of the packet, which is 0 <= written < sizeof packet), - * so schedule the rest for later. - */ - cnt_inc(&descP->writeWaits, 1); +static +ERL_NIF_TERM nsendto(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sendRef, + ErlNifBinary* dataP, + int flags, + SocketAddress* toAddrP) +{ + ssize_t written; - SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), - descP, NULL, sendRef); + if (!descP->isWritable) + return enif_make_badarg(env); - return make_ok(env, enif_make_int(env, written)); + /* We ignore the wrap for the moment. + * Maybe we should issue a wrap-message to controlling process... + */ + cnt_inc(&descP->writeTries, 1); + + if (toAddrP != NULL) { + written = sock_sendto(descP->sock, + dataP->data, dataP->size, flags, + &toAddrP->u.sa, toAddrP->len); + } else { + written = sock_sendto(descP->sock, + dataP->data, dataP->size, flags, + NULL, 0); + } + return send_check_result(env, descP, written, dataP->size, sendRef); } + +/* ---------------------------------------------------------------------- + * nif_writev / nif_sendv + * + * Description: + * Send a message (vector) on a socket + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * SendRef - A unique id for this (send) request. + * Data - A vector of binaries + * Flags - Send flags. + */ + #ifdef FOBAR static ERL_NIF_TERM nwritev(ErlNifEnv* env, @@ -2247,11 +2200,343 @@ ERL_NIF_TERM nwritev(ErlNifEnv* env, + + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- */ +static +ERL_NIF_TERM send_check_result(ErlNifEnv* env, + SocketDescriptor* descP, + ssize_t written, + ssize_t dataSize, + ERL_NIF_TERM sendRef) +{ + if (written == dataSize) { + + cnt_inc(&descP->writePkgCnt, 1); + cnt_inc(&descP->writeByteCnt, written); + + return atom_ok; + + } else if (written < 0) { + + /* Ouch, check what kind of failure */ + int save_errno = sock_errno(); + if ((save_errno != EAGAIN) && + (save_errno != EINTR)) { + + cnt_inc(&descP->writeFails, 1); + + return make_error2(env, save_errno); + + } else { + + /* Ok, try again later */ + + written = 0; + + } + } + + /* We failed to write the *entire* packet (anything less then size + * of the packet, which is 0 <= written < sizeof packet), + * so schedule the rest for later. + */ + + cnt_inc(&descP->writeWaits, 1); + + SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), + descP, NULL, sendRef); + + return make_ok(env, enif_make_int(env, written)); + +} + + +/* The rather odd thing about the 'toAddrP' (the **) is + * because we need to be able to return a NULL pointer, + * in the case of the dest address is the atom 'null'. + * Its possible to call the sendto function with the + * args NULL (address) and 0 (port number). + * + * This function whouls really have a char* return value + * type!! + */ +static +char* decode_send_addr(ErlNifEnv* env, + int domain, + ERL_NIF_TERM addr, + int port, + SocketAddress** toAddrP) +{ + if (IS_ATOM(env, addr)) { + unsigned int len; + char a[16]; // Just in case... + + /* The only acceptable value is the atom 'null' */ + + if (!(GET_ATOM_LEN(env, addr, &len) && + (len > 0) && + (len <= (sizeof("null"))))) + return str_einval; + + if (!GET_ATOM(env, addr, a, sizeof(a))) + return str_einval; + + *toAddrP = NULL; + if (strncmp(a, "null", len) == 0) + return NULL; + else + return str_einval; + + } else if (IS_TUPLE(env, addr)) { + /* We now know that the we have a proper address. */ + return decode_send_addr_tuple(env, domain, addr, port, *toAddrP); + } else { + return str_einval; + } +} + + +static +char* decode_send_addr_tuple(ErlNifEnv* env, + int domain, + ERL_NIF_TERM addr, + int port, + SocketAddress* toAddrP) +{ + /* We handle two different tuples: + * - size 4 (INET) + * - size 8 (INET6) + */ + + const ERL_NIF_TERM* addrt; + int addrtSz; + + if (!GET_TUPLE(env, addr, &addrtSz, &addrt)) + return str_einval; // PLACEHOLDER + + switch (domain) { + case AF_INET: + if (addrtSz != 4) + return str_einval; + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + if (addrtSz != 8) + return str_einval; + break; +#endif + + default: + return str_eafnosupport; + break; + } + + return decode_address_tuple(env, domain, + addrt, port, + toAddrP); + +} + + +/* Decode the 4- or 8-element address tuple + * and initiate the socket address structure. + */ +static +char* decode_address_tuple(ErlNifEnv* env, + int domain, + const ERL_NIF_TERM* addrt, + int port, + SocketAddress* addrP) +{ + + /* We now *know* that the size of the tuple is correct, + * so we don't need to check anything here, just unpack. + */ + + switch (domain) { + case AF_INET: + { + int a, v; + char laddr[4]; + + sys_memzero((char*)&addrP->u, sizeof(struct sockaddr_in)); +#ifndef NO_SA_LEN + addrP->u.sai.sin_len = sizeof(struct sockaddr_in); +#endif + addrP->u.sai.sin_family = domain; + addrP->u.sai.sin_port = sock_htons(port); + for (a = 0; a < 4; a++) { + if (!GET_INT(env, addrt[a], &v)) + return str_einval; + laddr[a] = v; + } + sys_memcpy(&addrP->u.sai.sin_addr, &laddr, sizeof(laddr)); + addrP->len = sizeof(struct sockaddr_in); + return NULL; + } + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + { + int a, v; + char laddr[16]; + + sys_memzero((char*)&addrP->u, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + addrP->u.sai6.sin6_len = sizeof(struct sockaddr_in6); +#endif + addrP->u.sai6.sin6_family = domain; + addrP->u.sai6.sin6_port = sock_htons(port); + addrP->u.sai6.sin6_flowinfo = 0; + /* 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, addrt[a], &v)) + return str_einval; + laddr[a*2 ] = ((v >> 8) & 0xFF); + laddr[a*2+1] = (v & 0xFF); + } + sys_memcpy(&addrP->u.sai6.sin6_addr, &laddr, sizeof(laddr)); + addrP->len = sizeof(struct sockaddr_in6); + return NULL; + } + break; +#endif + + } /* switch (domain) */ + + return str_eafnosupport; + +} + + +/* Decode the address when its an atom. + * Currently we only accept two atoms: 'any' and 'loopback' + */ +static +char* decode_address_atom(ErlNifEnv* env, + int domain, + char* addr, + int addrLen, + int port, + SocketAddress* localP) +{ + BOOLEAN_T any; + + if (strncmp(addr, "any", addrLen) == 0) { + any = TRUE; + } if (strncmp(addr, "loopback", addrLen) == 0) { + any = FALSE; + } else { + return str_einval; + } + + /* If we get this far, we *know* its either 'any' or 'loopback' */ + + switch (domain) { + case AF_INET: + { + struct in_addr addr; + if (any) { + addr.s_addr = sock_htonl(INADDR_ANY); + } else { + addr.s_addr = sock_htonl(INADDR_LOOPBACK); + } + sys_memzero((char*) localP, sizeof(struct sockaddr_in)); +#ifndef NO_SA_LEN + localP->u.sai.sin_len = sizeof(struct sockaddr_in6); +#endif + localP->u.sai.sin_family = domain; + localP->u.sai.sin_port = sock_htons(port); + localP->u.sai.sin_addr.s_addr = addr.s_addr; + localP->len = sizeof(struct sockaddr_in); + } + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + { + const struct in6_addr* paddr; + if (any) { + paddr = &in6addr_any; + } else { + paddr = &in6addr_loopback; + } + sys_memzero((char*)localP, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + localP->u.sai6.sin6_len = sizeof(struct sockaddr_in6); +#endif + localP->u.sai6.sin6_family = domain; + localP->u.sai6.sin6_port = sock_htons(port); + localP->u.sai6.sin6_flowinfo = 0; + localP->u.sai6.sin6_addr = *paddr; + localP->len = sizeof(struct sockaddr_in6); + } + break; +#endif + + default: + return str_einval; + break; + } + + return NULL; +} + + + +/* *** alloc_descriptor *** + * Allocate and perform basic initialization of a socket descriptor. + * + */ +static +SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) +{ + SocketDescriptor* descP; + + if ((descP = enif_alloc_resource(sockets, sizeof(SocketDescriptor))) != NULL) { + char buf[64]; /* Buffer used for building the mutex name */ + + sprintf(buf, "socket[w,%d]", sock); + descP->writeMtx = MCREATE(buf); + + sprintf(buf, "socket[r,%d]", sock); + descP->readMtx = MCREATE(buf); + + sprintf(buf, "socket[acc,%d]", sock); + descP->accMtx = MCREATE(buf); + + descP->dbg = SOCKET_DEBUG_DEFAULT; + descP->isWritable = TRUE; + descP->isReadable = TRUE; + descP->writePkgCnt = 0; + descP->writeByteCnt = 0; + descP->writeTries = 0; + descP->writeWaits = 0; + descP->writeFails = 0; + descP->readPkgCnt = 0; + descP->readByteCnt = 0; + descP->readTries = 0; + descP->readWaits = 0; + + descP->sock = sock; + descP->event = event; + + } + + return descP; +} + + /* compare_pids - Test if two pids are equal * */ -- cgit v1.2.3 From 28611d6e6daab8ae24e5e593c001bcd6442506eb Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Apr 2018 10:57:54 +0200 Subject: [socket-nif] Completed recv Need to fix the use of the request ref (ID) handling in previous functions. --- erts/emulator/nifs/common/socket_nif.c | 526 +++++++++++++++++++++++++-------- 1 file changed, 399 insertions(+), 127 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 46c5c696e2..d3aa3db2aa 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -223,19 +223,20 @@ typedef unsigned long long llu_t; /* *** Misc macros and defines *** */ -#define MALLOC(SZ) enif_alloc(SZ) -#define FREE(P) enif_free(P) -#define MKA(E,S) enif_make_atom(E, S) -#define MKREF(E) enif_make_ref(E) -#define MKT2(E,E1,E2) enif_make_tuple2(E, E1, E2) -#define MCREATE(N) enif_mutex_create(N) -#define MLOCK(M) enif_mutex_lock(M) -#define MUNLOCK(M) enif_mutex_unlock(M) +#define MALLOC(SZ) enif_alloc(SZ) +#define FREE(P) enif_free(P) +#define MKA(E,S) enif_make_atom(E, S) +#define MKBIN(E,B) enif_make_binary(E, B) +#define MKREF(E) enif_make_ref(E) +#define MKT2(E,E1,E2) enif_make_tuple2(E, E1, E2) +#define MKT3(E,E1,E2,E3) enif_make_tuple3(E, E1, E2, E3) +#define MCREATE(N) enif_mutex_create(N) +#define MLOCK(M) enif_mutex_lock(M) +#define MUNLOCK(M) enif_mutex_unlock(M) #define SELECT(E,FD,M,O,P,R) \ if (enif_select((E), (FD), (M), (O), (P), (R)) < 0) \ return enif_make_badarg((E)); - /* *** Socket state defs *** */ #define SOCKET_FLAG_OPEN 0x0001 @@ -275,6 +276,18 @@ typedef unsigned long long llu_t; #define SOCKET_SEND_FLAG_LOW SOCKET_SEND_FLAG_CONFIRM #define SOCKET_SEND_FLAG_HIGH SOCKET_SEND_FLAG_OOB +#define SOCKET_RECV_FLAG_CMSG_CLOEXEC 0 +#define SOCKET_RECV_FLAG_DONTWAIT 1 +#define SOCKET_RECV_FLAG_ERRQUEUE 2 +#define SOCKET_RECV_FLAG_OOB 3 +#define SOCKET_RECV_FLAG_PEEK 4 +#define SOCKET_RECV_FLAG_TRUNC 5 +#define SOCKET_RECV_FLAG_WAITALL 6 +#define SOCKET_RECV_FLAG_LOW SOCKET_RECV_FLAG_CMSG_CLOEXEC +#define SOCKET_RECV_FLAG_HIGH SOCKET_RECV_FLAG_WAITALL + +#define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048 + typedef union { struct { unsigned int open:1; @@ -343,6 +356,8 @@ typedef union { #define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) +#define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) + /* =================================================================== * * * @@ -366,6 +381,7 @@ typedef union { #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_open(domain, type, proto) \ make_noninheritable_handle(socket((domain), (type), (proto))) +#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) #define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) @@ -395,6 +411,7 @@ static unsigned long one_value = 1; #define sock_listen(s, b) listen((s), (b)) #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_open(domain, type, proto) socket((domain), (type), (proto)) +#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) #define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) @@ -437,17 +454,28 @@ typedef struct { typedef struct { - ErlNifPid pid; // PID of the acceptor - ErlNifMonitor mon; // Monitor for the acceptor - ERL_NIF_TERM ref; // The (unique) reference of the (accept) request -} SocketAcceptor; + ErlNifPid pid; // PID of the requesting process + ErlNifMonitor mon; // Monitor to the requesting process + ERL_NIF_TERM ref; // The (unique) reference (ID) of the request +} SocketRequestor; + +typedef struct socket_request_queue_element { + struct socket_request_queue_element* next; + SocketRequestor data; +} SocketRequestQueueElement; typedef struct { - // The actual socket + SocketRequestQueueElement* first; + SocketRequestQueueElement* last; +} SocketRequestQueue; + + +typedef struct { + /* +++ The actual socket +++ */ SOCKET sock; HANDLE event; - /* "Stuff" about the socket */ + /* +++ Stuff "about" the socket +++ */ int domain; int type; int protocol; @@ -456,51 +484,45 @@ typedef struct { SocketAddress remote; - // Controller (owner) process + /* +++ Controller (owner) process +++ */ ErlNifPid ctrlPid; ErlNifMonitor ctrlMon; - // Write - ErlNifMutex* writeMtx; - BOOLEAN_T isWritable; - uint32_t writePkgCnt; - uint32_t writeByteCnt; - uint32_t writeTries; - uint32_t writeWaits; - uint32_t writeFails; - - // Read - ErlNifMutex* readMtx; - BOOLEAN_T isReadable; - ErlNifBinary rbuffer; - uint32_t readCapacity; - uint32_t readPkgCnt; - uint32_t readByteCnt; - uint32_t readTries; - uint32_t readWaits; - - /* Accept - * We also need a queue for waiting acceptors... - * Lets see if this can be a common "request" queue... - */ - ErlNifMutex* accMtx; - SocketAcceptor acceptor; - - - /* We need to keep track of the "request(s)" we have pending. - * If for instance an accept takes to long, the issuer may - * decide to "cancel" the accept (actually the select). This - * is done by calling the *nif_cancel* function with the request - * ref as argument. - * We also need to keep track of requests so that if a new - * request is issued before the current has completed, we - * reply with e.g. ebusy (or something to that effect). - * Or do we? Can the caller actually do that? - */ - - - /* Misc stuff */ + /* +++ Write stuff +++ */ + ErlNifMutex* writeMtx; + SocketRequestor currentWriter; + SocketRequestor* currentWriterP; // NULL or points to currentWriter + SocketRequestQueue writersQ; + BOOLEAN_T isWritable; + uint32_t writePkgCnt; + uint32_t writeByteCnt; + uint32_t writeTries; + uint32_t writeWaits; + uint32_t writeFails; + + /* +++ Read stuff +++ */ + ErlNifMutex* readMtx; + SocketRequestor currentReader; + SocketRequestor* currentReaderP; // NULL or points to currentReader + SocketRequestQueue readersQ; + BOOLEAN_T isReadable; + ErlNifBinary rbuffer; // DO WE NEED THIS + uint32_t readCapacity; // DO WE NEED THIS + uint32_t readPkgCnt; + uint32_t readByteCnt; + uint32_t readTries; + uint32_t readWaits; + + /* +++ Accept stuff +++ */ + ErlNifMutex* accMtx; + SocketRequestor currentAcceptor; + SocketRequestor* currentAcceptorP; // NULL or points to currentReader + SocketRequestQueue acceptorsQ; + + /* +++ Misc stuff +++ */ + BOOLEAN_T iow; // Inform On Wrap BOOLEAN_T dbg; + } SocketDescriptor; @@ -515,39 +537,6 @@ typedef struct { } SocketData; -/* typedef struct socket_queue_element { */ -/* struct socket_queue_element* next; */ -/* /\* */ -/* unsigned int tag; */ -/* union { */ -/* SocketAcceptor acceptor; */ -/* } u; */ -/* *\/ */ -/* SocketAcceptor acceptor; */ -/* } SocketQueueElement; */ - -/* typedef struct socket_queue { */ -/* SocketQueueElement* first; */ -/* SocketQueueElement* last; */ -/* } SocketQueue; */ - -/* Macros for defining the various queues (accept, send receive) */ -#define SOCKET_QUEUE_ELEMENT(QE,QEP) \ - typedef struct socket_queue_element_##QEP { \ - struct socket_queue_element_##QEP* next; \ - QE elem; \ - } QE##Element; - -#define SOCKET_QUEUE(QE,Q) \ - typedef struct { \ - QE* first; \ - QE* last; \ - } Q; - -/* The Acceptor Queue types */ -SOCKET_QUEUE_ELEMENT(SocketAcceptor, acceptor); -SOCKET_QUEUE(SocketAcceptorElement, SocketAcceptQueue); - /* ---------------------------------------------------------------------- * F o r w a r d s * ---------------------------------------------------------------------- @@ -600,12 +589,12 @@ static ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, static ERL_NIF_TERM nif_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM nif_setopt(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM nif_getopt(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_setsockopt(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_getsockopt(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[]); @@ -649,12 +638,23 @@ static ERL_NIF_TERM nsendto(ErlNifEnv* env, ErlNifBinary* dataP, int flags, SocketAddress* toAddrP); +static ERL_NIF_TERM nrecv(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM recvRef, + int len, + int flags); static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, ssize_t written, ssize_t dataSize, ERL_NIF_TERM sendRef); +static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, + SocketDescriptor* descP, + int read, + int toRead, + ErlNifBinary* bufP, + ERL_NIF_TERM recvRef); static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP); @@ -707,6 +707,7 @@ static BOOLEAN_T edomain2domain(int edomain, int* domain); static BOOLEAN_T etype2type(int etype, int* type); static BOOLEAN_T eproto2proto(int eproto, int* proto); static BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags); +static BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags); #ifdef HAVE_SETNS static BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns); static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err); @@ -725,7 +726,8 @@ static void socket_down(ErlNifEnv* env, const ErlNifPid* pid, const ErlNifMonitor* mon); -static ERL_NIF_TERM make_ok(ErlNifEnv* env, ERL_NIF_TERM any); +static ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM val); +static ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2); 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); @@ -767,8 +769,9 @@ static const struct in6_addr in6addr_loopback = /* *** String constants *** */ -static char str_false[] = "false"; +static char str_closed[] = "closed"; static char str_error[] = "error"; +static char str_false[] = "false"; static char str_ok[] = "ok"; static char str_true[] = "true"; static char str_undefined[] = "undefined"; @@ -779,6 +782,7 @@ static char str_eafnosupport[] = "eafnosupport"; static char str_einval[] = "einval"; static char str_eisconn[] = "eisconn"; static char str_enotconn[] = "enotconn"; +static char str_exalloc[] = "exalloc"; static char str_exbadstate[] = "exbadstate"; static char str_exbusy[] = "exbusy"; static char str_exmon[] = "exmonitor"; // failed monitor @@ -786,8 +790,9 @@ static char str_exself[] = "exself"; // failed self /* *** Atoms *** */ -static ERL_NIF_TERM atom_false; +static ERL_NIF_TERM atom_closed; static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_undefined; @@ -797,6 +802,7 @@ static ERL_NIF_TERM atom_eafnosupport; static ERL_NIF_TERM atom_einval; static ERL_NIF_TERM atom_eisconn; static ERL_NIF_TERM atom_enotconn; +static ERL_NIF_TERM atom_exalloc; static ERL_NIF_TERM atom_exbadstate; static ERL_NIF_TERM atom_exbusy; static ERL_NIF_TERM atom_exmon; @@ -832,16 +838,16 @@ static SocketData socketData; * nif_connect(Sock, Addr, Port) * nif_listen(Sock, Backlog) * nif_accept(LSock, Ref) - * nif_send(Sock, Data, Flags) - * nif_sendto(Sock, Data, Flags, DstAddr, DstPort) - * nif_recv(Sock, Flags) + * nif_send(Sock, SendRef, Data, Flags) + * nif_sendto(Sock, SendRef, Data, Flags, DstAddr, DstPort) + * nif_recv(Sock, RecvRef, Length, Flags) * nif_recvfrom(Sock, Flags) * nif_close(Sock) * * And some functions to manipulate and retrieve socket options: * ------------------------------------------------------------- - * nif_setopt/3 - * nif_getopt/2 + * nif_setsockopt/3 + * nif_getsockopt/2 * * And some socket admin functions: * ------------------------------------------------------------- @@ -1033,7 +1039,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, #endif - return make_ok(env, res); + return make_ok2(env, res); } @@ -1213,7 +1219,7 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, port = 0; } - return make_ok(env, enif_make_int(env, port)); + return make_ok2(env, enif_make_int(env, port)); } @@ -1482,7 +1488,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, descP->sock, (ERL_NIF_SELECT_WRITE), descP, NULL, ref); - return make_ok(env, ref); + return make_ok2(env, ref); } else if (code == 0) { /* ok we are connected */ descP->state = SOCKET_STATE_CONNECTED; /* Do we need to do somthing for "active" mode? @@ -1738,13 +1744,13 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, /* *** Try again later *** */ - descP->acceptor.pid = caller; + descP->currentAcceptor.pid = caller; if (enif_monitor_process(env, descP, - &descP->acceptor.pid, - &descP->acceptor.mon) > 0) + &descP->currentAcceptor.pid, + &descP->currentAcceptor.mon) > 0) return make_error(env, atom_exmon); - descP->acceptor.ref = ref; + descP->currentAcceptor.ref = ref; SELECT(env, descP->sock, @@ -1830,7 +1836,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, accDescP->state = SOCKET_STATE_CONNECTED; - return make_ok(env, accRef); + return make_ok2(env, accRef); } } @@ -1854,7 +1860,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, if (enif_self(env, &caller) == NULL) return make_error(env, atom_exself); - if (compare_pids(env, &descP->acceptor.pid, &caller) != 0) { + if (compare_pids(env, &descP->currentAcceptor.pid, &caller) != 0) { /* This will have to do until we implement the queue. * When we have the queue, we should simply push this request, * and instead return with eagain (the caller will then wait @@ -1938,7 +1944,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, */ descP->state = SOCKET_STATE_LISTENING; - return make_ok(env, accRef); + return make_ok2(env, accRef); } } @@ -2200,6 +2206,101 @@ ERL_NIF_TERM nwritev(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * nif_recv + * + * Description: + * Receive a message on a socket. + * Normally used only on a connected socket! + * If we are trying to read > 0 bytes, then that is what we do. + * But if we have specified 0 bytes, then we want to read + * whatever is in the buffers (everything it got). + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * RecvRef - A unique id for this (send) request. + * Length - The number of bytes to receive. + * Flags - Receive flags. + */ + +static +ERL_NIF_TERM nif_recv(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + ERL_NIF_TERM recvRef; + int len; + unsigned int eflags; + int flags; + ERL_NIF_TERM res; + + if ((argc != 4) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP) || + !GET_INT(env, argv[2], &len) || + !GET_UINT(env, argv[3], &eflags)) { + return enif_make_badarg(env); + } + recvRef = argv[1]; + + if (!IS_CONNECTED(descP)) + return make_error(env, atom_enotconn); + + if (!erecvflags2recvflags(eflags, &flags)) + return enif_make_badarg(env); + + MLOCK(descP->readMtx); + + /* We need to handle the case when another process tries + * to receive at the same time. + * If the current recv could not read its entire package + * this time (resulting in an select). The read of the + * other process must be made to wait until current + * is done! + * Basically, we need a read queue! + * + * A 'reading' field (boolean), which is set if we did + * not manage to read the entire message and reset every + * time we do. + */ + + res = nrecv(env, descP, recvRef, len, flags); + + MUNLOCK(descP->readMtx); + + return res; + +} + + +static +ERL_NIF_TERM nrecv(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM recvRef, + int len, + int flags) +{ + ssize_t read; + ErlNifBinary buf; + + if (!descP->isReadable) + return enif_make_badarg(env); + + if (!ALLOC_BIN((len ? len : SOCKET_RECV_BUFFER_SIZE_DEFAULT), &buf)) + return make_error(env, atom_exalloc); + + /* We ignore the wrap for the moment. + * Maybe we should issue a wrap-message to controlling process... + */ + cnt_inc(&descP->readTries, 1); + + read = sock_recv(descP->sock, buf.data, buf.size, flags); + + return recv_check_result(env, descP, + read, len, + &buf, + recvRef); +} @@ -2237,7 +2338,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, /* Ok, try again later */ - written = 0; + written = 0; // SHOULD RESULT IN {error, eagain}!!!! } } @@ -2252,12 +2353,109 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), descP, NULL, sendRef); - return make_ok(env, enif_make_int(env, written)); + return make_ok2(env, enif_make_int(env, written)); } -/* The rather odd thing about the 'toAddrP' (the **) is +static +ERL_NIF_TERM recv_check_result(ErlNifEnv* env, + SocketDescriptor* descP, + int read, + int toRead, + ErlNifBinary* bufP, + ERL_NIF_TERM recvRef) +{ + /* There is a special case: If the provided 'to read' value is + * zero (0). That means that if we filled the (default size) + * buffer, we need to continue to read (since there *may* be + * more data), but we cannot loop here. Instead we inform the + * caller that it must call again. + */ + + if (bufP->size == read) { + + /* +++ We filled the buffer +++ */ + + if (toRead == 0) { + + /* +++ Give us everything you have got => needs to continue +++ */ + + /* How do we do this? + * Either: + * 1) Send up each chunk of data for each of the read + * and let the erlang code assemble it: {ok, false, Bin} + * (when complete it should return {ok, true, Bin}). + * We need to read atleast one more time to be sure if its + * done... + * 2) Or put it in a buffer here, and then let the erlang code + * know that it should call again (special return value) + * (continuous binary realloc "here"). + * + * => We choose alt 1 for now. + */ + + return make_ok3(env, atom_false, MKBIN(env, bufP)); + + } else { + + /* +++ We got exactly as much as we requested +++ */ + + /* + * WE NEED TO INFORM ANY WAITING READERS + * + */ + + return make_ok3(env, atom_true, MKBIN(env, bufP)); + + } + + } else if (read < 0) { + + /* +++ Error handling +++ */ + + int save_errno = sock_errno(); + + if (save_errno == ECONNRESET) { + + /* +++ Oups - closed +++ */ + + /* + * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING + * PROCESS, WE NEED TO INFORM IT!!! + * + * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! + * + * + */ + + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_STOP), + descP, NULL, recvRef); + + return make_error(env, atom_closed); + + } else if ((save_errno == ERRNO_BLOCK) || + (save_errno == EAGAIN)) { + return make_error(env, atom_eagain); + } else { + return make_error2(env, save_errno); + } + + } else { + + /* +++ We got only a part of what was expected - receive more later +++ */ + + return make_ok3(env, atom_false, MKBIN(env, bufP)); + + } +} + + +/* *** decode_send_addr *** + * + * The rather odd thing about the 'toAddrP' (the **) is * because we need to be able to return a NULL pointer, * in the case of the dest address is the atom 'null'. * Its possible to call the sendto function with the @@ -2507,14 +2705,24 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) char buf[64]; /* Buffer used for building the mutex name */ sprintf(buf, "socket[w,%d]", sock); - descP->writeMtx = MCREATE(buf); + descP->writeMtx = MCREATE(buf); + descP->currentWriterP = NULL; // currentWriter not used + descP->writersQ.first = NULL; + descP->writersQ.last = NULL; sprintf(buf, "socket[r,%d]", sock); - descP->readMtx = MCREATE(buf); + descP->readMtx = MCREATE(buf); + descP->currentReaderP = NULL; // currentReader not used + descP->readersQ.first = NULL; + descP->readersQ.last = NULL; sprintf(buf, "socket[acc,%d]", sock); - descP->accMtx = MCREATE(buf); + descP->accMtx = MCREATE(buf); + descP->currentAcceptorP = NULL; // currentAcceptor not used + descP->acceptorsQ.first = NULL; + descP->acceptorsQ.last = NULL; + descP->iow = FALSE; descP->dbg = SOCKET_DEBUG_DEFAULT; descP->isWritable = TRUE; descP->isReadable = TRUE; @@ -2756,17 +2964,81 @@ BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags) +/* erecvflags2recvflags - convert internal (erlang) send flags to (proper) + * send flags. + */ +static +BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags) +{ + unsigned int ef; + int tmp = 0; + + for (ef = SOCKET_RECV_FLAG_LOW; ef <= SOCKET_RECV_FLAG_HIGH; ef++) { + switch (ef) { + case SOCKET_RECV_FLAG_CMSG_CLOEXEC: + tmp |= MSG_CMSG_CLOEXEC; + break; + + case SOCKET_RECV_FLAG_DONTWAIT: + tmp |= MSG_DONTWAIT; + break; + + case SOCKET_RECV_FLAG_ERRQUEUE: + tmp |= MSG_ERRQUEUE; + break; + + case SOCKET_RECV_FLAG_OOB: + tmp |= MSG_OOB; + break; + + case SOCKET_RECV_FLAG_PEEK: + tmp |= MSG_PEEK; + break; + + case SOCKET_RECV_FLAG_TRUNC: + tmp |= MSG_TRUNC; + break; + + case SOCKET_RECV_FLAG_WAITALL: + tmp |= MSG_WAITALL; + break; + + default: + return FALSE; + } + + } + + *recvflags = tmp; + + return TRUE; +} + + + /* Create an ok two (2) tuple in the form: {ok, Any}. * The second element (Any) is already in the form of an * ERL_NIF_TERM so all we have to do is create the tuple. */ static -ERL_NIF_TERM make_ok(ErlNifEnv* env, ERL_NIF_TERM any) +ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM any) { return MKT2(env, atom_ok, any); } +/* Create an ok three (3) tuple in the form: {ok, Val1, Val2}. + * The second (Val1) and third (Val2) elements are already in + * the form of an ERL_NIF_TERM so all we have to do is create + * the tuple. + */ +static +ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2) +{ + return MKT3(env, atom_ok, val1, val2); +} + + /* Create an error two (2) tuple in the form: {error, Reason}. * The second element (Reason) is already in the form of an * ERL_NIF_TERM so all we have to do is create the tuple. @@ -2885,13 +3157,13 @@ ErlNifFunc socket_funcs[] = {"nif_connect", 3, nif_connect}, {"nif_listen", 2, nif_listen}, {"nif_accept", 2, nif_accept}, - {"nif_send", 3, nif_send}, - {"nif_sendto", 5, nif_sendto}, - {"nif_recv", 2, nif_recv}, + {"nif_send", 4, nif_send}, + {"nif_sendto", 6, nif_sendto}, + {"nif_recv", 4, nif_recv}, {"nif_recvfrom", 2, nif_recvfrom}, {"nif_close", 1, nif_close}, - {"nif_setopt", 3, nif_setopt}, - {"nif_getopt", 2, nif_getopt}, + {"nif_setsockopt", 3, nif_setsockopt}, + {"nif_getsockopt", 2, nif_getsockopt}, /* "Extra" functions to "complete" the socket interface. * For instance, the function nif_finalize_connection @@ -2966,7 +3238,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) // atom_active_once = MKA(env, str_active_once); // atom_binary = MKA(env, str_binary); // atom_buildDate = MKA(env, str_buildDate); - // atom_closed = MKA(env, str_closed); + atom_closed = MKA(env, str_closed); atom_error = MKA(env, str_error); atom_false = MKA(env, str_false); // atom_list = MKA(env, str_list); @@ -2986,7 +3258,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_einval = MKA(env, str_einval); atom_eisconn = MKA(env, str_eisconn); atom_enotconn = MKA(env, str_enotconn); - // atom_exalloc = MKA(env, str_exalloc); + atom_exalloc = MKA(env, str_exalloc); atom_exbadstate = MKA(env, str_exbadstate); atom_exbusy = MKA(env, str_exbusy); // atom_exnotopen = MKA(env, str_exnotopen); -- cgit v1.2.3 From 04335ca6aedfc5ad9f0d6a8d193dfd76a222291c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Apr 2018 10:40:28 +0200 Subject: [socket-nif] Completed the recv and recvfrom functions Also updated the socket type (now a record for easy use). --- erts/emulator/nifs/common/socket_nif.c | 716 ++++++++++++++++++++++++++------- 1 file changed, 567 insertions(+), 149 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index d3aa3db2aa..6e6851a608 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -181,6 +181,9 @@ typedef unsigned int BOOLEAN_T; #define BOOL2STR(__B__) ((__B__) ? "true" : "false") #define BOOL2ATOM(__B__) ((__B__) ? atom_true : atom_false) +/* Two byte integer decoding */ +#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ + (((unsigned char*) (s))[1])) /* Debug stuff... */ #define SOCKET_NIF_DEBUG_DEFAULT TRUE @@ -223,19 +226,6 @@ typedef unsigned long long llu_t; /* *** Misc macros and defines *** */ -#define MALLOC(SZ) enif_alloc(SZ) -#define FREE(P) enif_free(P) -#define MKA(E,S) enif_make_atom(E, S) -#define MKBIN(E,B) enif_make_binary(E, B) -#define MKREF(E) enif_make_ref(E) -#define MKT2(E,E1,E2) enif_make_tuple2(E, E1, E2) -#define MKT3(E,E1,E2,E3) enif_make_tuple3(E, E1, E2, E3) -#define MCREATE(N) enif_mutex_create(N) -#define MLOCK(M) enif_mutex_lock(M) -#define MUNLOCK(M) enif_mutex_unlock(M) -#define SELECT(E,FD,M,O,P,R) \ - if (enif_select((E), (FD), (M), (O), (P), (R)) < 0) \ - return enif_make_badarg((E)); /* *** Socket state defs *** */ @@ -268,23 +258,20 @@ typedef unsigned long long llu_t; #define SOCKET_SEND_FLAG_CONFIRM 0 #define SOCKET_SEND_FLAG_DONTROUTE 1 -#define SOCKET_SEND_FLAG_DONTWAIT 2 -#define SOCKET_SEND_FLAG_EOR 3 -#define SOCKET_SEND_FLAG_MORE 4 -#define SOCKET_SEND_FLAG_NOSIGNAL 5 -#define SOCKET_SEND_FLAG_OOB 6 +#define SOCKET_SEND_FLAG_EOR 2 +#define SOCKET_SEND_FLAG_MORE 3 +#define SOCKET_SEND_FLAG_NOSIGNAL 4 +#define SOCKET_SEND_FLAG_OOB 5 #define SOCKET_SEND_FLAG_LOW SOCKET_SEND_FLAG_CONFIRM #define SOCKET_SEND_FLAG_HIGH SOCKET_SEND_FLAG_OOB #define SOCKET_RECV_FLAG_CMSG_CLOEXEC 0 -#define SOCKET_RECV_FLAG_DONTWAIT 1 -#define SOCKET_RECV_FLAG_ERRQUEUE 2 -#define SOCKET_RECV_FLAG_OOB 3 -#define SOCKET_RECV_FLAG_PEEK 4 -#define SOCKET_RECV_FLAG_TRUNC 5 -#define SOCKET_RECV_FLAG_WAITALL 6 +#define SOCKET_RECV_FLAG_ERRQUEUE 1 +#define SOCKET_RECV_FLAG_OOB 2 +#define SOCKET_RECV_FLAG_PEEK 3 +#define SOCKET_RECV_FLAG_TRUNC 4 #define SOCKET_RECV_FLAG_LOW SOCKET_RECV_FLAG_CMSG_CLOEXEC -#define SOCKET_RECV_FLAG_HIGH SOCKET_RECV_FLAG_WAITALL +#define SOCKET_RECV_FLAG_HIGH SOCKET_RECV_FLAG_TRUNC #define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048 @@ -342,6 +329,27 @@ typedef union { * * * =================================================================== */ +#define MALLOC(SZ) enif_alloc((SZ)) +#define FREE(P) enif_free((P)) +#define MKA(E,S) enif_make_atom((E), (S)) +#define MKBIN(E,B) enif_make_binary((E), (B)) +#define MKI(E,I) enif_make_int((E), (I)) +#define MKREF(E) enif_make_ref((E)) +#define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) +#define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) +#define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) +#define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) +#define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) +#define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) +#define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ + enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) +#define MCREATE(N) enif_mutex_create((N)) +#define MLOCK(M) enif_mutex_lock((M)) +#define MUNLOCK(M) enif_mutex_unlock((M)) +#define SELECT(E,FD,M,O,P,R) \ + if (enif_select((E), (FD), (M), (O), (P), (R)) < 0) \ + return enif_make_badarg((E)); + #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)) @@ -379,12 +387,15 @@ typedef union { #define sock_htonl(x) htonl((x)) #define sock_listen(s, b) listen((s), (b)) #define sock_name(s, addr, len) getsockname((s), (addr), (len)) +#define sock_ntohs(x) ntohs((x)) #define sock_open(domain, type, proto) \ make_noninheritable_handle(socket((domain), (type), (proto))) #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)) #define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ - sendto((s),(buf),(blen),(flag),(addr),(alen)) + sendto((s),(buf),(blen),(flag),(addr),(alen)) #define sock_errno() WSAGetLastError() #define sock_create_event(s) WSACreateEvent() @@ -410,8 +421,11 @@ static unsigned long one_value = 1; #define sock_htonl(x) htonl((x)) #define sock_listen(s, b) listen((s), (b)) #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_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)) #define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) @@ -430,27 +444,23 @@ static unsigned long one_value = 1; /* The general purpose sockaddr */ -typedef struct { - union { - struct sockaddr sa; - struct sockaddr_in sai; +typedef union { + struct sockaddr sa; + struct sockaddr_in sai; #ifdef HAVE_IN6 - struct sockaddr_in6 sai6; + struct sockaddr_in6 sai6; #endif #ifdef HAVE_SYS_UN_H - struct sockaddr_un sal; + struct sockaddr_un sal; #endif - - } u; - unsigned int len; } SocketAddress; #define which_address_port(sap) \ - ((((sap)->u.sai.sin_family == AF_INET) || \ - ((sap)->u.sai.sin_family == AF_INET6)) ? \ - ((sap)->u.sai.sin_port) : -1) + ((((sap)->sai.sin_family == AF_INET) || \ + ((sap)->sai.sin_family == AF_INET6)) ? \ + ((sap)->sai.sin_port) : -1) typedef struct { @@ -519,9 +529,10 @@ typedef struct { SocketRequestor* currentAcceptorP; // NULL or points to currentReader SocketRequestQueue acceptorsQ; - /* +++ Misc stuff +++ */ - BOOLEAN_T iow; // Inform On Wrap - BOOLEAN_T dbg; + /* +++ Config & Misc stuff +++ */ + size_t rBufSz; // Read buffer size (when data length = 0 is specified) + BOOLEAN_T iow; // Inform On Wrap + BOOLEAN_T dbg; } SocketDescriptor; @@ -637,12 +648,18 @@ static ERL_NIF_TERM nsendto(ErlNifEnv* env, ERL_NIF_TERM sendRef, ErlNifBinary* dataP, int flags, - SocketAddress* toAddrP); + SocketAddress* toAddrP, + unsigned int toAddrLen); static ERL_NIF_TERM nrecv(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM recvRef, int len, int flags); +static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM recvRef, + uint16_t bufSz, + int flags); static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -655,6 +672,13 @@ static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, int toRead, ErlNifBinary* bufP, ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, + SocketDescriptor* descP, + int read, + ErlNifBinary* bufP, + SocketAddress* fromAddrP, + unsigned int fromAddrLen, + ERL_NIF_TERM recvRef); static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP); @@ -663,36 +687,49 @@ static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, static char* decode_laddress(ErlNifEnv* env, int domain, ERL_NIF_TERM localAddr, - SocketAddress* localP); + SocketAddress* localP, + unsigned int* addrLenP); static char* decode_laddress_binary(ErlNifEnv* env, int domain, ERL_NIF_TERM localAddr, - SocketAddress* localP); + SocketAddress* localP, + unsigned int* addrLenP); static char* decode_laddress_tuple(ErlNifEnv* env, int domain, ERL_NIF_TERM laddr, - SocketAddress* localP); + SocketAddress* localP, + unsigned int* addrLenP); static char* decode_address_tuple(ErlNifEnv* env, int domain, const ERL_NIF_TERM* addrt, int port, - SocketAddress* localP); + SocketAddress* localP, + unsigned int* addrLenP); static char* decode_address_atom(ErlNifEnv* env, int domain, char* addr, int addrLen, int port, - SocketAddress* localP); + SocketAddress* localP, + unsigned int* addrLenP); static char* decode_send_addr(ErlNifEnv* env, int domain, ERL_NIF_TERM addr, int port, - SocketAddress** toAddrP); + SocketAddress** toAddrP, + unsigned int* addrLenP); static char* decode_send_addr_tuple(ErlNifEnv* env, int domain, ERL_NIF_TERM addr, int port, - SocketAddress* toAddrP); + SocketAddress* toAddrP, + unsigned int* addrLenP); +static void encode_address(ErlNifEnv* env, + SocketAddress* fromAddrP, + unsigned int fromAddrLen, + ERL_NIF_TERM* fromDomainT, + ERL_NIF_TERM* fromSourceT); + static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err); static SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event); @@ -716,6 +753,10 @@ static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err); static BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc); +#if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) +static size_t my_strnlen(const char *s, size_t maxlen); +#endif + static void socket_dtor(ErlNifEnv* env, void* obj); static void socket_stop(ErlNifEnv* env, void* obj, @@ -1198,22 +1239,23 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, ERL_NIF_TERM addr) { SocketAddress local; + unsigned int addrLen; char* err; int port; - if ((err = decode_laddress(env, descP->domain, addr, &local)) != NULL) + if ((err = decode_laddress(env, descP->domain, addr, &local, &addrLen)) != NULL) return make_error1(env, err); if (IS_SOCKET_ERROR(sock_bind(descP->sock, - (struct sockaddr*) &local.u, local.len))) { + (struct sockaddr*) &local, addrLen))) { return make_error2(env, sock_errno()); } port = which_address_port(&local); if (port == 0) { - SOCKLEN_T addrLen = sizeof(local.u); - sys_memzero((char *) &local.u, addrLen); - sock_name(descP->sock, &local.u.sa, &addrLen); + SOCKLEN_T len = sizeof(local); + sys_memzero((char *) &local, len); + sock_name(descP->sock, &local.sa, &len); port = which_address_port(&local); } else if (port == -1) { port = 0; @@ -1233,12 +1275,13 @@ static char* decode_laddress(ErlNifEnv* env, int domain, ERL_NIF_TERM localAddr, - SocketAddress* localP) + SocketAddress* localP, + unsigned int* addrLenP) { if (IS_BIN(env, localAddr)) { - return decode_laddress_binary(env, domain, localAddr, localP); + return decode_laddress_binary(env, domain, localAddr, localP, addrLenP); } else if (IS_TUPLE(env, localAddr)) { - return decode_laddress_tuple(env, domain, localAddr, localP); + return decode_laddress_tuple(env, domain, localAddr, localP, addrLenP); } else { return str_einval; } @@ -1254,8 +1297,11 @@ static char* decode_laddress_binary(ErlNifEnv* env, int domain, ERL_NIF_TERM localAddr, - SocketAddress* localP) + SocketAddress* localP, + unsigned int* addrLenP) { + unsigned int addrLen; + #ifdef HAVE_SYS_UN_H ErlNifBinary bin; @@ -1278,16 +1324,17 @@ char* decode_laddress_binary(ErlNifEnv* env, #else 1 #endif - ) > sizeof(localP->u.sal.sun_path)) + ) > sizeof(localP->sal.sun_path)) return str_einval; - sys_memzero((char*)&localP->u, sizeof(struct sockaddr_un)); - localP->u.sal.sun_family = domain; - sys_memcpy(localP->u.sal.sun_path, bin.data, bin.size); - localP->len = offsetof(struct sockaddr_un, sun_path) + bin.size; + sys_memzero((char*)localP, sizeof(struct sockaddr_un)); + localP->sal.sun_family = domain; + sys_memcpy(localP->sal.sun_path, bin.data, bin.size); + addrLen = offsetof(struct sockaddr_un, sun_path) + bin.size; #ifndef NO_SA_LEN - localP->u.sal.sun_len = localP->len; + localP->u.sal.sun_len = addrLen; #endif + *addrLenP = addrLen; return NULL; #else // HAVE_SYS_UN_H @@ -1312,7 +1359,8 @@ static char* decode_laddress_tuple(ErlNifEnv* env, int domain, ERL_NIF_TERM laddr, - SocketAddress* localP) + SocketAddress* localP, + unsigned int* addrLenP) { const ERL_NIF_TERM* laddrt; int laddrtSz; @@ -1365,7 +1413,7 @@ char* decode_laddress_tuple(ErlNifEnv* env, return decode_address_tuple(env, domain, addrt, port, - localP); + localP, addrLenP); } else if (IS_ATOM(env, laddrt[0]) && IS_NUM(env, laddrt[1])) { @@ -1388,7 +1436,7 @@ char* decode_laddress_tuple(ErlNifEnv* env, return decode_address_atom(env, domain, a, len, port, - localP); + localP, addrLenP); } else { return str_einval; @@ -1457,8 +1505,9 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, const ERL_NIF_TERM* addr, int port) { - int code; - char* xerr; + unsigned int addrLen; + int code; + char* xerr; /* Verify that we are where in the proper state */ @@ -1473,11 +1522,11 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, if ((xerr = decode_address_tuple(env, descP->domain, addr, port, - &descP->remote)) != NULL) + &descP->remote, &addrLen)) != NULL) return make_error1(env, xerr); code = sock_connect(descP->sock, - (struct sockaddr*) &descP->remote.u, descP->remote.len); + (struct sockaddr*) &descP->remote, addrLen); if (IS_SOCKET_ERROR(code) && ((sock_errno() == ERRNO_BLOCK) || /* Winsock2 */ @@ -1572,8 +1621,8 @@ BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err) #ifndef SO_ERROR int sz, code; - sz = sizeof(descP->inet.remote); - sys_memzero((char *) &descP->inet.remote, sz); + sz = sizeof(descP->remote); + sys_memzero((char *) &descP->remote, sz); code = sock_peer(desc->sock, (struct sockaddr*) &descP->remote, &sz); @@ -1735,7 +1784,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, if (enif_self(env, &caller) == NULL) return make_error(env, atom_exself); - n = sizeof(descP->remote.u); + n = sizeof(remote); sys_memzero((char *) &remote, n); accSock = sock_accept(descP->sock, (struct sockaddr*) &remote, &n); if (accSock == INVALID_SOCKET) { @@ -1869,7 +1918,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, return make_error(env, atom_exbusy); } - n = sizeof(descP->remote.u); + n = sizeof(descP->remote); sys_memzero((char *) &remote, n); accSock = sock_accept(descP->sock, (struct sockaddr*) &remote, &n); if (accSock == INVALID_SOCKET) { @@ -2074,6 +2123,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, int port; SocketAddress remoteAddr; SocketAddress* remoteAddrP = &remoteAddr; + unsigned int remoteAddrLen; char* xerr; // ERL_NIF_TERM res; @@ -2098,10 +2148,11 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, if ((xerr = decode_send_addr(env, descP->domain, addr, port, - &remoteAddrP)) != NULL) + &remoteAddrP, + &remoteAddrLen)) != NULL) return make_error1(env, xerr); - return nsendto(env, descP, sendRef, &data, flags, remoteAddrP); + return nsendto(env, descP, sendRef, &data, flags, remoteAddrP, remoteAddrLen); } @@ -2111,7 +2162,8 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, ERL_NIF_TERM sendRef, ErlNifBinary* dataP, int flags, - SocketAddress* toAddrP) + SocketAddress* toAddrP, + unsigned int toAddrLen) { ssize_t written; @@ -2126,7 +2178,7 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, if (toAddrP != NULL) { written = sock_sendto(descP->sock, dataP->data, dataP->size, flags, - &toAddrP->u.sa, toAddrP->len); + &toAddrP->sa, toAddrLen); } else { written = sock_sendto(descP->sock, dataP->data, dataP->size, flags, @@ -2273,6 +2325,11 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, } +/* The (read) buffer handling *must* be optimized! + * But for now we make it easy for ourselves by + * allocating a binary (of the specified or default + * size) and then throwing it away... + */ static ERL_NIF_TERM nrecv(ErlNifEnv* env, SocketDescriptor* descP, @@ -2286,7 +2343,11 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, if (!descP->isReadable) return enif_make_badarg(env); - if (!ALLOC_BIN((len ? len : SOCKET_RECV_BUFFER_SIZE_DEFAULT), &buf)) + /* Allocate a buffer: + * Either as much as we want to read or (if zero (0)) use the "default" + * size (what has been configured). + */ + if (!ALLOC_BIN((len ? len : descP->rBufSz), &buf)) return make_error(env, atom_exalloc); /* We ignore the wrap for the moment. @@ -2304,6 +2365,121 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * nif_recvfrom + * + * Description: + * Receive a message on a socket. + * Normally used only on a (un-) connected socket! + * If a buffer size = 0 is specified, then the we will use the default + * buffer size for this socket (whatever has been configured). + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * RecvRef - A unique id for this (send) request. + * BufSz - Size of the buffer into which we put the received message. + * Flags - Receive flags. + */ + +static +ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + ERL_NIF_TERM recvRef; + unsigned int bufSz; + unsigned int eflags; + int flags; + ERL_NIF_TERM res; + + if ((argc != 4) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP) || + !GET_UINT(env, argv[2], &bufSz) || + !GET_UINT(env, argv[3], &eflags)) { + return enif_make_badarg(env); + } + recvRef = argv[1]; + + /* if (IS_OPEN(descP)) */ + /* return make_error(env, atom_enotconn); */ + + if (!erecvflags2recvflags(eflags, &flags)) + return enif_make_badarg(env); + + MLOCK(descP->readMtx); + + /* + * We need to handle the case when another process tries + * to receive at the same time. + * If the current recv could not read its entire package + * this time (resulting in an select). The read of the + * other process must be made to wait until current + * is done! + * Basically, we need a read queue! + * + * A 'reading' field (boolean), which is set if we did + * not manage to read the entire message and reset every + * time we do. + * + */ + + res = nrecvfrom(env, descP, recvRef, bufSz, flags); + + MUNLOCK(descP->readMtx); + + return res; + +} + + +/* The (read) buffer handling *must* be optimized! + * But for now we make it easy for ourselves by + * allocating a binary (of the specified or default + * size) and then throwing it away... + */ +static +ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM recvRef, + uint16_t bufSz, + int flags) +{ + SocketAddress fromAddr; + unsigned int addrLen; + ssize_t read; + ErlNifBinary buf; + + if (!descP->isReadable) + return enif_make_badarg(env); + + /* Allocate a buffer: + * 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)) + return make_error(env, atom_exalloc); + + /* We ignore the wrap for the moment. + * Maybe we should issue a wrap-message to controlling process... + */ + cnt_inc(&descP->readTries, 1); + + addrLen = sizeof(fromAddr); + sys_memzero((char*) &fromAddr, addrLen); + + read = sock_recvfrom(descP->sock, buf.data, buf.size, flags, + &fromAddr.sa, &addrLen); + + return recvfrom_check_result(env, descP, + read, + &buf, + &fromAddr, addrLen, + recvRef); +} + + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -2338,7 +2514,11 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, /* Ok, try again later */ - written = 0; // SHOULD RESULT IN {error, eagain}!!!! + /* + * SHOULD RESULT IN {error, eagain}!!!! + * + */ + written = 0; } } @@ -2366,11 +2546,11 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, ErlNifBinary* bufP, ERL_NIF_TERM recvRef) { + ERL_NIF_TERM data; + /* There is a special case: If the provided 'to read' value is - * zero (0). That means that if we filled the (default size) - * buffer, we need to continue to read (since there *may* be - * more data), but we cannot loop here. Instead we inform the - * caller that it must call again. + * zero (0). That means that we reads as much as we can, using + * the default read buffer size. */ if (bufP->size == read) { @@ -2395,7 +2575,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * => We choose alt 1 for now. */ - return make_ok3(env, atom_false, MKBIN(env, bufP)); + data = MKBIN(env, bufP); + + return make_ok3(env, atom_false, data); } else { @@ -2406,7 +2588,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * */ - return make_ok3(env, atom_true, MKBIN(env, bufP)); + data = MKBIN(env, bufP); + + return make_ok3(env, atom_true, data); } @@ -2416,6 +2600,89 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, int save_errno = sock_errno(); + if (save_errno == ECONNRESET) { + + /* +++ Oups - closed +++ */ + + /* + * + * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING + * PROCESS, WE NEED TO INFORM IT!!! + * + * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! + * + * + */ + + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_STOP), + descP, NULL, recvRef); + + return make_error(env, atom_closed); + + } else if ((save_errno == ERRNO_BLOCK) || + (save_errno == EAGAIN)) { + return make_error(env, atom_eagain); + } else { + return make_error2(env, save_errno); + } + + } else { + + /* +++ We did not fill the buffer +++ */ + + if (toRead == 0) { + + /* +++ We got a chunk of data but +++ + * +++ since we did not fill the +++ + * +++ buffer, we must split it +++ + * +++ into a sub-binary. +++ + */ + + data = MKBIN(env, bufP); + data = MKSBIN(env, data, 0, read); + + return make_ok3(env, atom_true, data); + + } else { + + /* +++ We got only a part of what was expected +++ + * +++ => receive more later. +++ */ + + return make_ok3(env, atom_false, MKBIN(env, bufP)); + } + } +} + + +/* The recvfrom function delivers one (1) message. If our buffer + * is to small, the message will be truncated. So, regardless + * if we filled the buffer or not, we have got what we are going + * to get regarding this message. + */ +static +ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, + SocketDescriptor* descP, + int read, + ErlNifBinary* bufP, + SocketAddress* fromAddrP, + unsigned int fromAddrLen, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM data; + + /* 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. + */ + + if (read < 0) { + + /* +++ Error handling +++ */ + + int save_errno = sock_errno(); + if (save_errno == ECONNRESET) { /* +++ Oups - closed +++ */ @@ -2445,9 +2712,29 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, } else { - /* +++ We got only a part of what was expected - receive more later +++ */ + /* +++ We sucessfully got a message - time to encode the address +++ */ + + ERL_NIF_TERM fromDomainT, fromSourceT; - return make_ok3(env, atom_false, MKBIN(env, bufP)); + encode_address(env, + fromAddrP, fromAddrLen, + &fromDomainT, &fromSourceT); + + if (read == bufP->size) { + data = MKBIN(env, bufP); + } else { + + /* +++ We got a chunk of data but +++ + * +++ since we did not fill the +++ + * +++ buffer, we must split it +++ + * +++ into a sub-binary. +++ + */ + + data = MKBIN(env, bufP); + data = MKSBIN(env, data, 0, read); + } + + return make_ok2(env, MKT3(env, fromDomainT, fromSourceT, data)); } } @@ -2469,7 +2756,8 @@ char* decode_send_addr(ErlNifEnv* env, int domain, ERL_NIF_TERM addr, int port, - SocketAddress** toAddrP) + SocketAddress** toAddrP, + unsigned int* toAddrLenP) { if (IS_ATOM(env, addr)) { unsigned int len; @@ -2493,7 +2781,8 @@ char* decode_send_addr(ErlNifEnv* env, } else if (IS_TUPLE(env, addr)) { /* We now know that the we have a proper address. */ - return decode_send_addr_tuple(env, domain, addr, port, *toAddrP); + return decode_send_addr_tuple(env, domain, addr, port, + *toAddrP, toAddrLenP); } else { return str_einval; } @@ -2505,7 +2794,8 @@ char* decode_send_addr_tuple(ErlNifEnv* env, int domain, ERL_NIF_TERM addr, int port, - SocketAddress* toAddrP) + SocketAddress* toAddrP, + unsigned int* toAddrLenP) { /* We handle two different tuples: * - size 4 (INET) @@ -2538,7 +2828,7 @@ char* decode_send_addr_tuple(ErlNifEnv* env, return decode_address_tuple(env, domain, addrt, port, - toAddrP); + toAddrP, toAddrLenP); } @@ -2551,7 +2841,8 @@ char* decode_address_tuple(ErlNifEnv* env, int domain, const ERL_NIF_TERM* addrt, int port, - SocketAddress* addrP) + SocketAddress* addrP, + unsigned int* addrLenP) { /* We now *know* that the size of the tuple is correct, @@ -2564,19 +2855,19 @@ char* decode_address_tuple(ErlNifEnv* env, int a, v; char laddr[4]; - sys_memzero((char*)&addrP->u, sizeof(struct sockaddr_in)); + sys_memzero((char*)addrP, sizeof(struct sockaddr_in)); #ifndef NO_SA_LEN - addrP->u.sai.sin_len = sizeof(struct sockaddr_in); + addrP->sai.sin_len = sizeof(struct sockaddr_in); #endif - addrP->u.sai.sin_family = domain; - addrP->u.sai.sin_port = sock_htons(port); + addrP->sai.sin_family = domain; + addrP->sai.sin_port = sock_htons(port); for (a = 0; a < 4; a++) { if (!GET_INT(env, addrt[a], &v)) return str_einval; laddr[a] = v; } - sys_memcpy(&addrP->u.sai.sin_addr, &laddr, sizeof(laddr)); - addrP->len = sizeof(struct sockaddr_in); + sys_memcpy(&addrP->sai.sin_addr, &laddr, sizeof(laddr)); + *addrLenP = sizeof(struct sockaddr_in); return NULL; } break; @@ -2587,13 +2878,13 @@ char* decode_address_tuple(ErlNifEnv* env, int a, v; char laddr[16]; - sys_memzero((char*)&addrP->u, sizeof(struct sockaddr_in6)); + sys_memzero((char*)addrP, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN - addrP->u.sai6.sin6_len = sizeof(struct sockaddr_in6); + addrP->sai6.sin6_len = sizeof(struct sockaddr_in6); #endif - addrP->u.sai6.sin6_family = domain; - addrP->u.sai6.sin6_port = sock_htons(port); - addrP->u.sai6.sin6_flowinfo = 0; + addrP->sai6.sin6_family = domain; + addrP->sai6.sin6_port = sock_htons(port); + addrP->sai6.sin6_flowinfo = 0; /* The address tuple is of size 8 * and each element is a two byte integer */ @@ -2603,8 +2894,8 @@ char* decode_address_tuple(ErlNifEnv* env, laddr[a*2 ] = ((v >> 8) & 0xFF); laddr[a*2+1] = (v & 0xFF); } - sys_memcpy(&addrP->u.sai6.sin6_addr, &laddr, sizeof(laddr)); - addrP->len = sizeof(struct sockaddr_in6); + sys_memcpy(&addrP->sai6.sin6_addr, &laddr, sizeof(laddr)); + *addrLenP = sizeof(struct sockaddr_in6); return NULL; } break; @@ -2616,6 +2907,131 @@ char* decode_address_tuple(ErlNifEnv* env, } +/* Encode the 4- or 8-element address tuple from the socket address structure. + * + * This function is called when we have received a message. So, if we for some + * reason fail to decode the address or parts of it, it makes more sense to + * return with "undefined" for the values rather then fail completely (and not + * deliver the received message). + * + * Returns two things (assuming the encode works): + * + * Domain: inet | inet6 | local + * Source: {Address, Port} | string() + * + */ +static +void encode_address(ErlNifEnv* env, + SocketAddress* addrP, + unsigned int addrLen, + ERL_NIF_TERM* domainT, + ERL_NIF_TERM* sourceT) +{ + short port; + + switch (addrP->sa.sa_family) { + + /* +++ inet (IPv4) +++ */ + + case AF_INET: + if (addrLen >= sizeof(struct sockaddr_in)) { + ERL_NIF_TERM addrT, portT; + unsigned int i; + ERL_NIF_TERM at4[4]; + char* a4 = (char*) &addrP->sai.sin_addr; + + port = sock_ntohs(addrP->sai.sin_port); + for (i = 0; i < 4; i++) { + at4[i] = MKI(env, a4[i]); + } + + *domainT = MKA(env, "inet"); // Shall we encode these? See decode + addrT = MKT4(env, at4[0], at4[1], at4[2], at4[3]); + portT = MKI(env, port); + *sourceT = MKT2(env, addrT, portT); + } else { + *domainT = atom_undefined; + *sourceT = atom_undefined; + } + break; + + + /* +++ inet6 (IPv6) +++ */ + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + if (addrLen >= sizeof(struct sockaddr_in6)) { + ERL_NIF_TERM addrT, portT; + unsigned int i; + ERL_NIF_TERM at6[8]; + char* a16 = (char*) &addrP->sai6.sin6_addr; + + port = sock_ntohs(addrP->sai6.sin6_port); + /* The address tuple is of size 8 + * and each element is a two byte integer + */ + for (i = 0; i < 8; i++) { + // at6[i] = MKI(env, get_int16(a16[i*2])); + at6[i] = MKI(env, get_int16(a16 + i*2)); + } + + *domainT = MKA(env, "inet6"); // Shall we encode these? See decode + addrT = MKT8(env, + at6[0], at6[1], at6[2], at6[3], + at6[4], at6[5], at6[6], at6[7]); + portT = MKI(env, port); + *sourceT = MKT2(env, addrT, portT); + } else { + *domainT = atom_undefined; + *sourceT = atom_undefined; + } + break; +#endif + + /* +++ local (Unix Domain Sockets) +++ */ + +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + { + size_t n, m; + + *domainT = MKA(env, "local"); + if (addrLen < offsetof(struct sockaddr_un, sun_path)) { + *sourceT = atom_undefined; + } else { + n = addrLen - offsetof(struct sockaddr_un, sun_path); + if (255 < n) { + *sourceT = atom_undefined; + } else { + m = my_strnlen(addrP->sal.sun_path, n); +#ifdef __linux__ + /* Assume that the address is a zero terminated string, + * except when the first byte is \0 i.e the string length is 0, + * then use the reported length instead. + * This fix handles Linux's nonportable + * abstract socket address extension. + */ + if (m == 0) { + m = n; + } +#endif + + *sourceT = MKSL(env, addrP->sal.sun_path, m); + } + } + } + break; +#endif + + default: + *domainT = atom_undefined; + *sourceT = atom_undefined; + break; + + } /* switch (addrP->sa.sa_family) */ + +} + /* Decode the address when its an atom. * Currently we only accept two atoms: 'any' and 'loopback' @@ -2626,7 +3042,8 @@ char* decode_address_atom(ErlNifEnv* env, char* addr, int addrLen, int port, - SocketAddress* localP) + SocketAddress* addrP, + unsigned int* addrLenP) { BOOLEAN_T any; @@ -2649,14 +3066,14 @@ char* decode_address_atom(ErlNifEnv* env, } else { addr.s_addr = sock_htonl(INADDR_LOOPBACK); } - sys_memzero((char*) localP, sizeof(struct sockaddr_in)); + sys_memzero((char*) addrP, sizeof(struct sockaddr_in)); #ifndef NO_SA_LEN - localP->u.sai.sin_len = sizeof(struct sockaddr_in6); + addrP->sai.sin_len = sizeof(struct sockaddr_in6); #endif - localP->u.sai.sin_family = domain; - localP->u.sai.sin_port = sock_htons(port); - localP->u.sai.sin_addr.s_addr = addr.s_addr; - localP->len = sizeof(struct sockaddr_in); + addrP->sai.sin_family = domain; + addrP->sai.sin_port = sock_htons(port); + addrP->sai.sin_addr.s_addr = addr.s_addr; + *addrLenP = sizeof(struct sockaddr_in); } break; @@ -2669,15 +3086,15 @@ char* decode_address_atom(ErlNifEnv* env, } else { paddr = &in6addr_loopback; } - sys_memzero((char*)localP, sizeof(struct sockaddr_in6)); + sys_memzero((char*)addrP, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN - localP->u.sai6.sin6_len = sizeof(struct sockaddr_in6); + addrP->sai6.sin6_len = sizeof(struct sockaddr_in6); #endif - localP->u.sai6.sin6_family = domain; - localP->u.sai6.sin6_port = sock_htons(port); - localP->u.sai6.sin6_flowinfo = 0; - localP->u.sai6.sin6_addr = *paddr; - localP->len = sizeof(struct sockaddr_in6); + addrP->sai6.sin6_family = domain; + addrP->sai6.sin6_port = sock_htons(port); + addrP->sai6.sin6_flowinfo = 0; + addrP->sai6.sin6_addr = *paddr; + *addrLenP = sizeof(struct sockaddr_in6); } break; #endif @@ -2709,12 +3126,23 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->currentWriterP = NULL; // currentWriter not used descP->writersQ.first = NULL; descP->writersQ.last = NULL; + descP->isWritable = TRUE; + descP->writePkgCnt = 0; + descP->writeByteCnt = 0; + descP->writeTries = 0; + descP->writeWaits = 0; + descP->writeFails = 0; sprintf(buf, "socket[r,%d]", sock); descP->readMtx = MCREATE(buf); descP->currentReaderP = NULL; // currentReader not used descP->readersQ.first = NULL; descP->readersQ.last = NULL; + descP->isReadable = TRUE; + descP->readPkgCnt = 0; + descP->readByteCnt = 0; + descP->readTries = 0; + descP->readWaits = 0; sprintf(buf, "socket[acc,%d]", sock); descP->accMtx = MCREATE(buf); @@ -2722,22 +3150,12 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->acceptorsQ.first = NULL; descP->acceptorsQ.last = NULL; - descP->iow = FALSE; - descP->dbg = SOCKET_DEBUG_DEFAULT; - descP->isWritable = TRUE; - descP->isReadable = TRUE; - descP->writePkgCnt = 0; - descP->writeByteCnt = 0; - descP->writeTries = 0; - descP->writeWaits = 0; - descP->writeFails = 0; - descP->readPkgCnt = 0; - descP->readByteCnt = 0; - descP->readTries = 0; - descP->readWaits = 0; - - descP->sock = sock; - descP->event = event; + descP->rBufSz = SOCKET_RECV_BUFFER_SIZE_DEFAULT; + descP->iow = FALSE; + descP->dbg = SOCKET_DEBUG_DEFAULT; + + descP->sock = sock; + descP->event = event; } @@ -2931,10 +3349,6 @@ BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags) tmp |= MSG_DONTROUTE; break; - case SOCKET_SEND_FLAG_DONTWAIT: - tmp |= MSG_DONTWAIT; - break; - case SOCKET_SEND_FLAG_EOR: tmp |= MSG_EOR; break; @@ -2979,10 +3393,6 @@ BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags) tmp |= MSG_CMSG_CLOEXEC; break; - case SOCKET_RECV_FLAG_DONTWAIT: - tmp |= MSG_DONTWAIT; - break; - case SOCKET_RECV_FLAG_ERRQUEUE: tmp |= MSG_ERRQUEUE; break; @@ -2999,10 +3409,6 @@ BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags) tmp |= MSG_TRUNC; break; - case SOCKET_RECV_FLAG_WAITALL: - tmp |= MSG_WAITALL; - break; - default: return FALSE; } @@ -3015,6 +3421,18 @@ BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags) } +#if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) +/* strnlen doesn't exist everywhere */ +static +size_t my_strnlen(const char *s, size_t maxlen) +{ + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; +} +#endif + /* Create an ok two (2) tuple in the form: {ok, Any}. * The second element (Any) is already in the form of an -- cgit v1.2.3 From 599a320f630991823fc28b6a8a9f09851e261fed Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Apr 2018 17:38:52 +0200 Subject: [socket-nif] "Completed" the close function There is probably a lot of things left to be here. For instance the handling of ECONNRESET when reading (recv and recvfrom). Also some stuff about setopt and getopt. --- erts/emulator/nifs/common/socket_nif.c | 542 ++++++++++++++++++++++++++++++--- 1 file changed, 496 insertions(+), 46 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 6e6851a608..d55de9ff4e 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -185,6 +185,10 @@ typedef unsigned int BOOLEAN_T; #define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ (((unsigned char*) (s))[1])) +#define SASSERT(e) \ + ((void) ((e) ? 1 : (xabort(#e, __func__, __FILE__, __LINE__), 0))) + + /* Debug stuff... */ #define SOCKET_NIF_DEBUG_DEFAULT TRUE #define SOCKET_DEBUG_DEFAULT TRUE @@ -235,6 +239,7 @@ typedef unsigned long long llu_t; #define SOCKET_FLAG_CON 0x0010 #define SOCKET_FLAG_ACC 0x0020 #define SOCKET_FLAG_BUSY 0x0040 +#define SOCKET_FLAG_CLOSE 0x0080 #define SOCKET_STATE_CLOSED (0) #define SOCKET_STATE_OPEN (SOCKET_FLAG_OPEN) @@ -242,6 +247,7 @@ typedef unsigned long long llu_t; #define SOCKET_STATE_LISTENING (SOCKET_STATE_OPEN | SOCKET_FLAG_LISTEN) #define SOCKET_STATE_CONNECTING (SOCKET_STATE_OPEN | SOCKET_FLAG_CON) #define SOCKET_STATE_ACCEPTING (SOCKET_STATE_LISTENING | SOCKET_FLAG_ACC) +#define SOCKET_STATE_CLOSING (SOCKET_FLAG_CLOSE) #define IS_OPEN(d) \ (((d)->state & SOCKET_FLAG_OPEN) == SOCKET_FLAG_OPEN) @@ -331,6 +337,7 @@ typedef union { #define MALLOC(SZ) enif_alloc((SZ)) #define FREE(P) enif_free((P)) + #define MKA(E,S) enif_make_atom((E), (S)) #define MKBIN(E,B) enif_make_binary((E), (B)) #define MKI(E,I) enif_make_int((E), (I)) @@ -343,9 +350,15 @@ typedef union { #define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) #define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) + #define MCREATE(N) enif_mutex_create((N)) +#define MDESTROY(M) enif_mutex_destroy((M)) #define MLOCK(M) enif_mutex_lock((M)) #define MUNLOCK(M) enif_mutex_unlock((M)) + +#define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) +#define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) + #define SELECT(E,FD,M,O,P,R) \ if (enif_select((E), (FD), (M), (O), (P), (R)) < 0) \ return enif_make_badarg((E)); @@ -375,13 +388,16 @@ typedef union { #ifdef __WIN32__ -/* *** Windown macros *** */ +/* *** Windows macros *** */ #define sock_accept(s, addr, len) \ make_noninheritable_handle(accept((s), (addr), (len))) #define sock_bind(s, addr, len) bind((s), (addr), (len)) #define sock_close(s) closesocket((s)) +#define sock_close_event(e) WSACloseEvent(e) #define sock_connect(s, addr, len) connect((s), (addr), (len)) +#define sock_create_event(s) WSACreateEvent() +#define sock_errno() WSAGetLastError() #define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) #define sock_htons(x) htons((x)) #define sock_htonl(x) htonl((x)) @@ -397,16 +413,16 @@ typedef union { #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) -#define sock_errno() WSAGetLastError() -#define sock_create_event(s) WSACreateEvent() #define SET_BLOCKING(s) ioctlsocket(s, FIONBIO, &zero_value) #define SET_NONBLOCKING(s) ioctlsocket(s, FIONBIO, &one_value) static unsigned long zero_value = 0; static unsigned long one_value = 1; + #else /* !__WIN32__ */ + #ifdef HAS_ACCEPT4 // We have to figure out what the flags are... #define sock_accept(s, addr, len) accept4((s), (addr), (len), (SOCK_CLOEXEC)) @@ -415,7 +431,10 @@ static unsigned long one_value = 1; #endif #define sock_bind(s, addr, len) bind((s), (addr), (len)) #define sock_close(s) close((s)) +#define sock_close_event(e) /* do nothing */ #define sock_connect(s, addr, len) connect((s), (addr), (len)) +#define sock_create_event(s) (s) /* return file descriptor */ +#define sock_errno() errno #define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) #define sock_htons(x) htons((x)) #define sock_htonl(x) htonl((x)) @@ -430,10 +449,6 @@ static unsigned long one_value = 1; #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) -#define sock_errno() errno -#define sock_create_event(s) (s) /* return file descriptor */ - - #endif /* !__WIN32__ */ #ifdef HAVE_SOCKLEN_T @@ -470,7 +485,7 @@ typedef struct { } SocketRequestor; typedef struct socket_request_queue_element { - struct socket_request_queue_element* next; + struct socket_request_queue_element* nextP; SocketRequestor data; } SocketRequestQueueElement; @@ -534,6 +549,12 @@ typedef struct { BOOLEAN_T iow; // Inform On Wrap BOOLEAN_T dbg; + /* +++ Close stuff +++ */ + ErlNifMutex* closeMtx; + ErlNifPid closerPid; + ErlNifMonitor closerMon; + ERL_NIF_TERM closeRef; + } SocketDescriptor; @@ -609,6 +630,9 @@ static ERL_NIF_TERM nif_getsockopt(ErlNifEnv* env, static ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +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[]); @@ -660,6 +684,8 @@ static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, ERL_NIF_TERM recvRef, uint16_t bufSz, int flags); +static ERL_NIF_TERM nclose(ErlNifEnv* env, + SocketDescriptor* descP); static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -682,6 +708,8 @@ static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP); +static ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, + SocketDescriptor* descP); static char* decode_laddress(ErlNifEnv* env, @@ -730,6 +758,12 @@ static void encode_address(ErlNifEnv* env, ERL_NIF_TERM* fromDomainT, ERL_NIF_TERM* fromSourceT); +static void inform_waiting_procs(ErlNifEnv* env, + SocketDescriptor* descP, + SocketRequestQueue* q, + BOOLEAN_T free, + ERL_NIF_TERM msg); + static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err); static SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event); @@ -773,6 +807,19 @@ 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(ErlNifEnv* env, + ERL_NIF_TERM msg, + ErlNifPid* pid); + +static void xabort(const char* expr, + const char* func, + const char* file, + int line); static BOOLEAN_T extract_item_on_load(ErlNifEnv* env, ERL_NIF_TERM map, @@ -810,10 +857,14 @@ 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_ok[] = "ok"; +static char str_select[] = "select"; +static char str_timeout[] = "timeout"; static char str_true[] = "true"; static char str_undefined[] = "undefined"; @@ -822,19 +873,25 @@ static char str_eagain[] = "eagain"; static char str_eafnosupport[] = "eafnosupport"; static char str_einval[] = "einval"; static char str_eisconn[] = "eisconn"; +static char str_enotclosing[] = "enotclosing"; static char str_enotconn[] = "enotconn"; static char str_exalloc[] = "exalloc"; static char str_exbadstate[] = "exbadstate"; static char str_exbusy[] = "exbusy"; static char str_exmon[] = "exmonitor"; // failed monitor static char str_exself[] = "exself"; // failed self +static char str_exsend[] = "exsend"; // failed send /* *** Atoms *** */ +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_ok; +static ERL_NIF_TERM atom_select; +static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_undefined; @@ -842,12 +899,14 @@ static ERL_NIF_TERM atom_eagain; static ERL_NIF_TERM atom_eafnosupport; static ERL_NIF_TERM atom_einval; static ERL_NIF_TERM atom_eisconn; +static ERL_NIF_TERM atom_enotclosing; static ERL_NIF_TERM atom_enotconn; static ERL_NIF_TERM atom_exalloc; static ERL_NIF_TERM atom_exbadstate; static ERL_NIF_TERM atom_exbusy; static ERL_NIF_TERM atom_exmon; static ERL_NIF_TERM atom_exself; +static ERL_NIF_TERM atom_exsend; /* *** Sockets *** */ @@ -1059,9 +1118,9 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, if (enif_self(env, &descP->ctrlPid) == NULL) return make_error(env, atom_exself); - if (enif_monitor_process(env, descP, - &descP->ctrlPid, - &descP->ctrlMon) > 0) + if (MONP(env, descP, + &descP->ctrlPid, + &descP->ctrlMon) > 0) return make_error(env, atom_exmon); @@ -1794,9 +1853,9 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, /* *** Try again later *** */ descP->currentAcceptor.pid = caller; - if (enif_monitor_process(env, descP, - &descP->currentAcceptor.pid, - &descP->currentAcceptor.mon) > 0) + if (MONP(env, descP, + &descP->currentAcceptor.pid, + &descP->currentAcceptor.mon) > 0) return make_error(env, atom_exmon); descP->currentAcceptor.ref = ref; @@ -1865,9 +1924,9 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, enif_release_resource(accDescP); // We should really store a reference ... accDescP->ctrlPid = caller; - if (enif_monitor_process(env, accDescP, - &accDescP->ctrlPid, - &accDescP->ctrlMon) > 0) { + if (MONP(env, accDescP, + &accDescP->ctrlPid, + &accDescP->ctrlMon) > 0) { sock_close(accSock); return make_error(env, atom_exmon); } @@ -1966,9 +2025,9 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, enif_release_resource(accDescP); // We should really store a reference ... accDescP->ctrlPid = caller; - if (enif_monitor_process(env, accDescP, - &accDescP->ctrlPid, - &accDescP->ctrlMon) > 0) { + if (MONP(env, accDescP, + &accDescP->ctrlPid, + &accDescP->ctrlMon) > 0) { sock_close(accSock); return make_error(env, atom_exmon); } @@ -2480,6 +2539,189 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * nif_close + * + * Description: + * Close a (socket) file descriptor. + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + */ + +static +ERL_NIF_TERM nif_close(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + + if ((argc != 1) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + + return nclose(env, descP); +} + + +static +ERL_NIF_TERM nclose(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM reply, reason; + BOOLEAN_T doClose; + int selectRes; + + MLOCK(descP->closeMtx); + + if (descP->state == SOCKET_STATE_CLOSED) { + reason = atom_closed; + doClose = FALSE; + } else if (descP->state == SOCKET_STATE_CLOSING) { + reason = atom_closing; + doClose = FALSE; + } else { + + /* Store the PID of the caller, + * since we need to inform it when we + * (that is, the stop callback function) + * completes. + */ + + if (enif_self(env, &descP->closerPid) == NULL) { + MUNLOCK(descP->closeMtx); + return make_error(env, atom_exself); + } + + /* Monitor the caller, since we should complete this operation even if + * the caller dies (for whatever reason). + */ + + if (MONP(env, descP, + &descP->closerPid, + &descP->closerMon) > 0) { + MUNLOCK(descP->closeMtx); + return make_error(env, atom_exmon); + } + + descP->state = SOCKET_STATE_CLOSING; + doClose = TRUE; + } + + MUNLOCK(descP->closeMtx); + + if (doClose) { + descP->closeRef = MKREF(env); + selectRes = enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), + descP, NULL, descP->closeRef); + if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { + /* Prep done - inform the caller it can finalize (close) directly */ + reply = atom_ok; + } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { + /* The stop callback function has been *scheduled* which means that we + * have to wait for it to complete. */ + reply = make_ok2(env, descP->closeRef); + } else { + /* + * + * WE SHOULD REALLY HAVE A WAY TO CLOBBER THE SOCKET, + * SO WE DON'T LET STUFF LEAK. + * NOW, BECAUSE WE FAILED TO SELECT, WE CANNOT FINISH + * THE CLOSE, WHAT TO DO? ABORT? + * + * + */ + reason = MKT2(env, atom_select, MKI(env, selectRes)); + reply = make_error(env, reason); + } + } else { + reply = make_error(env, reason); + } + + return reply; +} + + + +/* ---------------------------------------------------------------------- + * nif_finalize_close + * + * Description: + * Perform the actual socket close! + * Note that this function is executed in a dirfty scheduler. + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + */ +static +ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 1) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + + return nfinalize_close(env, descP); + +} + + +/* *** nfinalize_close *** + * Perform the final step in the socket close. + */ +static +ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM reply; + + if (descP->state == SOCKET_STATE_CLOSED) + return atom_ok; + + if (descP->state != SOCKET_STATE_CLOSING) + return make_error(env, atom_enotclosing); + + /* This nif is executed in a dirty scheduler just so that + * it can "hang" (whith minumum effect on the VM) while the + * kernel writes our buffers. IF we have set the linger option + * for this ({true, integer() > 0}). For this to work we must + * be blocking... + */ + SET_BLOCKING(descP->sock); + + if (sock_close(descP->sock) != 0) { + int save_errno = sock_errno(); + + if (save_errno != ERRNO_BLOCK) { + /* Not all data in the buffers where sent, + * make sure the caller gets this. + */ + reply = make_error(env, atom_timeout); + } else { + reply = make_error2(env, save_errno); + } + } else { + reply = atom_ok; + } + sock_close_event(descP->event); + + descP->sock = INVALID_SOCKET; + descP->event = INVALID_EVENT; + + descP->state = SOCKET_STATE_CLOSED; + + return reply; +} + + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -2610,10 +2852,16 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * PROCESS, WE NEED TO INFORM IT!!! * * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! + * HANDLED BY THE STOP (CALLBACK) FUNCTION? + * + * WE DON'T NEED TO WAIT FOR OUTPUT TO BE WRITTEN, + * JUST ABORT THE SOCKET!!! * * */ + descP->state = SOCKET_STATE_CLOSING; + SELECT(env, descP->sock, (ERL_NIF_SELECT_STOP), @@ -3150,6 +3398,9 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->acceptorsQ.first = NULL; descP->acceptorsQ.last = NULL; + sprintf(buf, "socket[close,%d]", sock); + descP->closeMtx = MCREATE(buf); + descP->rBufSz = SOCKET_RECV_BUFFER_SIZE_DEFAULT; descP->iow = FALSE; descP->dbg = SOCKET_DEBUG_DEFAULT; @@ -3491,6 +3742,65 @@ ERL_NIF_TERM make_error2(ErlNifEnv* env, int err) } +/* Send an error closed message to the specified process: + * + * 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: + * + * {error, Reason} + * + * 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, + ErlNifPid* pid) +{ + ERL_NIF_TERM msg = enif_make_tuple2(env, atom_error, reason); + + return send_msg(env, msg, pid); +} + + +/* Send a message to the specified process. + */ +static +char* send_msg(ErlNifEnv* env, + ERL_NIF_TERM msg, + ErlNifPid* pid) +{ + if (!enif_send(env, pid, NULL, msg)) + return str_exsend; + else + return NULL; +} + + +static +void xabort(const char* expr, + const char* func, + const char* file, + int line) +{ + fflush(stdout); + fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", + file, line, func, expr); + fflush(stderr); + abort(); +} + /* ---------------------------------------------------------------------- * C o u n t e r F u n c t i o n s @@ -3529,19 +3839,153 @@ BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc) static void socket_dtor(ErlNifEnv* env, void* obj) { + SocketDescriptor* descP = (SocketDescriptor*) obj; + + MDESTROY(descP->writeMtx); + MDESTROY(descP->readMtx); + MDESTROY(descP->accMtx); + MDESTROY(descP->closeMtx); } /* ========================================================================= * socket_stop - Callback function for resource stop * + * When the socket is stopped, we need to inform: + * + * * the controlling process + * * the current writer and any waiting writers + * * the current reader and any waiting readers + * * the current acceptor and any waiting acceptor + * + * Also, make sure no process gets the message twice + * (in case it is, for instance, both controlling process + * and a writer). + * + * + * We do not handle linger-issues yet! So anything in the out + * buffers will be left for the OS to solve... + * Do we need a special "close"-thread? Dirty scheduler? + * */ static void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) { + SocketDescriptor* descP = (SocketDescriptor*) obj; + ERL_NIF_TERM errClosed = MKT2(env, atom_error, atom_closed); + + MLOCK(descP->writeMtx); + MLOCK(descP->readMtx); + MLOCK(descP->accMtx); + MLOCK(descP->closeMtx); + + + descP->state = SOCKET_STATE_CLOSING; + descP->isReadable = FALSE; + descP->isWritable = FALSE; + + + /* We should check that we actually have a monitor. + * This *should* be done with a "NULL" monitor value, + * which there currently is none... + */ + DEMONP(env, descP, &descP->ctrlMon); + + if (descP->currentWriterP != NULL) { + /* We have a (current) writer and *may* therefor also have + * writers waiting. + */ + + SASSERT( (NULL == send_msg_error_closed(env, &descP->currentWriter.pid)) ); + + /* And also deal with the waiting writers (in the same way) */ + inform_waiting_procs(env, descP, &descP->writersQ, TRUE, errClosed); + } + + if (descP->currentReaderP != NULL) { + + /* We have a (current) reader and *may* therefor also have + * readers waiting. + */ + + SASSERT( (NULL == send_msg_error_closed(env, &descP->currentReader.pid)) ); + + /* And also deal with the waiting readers (in the same way) */ + inform_waiting_procs(env, descP, &descP->readersQ, TRUE, errClosed); + } + + if (descP->currentAcceptorP != NULL) { + /* We have a (current) acceptor and *may* therefor also have + * acceptors waiting. + */ + + SASSERT( (NULL == send_msg_error_closed(env, &descP->currentAcceptor.pid)) ); + + /* And also deal with the waiting acceptors (in the same way) */ + inform_waiting_procs(env, descP, &descP->acceptorsQ, TRUE, errClosed); + } + + + if (descP->sock != INVALID_SOCKET) { + + /* +++ send close message to the waiting process +++ + * + * {close, CloseRef} + * + */ + + send_msg(env, MKT2(env, atom_close, descP->closeRef), &descP->closerPid); + + DEMONP(env, descP, &descP->closerMon); + } + + + MUNLOCK(descP->closeMtx); + MUNLOCK(descP->accMtx); + MUNLOCK(descP->readMtx); + MUNLOCK(descP->writeMtx); + } +/* This function traverse the queue and sends the specified + * message to each member, and if the 'free' argument is TRUE, + * the queue will be emptied. + */ +static +void inform_waiting_procs(ErlNifEnv* env, + SocketDescriptor* descP, + SocketRequestQueue* q, + BOOLEAN_T free, + ERL_NIF_TERM msg) +{ + SocketRequestQueueElement* currentP = q->first; + SocketRequestQueueElement* nextP; + + while (currentP != NULL) { + + /* + * Should we inform anyone if we fail to demonitor? + * NOT SURE WHAT THAT WOULD REPRESENT AND IT IS NOT + * IMPORTANT IN *THIS* CASE, BUT ITS A FUNDAMENTAL OP... + * + */ + + SASSERT( (NULL == send_msg(env, msg, ¤tP->data.pid)) ); + DEMONP(env, descP, ¤tP->data.mon); + nextP = currentP->nextP; + if (free) FREE(currentP); + currentP = nextP; + } + + if (free) { + q->first = NULL; + q->last = NULL; + } +} + + + /* ========================================================================= * socket_down - Callback function for resource down (monitored processes) * @@ -3564,31 +4008,32 @@ void socket_down(ErlNifEnv* env, static ErlNifFunc socket_funcs[] = { - // Some utility functions - {"nif_is_loaded", 0, nif_is_loaded}, - {"nif_info", 0, nif_info}, - // {"nif_debug", 1, nif_debug_}, - - // The proper "socket" interface - {"nif_open", 4, nif_open}, - {"nif_bind", 3, nif_bind}, - {"nif_connect", 3, nif_connect}, - {"nif_listen", 2, nif_listen}, - {"nif_accept", 2, nif_accept}, - {"nif_send", 4, nif_send}, - {"nif_sendto", 6, nif_sendto}, - {"nif_recv", 4, nif_recv}, - {"nif_recvfrom", 2, nif_recvfrom}, - {"nif_close", 1, nif_close}, - {"nif_setsockopt", 3, nif_setsockopt}, - {"nif_getsockopt", 2, nif_getsockopt}, - - /* "Extra" functions to "complete" the socket interface. - * For instance, the function nif_finalize_connection - * is called after the connect *select* has "completed". - */ - {"nif_finalize_connection", 1, nif_finalize_connection}, - {"nif_cancel", 2, nif_cancel}, + // Some utility functions + {"nif_is_loaded", 0, nif_is_loaded, 0}, + {"nif_info", 0, nif_info, 0}, + // {"nif_debug", 1, nif_debug_, 0}, + + // The proper "socket" interface + {"nif_open", 4, nif_open, 0}, + {"nif_bind", 3, nif_bind, 0}, + {"nif_connect", 3, 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_recv", 4, nif_recv, 0}, + {"nif_recvfrom", 2, nif_recvfrom, 0}, + {"nif_close", 1, nif_close, 0}, + {"nif_setsockopt", 3, nif_setsockopt, 0}, + {"nif_getsockopt", 2, nif_getsockopt, 0}, + + /* "Extra" functions to "complete" the socket interface. + * For instance, the function nif_finalize_connection + * is called after the connect *select* has "completed". + */ + {"nif_finalize_connection", 1, nif_finalize_connection, 0}, + {"nif_cancel", 2, nif_cancel, 0}, + {"nif_finalize_close", 1, nif_finalize_close, ERL_NIF_DIRTY_JOB_IO_BOUND} }; @@ -3656,7 +4101,9 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) // atom_active_once = MKA(env, str_active_once); // atom_binary = MKA(env, str_binary); // atom_buildDate = MKA(env, str_buildDate); + atom_close = MKA(env, str_close); atom_closed = MKA(env, str_closed); + atom_closing = MKA(env, str_closing); atom_error = MKA(env, str_error); atom_false = MKA(env, str_false); // atom_list = MKA(env, str_list); @@ -3665,7 +4112,9 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) // atom_once = MKA(env, str_once); // atom_passive = MKA(env, str_passive); // atom_receiver = MKA(env, str_receiver); + atom_select = MKA(env, str_select); // atom_tcp_closed = MKA(env, str_tcp_closed); + atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); atom_undefined = MKA(env, str_undefined); // atom_version = MKA(env, str_version); @@ -3675,6 +4124,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_eafnosupport = MKA(env, str_eafnosupport); atom_einval = MKA(env, str_einval); atom_eisconn = MKA(env, str_eisconn); + atom_enotclosing = MKA(env, str_enotclosing); atom_enotconn = MKA(env, str_enotconn); atom_exalloc = MKA(env, str_exalloc); atom_exbadstate = MKA(env, str_exbadstate); @@ -3682,7 +4132,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) // atom_exnotopen = MKA(env, str_exnotopen); atom_exmon = MKA(env, str_exmon); atom_exself = MKA(env, str_exself); - // atom_exsend = MKA(env, str_exsend); + atom_exsend = MKA(env, str_exsend); // For storing "global" things... // socketData.env = enif_alloc_env(); // We should really check -- cgit v1.2.3 From 82bfbdd7919e1aee360b76bdbaca17d2cf00ee73 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Apr 2018 14:54:03 +0200 Subject: [socket-nif] More close-related work There are still some questions regarding what hapopens when writing / reading from an (remote) closed socket (I talking about "properly" closed sockets). --- erts/emulator/nifs/common/socket_nif.c | 122 ++++++++++++++++++++++++++------- 1 file changed, 96 insertions(+), 26 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index d55de9ff4e..dadea3b6fb 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -554,6 +554,7 @@ typedef struct { ErlNifPid closerPid; ErlNifMonitor closerMon; ERL_NIF_TERM closeRef; + BOOLEAN_T closeLocal; } SocketDescriptor; @@ -762,7 +763,7 @@ static void inform_waiting_procs(ErlNifEnv* env, SocketDescriptor* descP, SocketRequestQueue* q, BOOLEAN_T free, - ERL_NIF_TERM msg); + ERL_NIF_TERM reason); static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err); @@ -812,6 +813,10 @@ static char* send_msg_error_closed(ErlNifEnv* env, 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, + ErlNifPid* pid); static char* send_msg(ErlNifEnv* env, ERL_NIF_TERM msg, ErlNifPid* pid); @@ -862,6 +867,7 @@ 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"; @@ -889,6 +895,7 @@ 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_nif_abort; static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_timeout; @@ -2605,8 +2612,9 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, return make_error(env, atom_exmon); } - descP->state = SOCKET_STATE_CLOSING; - doClose = TRUE; + descP->closeLocal = TRUE; + descP->state = SOCKET_STATE_CLOSING; + doClose = TRUE; } MUNLOCK(descP->closeMtx); @@ -2854,13 +2862,15 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! * HANDLED BY THE STOP (CALLBACK) FUNCTION? * - * WE DON'T NEED TO WAIT FOR OUTPUT TO BE WRITTEN, - * JUST ABORT THE SOCKET!!! + * SINCE THIS IS A REMOTE CLOSE, WE DON'T NEED TO WAIT + * FOR OUTPUT TO BE WRITTEN (NO ONE WILL READ), JUST + * ABORT THE SOCKET REGARDLESS OF LINGER??? * * */ - descP->state = SOCKET_STATE_CLOSING; + descP->closeLocal = FALSE; + descP->state = SOCKET_STATE_CLOSING; SELECT(env, descP->sock, @@ -2944,6 +2954,9 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, * */ + descP->closeLocal = FALSE; + descP->state = SOCKET_STATE_CLOSING; + SELECT(env, descP->sock, (ERL_NIF_SELECT_STOP), @@ -3768,9 +3781,29 @@ char* send_msg_error(ErlNifEnv* env, ERL_NIF_TERM reason, ErlNifPid* pid) { - ERL_NIF_TERM msg = enif_make_tuple2(env, atom_error, reason); + ERL_NIF_TERM msg = enif_make_tuple2(env, atom_error, reason); + + return send_msg(env, msg, pid); +} + + +/* Send an (nif-) abort message to the specified process: + * A message in the form: + * + * {nif_abort, Ref, Reason} + * + * This message is for processes that are waiting in the + * erlang API functions for a select message. + */ +static +char* send_msg_nif_abort(ErlNifEnv* env, + ERL_NIF_TERM ref, + ERL_NIF_TERM reason, + ErlNifPid* pid) +{ + ERL_NIF_TERM msg = MKT3(env, atom_nif_abort, ref, reason); - return send_msg(env, msg, pid); + return send_msg(env, msg, pid); } @@ -3871,8 +3904,7 @@ void socket_dtor(ErlNifEnv* env, void* obj) static void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) { - SocketDescriptor* descP = (SocketDescriptor*) obj; - ERL_NIF_TERM errClosed = MKT2(env, atom_error, atom_closed); + SocketDescriptor* descP = (SocketDescriptor*) obj; MLOCK(descP->writeMtx); MLOCK(descP->readMtx); @@ -3880,7 +3912,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) MLOCK(descP->closeMtx); - descP->state = SOCKET_STATE_CLOSING; + descP->state = SOCKET_STATE_CLOSING; // Just in case...??? descP->isReadable = FALSE; descP->isWritable = FALSE; @@ -3896,10 +3928,13 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * writers waiting. */ - SASSERT( (NULL == send_msg_error_closed(env, &descP->currentWriter.pid)) ); + SASSERT( (NULL == send_msg_nif_abort(env, + descP->currentWriter.ref, + atom_closed, + &descP->currentWriter.pid)) ); /* And also deal with the waiting writers (in the same way) */ - inform_waiting_procs(env, descP, &descP->writersQ, TRUE, errClosed); + inform_waiting_procs(env, descP, &descP->writersQ, TRUE, atom_closed); } if (descP->currentReaderP != NULL) { @@ -3908,10 +3943,13 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * readers waiting. */ - SASSERT( (NULL == send_msg_error_closed(env, &descP->currentReader.pid)) ); + SASSERT( (NULL == send_msg_nif_abort(env, + descP->currentReader.ref, + atom_closed, + &descP->currentReader.pid)) ); /* And also deal with the waiting readers (in the same way) */ - inform_waiting_procs(env, descP, &descP->readersQ, TRUE, errClosed); + inform_waiting_procs(env, descP, &descP->readersQ, TRUE, atom_closed); } if (descP->currentAcceptorP != NULL) { @@ -3919,24 +3957,50 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * acceptors waiting. */ - SASSERT( (NULL == send_msg_error_closed(env, &descP->currentAcceptor.pid)) ); + SASSERT( (NULL == send_msg_nif_abort(env, + descP->currentAcceptor.ref, + atom_closed, + &descP->currentAcceptor.pid)) ); /* And also deal with the waiting acceptors (in the same way) */ - inform_waiting_procs(env, descP, &descP->acceptorsQ, TRUE, errClosed); + inform_waiting_procs(env, descP, &descP->acceptorsQ, TRUE, atom_closed); } if (descP->sock != INVALID_SOCKET) { - /* +++ send close message to the waiting process +++ + /* + * * - * {close, CloseRef} + * WE NEED TO CHECK IF THIS OPERATION IS TRIGGERED + * LOCALLY (VIA A CALL TO CLOSE) OR REMOTELLY + * (VIA I.E. ECONSRESET). * + * */ - send_msg(env, MKT2(env, atom_close, descP->closeRef), &descP->closerPid); + if (descP->closeLocal) { + + /* +++ send close message to the waiting process +++ + * + * {close, CloseRef} + * + */ + + send_msg(env, MKT2(env, atom_close, descP->closeRef), &descP->closerPid); + + DEMONP(env, descP, &descP->closerMon); + + } else { - DEMONP(env, descP, &descP->closerMon); + /* + * + * + * ABORT? + * + * + */ + } } @@ -3949,29 +4013,34 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) /* This function traverse the queue and sends the specified - * message to each member, and if the 'free' argument is TRUE, - * the queue will be emptied. + * nif_abort message with the specified reason to each member, + * and if the 'free' argument is TRUE, the queue will be emptied. */ static void inform_waiting_procs(ErlNifEnv* env, SocketDescriptor* descP, SocketRequestQueue* q, BOOLEAN_T free, - ERL_NIF_TERM msg) + ERL_NIF_TERM reason) { SocketRequestQueueElement* currentP = q->first; SocketRequestQueueElement* nextP; while (currentP != NULL) { - /* + /* + * * Should we inform anyone if we fail to demonitor? * NOT SURE WHAT THAT WOULD REPRESENT AND IT IS NOT * IMPORTANT IN *THIS* CASE, BUT ITS A FUNDAMENTAL OP... + * * */ - SASSERT( (NULL == send_msg(env, msg, ¤tP->data.pid)) ); + SASSERT( (NULL == send_msg_nif_abort(env, + currentP->data.ref, + reason, + ¤tP->data.pid)) ); DEMONP(env, descP, ¤tP->data.mon); nextP = currentP->nextP; if (free) FREE(currentP); @@ -4108,6 +4177,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_false = MKA(env, str_false); // atom_list = MKA(env, str_list); // atom_mode = MKA(env, str_mode); + atom_nif_abort = MKA(env, str_nif_abort); atom_ok = MKA(env, str_ok); // atom_once = MKA(env, str_once); // atom_passive = MKA(env, str_passive); -- cgit v1.2.3 From b69436fc5970ff8fc749a37351e9d9c5a54f445e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 26 Apr 2018 16:16:24 +0200 Subject: [socket-nif] Completed the shutdown function --- erts/emulator/nifs/common/socket_nif.c | 254 +++++++++++++++++++++++++++++---- 1 file changed, 223 insertions(+), 31 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index dadea3b6fb..fdae18a513 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -193,6 +193,10 @@ typedef unsigned int BOOLEAN_T; #define SOCKET_NIF_DEBUG_DEFAULT TRUE #define SOCKET_DEBUG_DEFAULT TRUE +/* Counters and stuff (Don't know where to sent this stuff anyway) */ +#define SOCKET_NIF_IOW_DEFAULT FALSE + + /* Used in debug printouts */ #ifdef __WIN32__ #define LLU "%I64u" @@ -328,6 +332,11 @@ typedef union { #define SOCKET_PROTOCOL_UDP 3 #define SOCKET_PROTOCOL_SCTP 4 +/* shutdown how */ +#define SOCKET_SHUTDOWN_HOW_RD 0 +#define SOCKET_SHUTDOWN_HOW_WR 1 +#define SOCKET_SHUTDOWN_HOW_RDWR 2 + /* =================================================================== * * * @@ -412,6 +421,7 @@ typedef union { #define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) +#define sock_shutdown(s, how) shutdown((s), (how)) #define SET_BLOCKING(s) ioctlsocket(s, FIONBIO, &zero_value) @@ -448,6 +458,7 @@ static unsigned long one_value = 1; #define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) +#define sock_shutdown(s, how) shutdown((s), (how)) #endif /* !__WIN32__ */ @@ -563,10 +574,24 @@ typedef struct { * these things?) */ typedef struct { - /* These are for debugging, testing and the like */ - ERL_NIF_TERM version; - ERL_NIF_TERM buildDate; - BOOLEAN_T dbg; + /* These are for debugging, testing and the like */ + ERL_NIF_TERM version; + ERL_NIF_TERM buildDate; + BOOLEAN_T dbg; + + ErlNifMutex* cntMtx; + BOOLEAN_T iow; + uint32_t numSockets; + uint32_t numTypeDGrams; + uint32_t numTypeStreams; + uint32_t numTypeSeqPkg; + uint32_t numDomainLocal; + uint32_t numDomainInet; + uint32_t numDomainInet6; + uint32_t numProtoIP; + uint32_t numProtoTCP; + uint32_t numProtoUDP; + uint32_t numProtoSCTP; } SocketData; @@ -622,6 +647,9 @@ static ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, static ERL_NIF_TERM nif_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_setsockopt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -687,6 +715,9 @@ static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, int flags); static ERL_NIF_TERM nclose(ErlNifEnv* env, SocketDescriptor* descP); +static ERL_NIF_TERM nshutdown(ErlNifEnv* env, + SocketDescriptor* descP, + int how); static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -778,6 +809,7 @@ static int compare_pids(ErlNifEnv* env, static BOOLEAN_T edomain2domain(int edomain, int* domain); static BOOLEAN_T etype2type(int etype, int* type); static BOOLEAN_T eproto2proto(int eproto, int* proto); +static BOOLEAN_T ehow2how(unsigned int ehow, int* how); static BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags); static BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags); #ifdef HAVE_SETNS @@ -834,6 +866,9 @@ static BOOLEAN_T extract_item_on_load(ErlNifEnv* env, static BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def); +static BOOLEAN_T extract_iow_on_load(ErlNifEnv* env, + ERL_NIF_TERM map, + BOOLEAN_T def); static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); @@ -950,6 +985,7 @@ static SocketData socketData; * nif_recv(Sock, RecvRef, Length, Flags) * nif_recvfrom(Sock, Flags) * nif_close(Sock) + * nif_shutdown(Sock, How) * * And some functions to manipulate and retrieve socket options: * ------------------------------------------------------------- @@ -2730,6 +2766,69 @@ ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * nif_shutdown + * + * Description: + * Disable sends and/or receives on a socket. + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * How - What will be shutdown. + */ + +static +ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + unsigned int ehow; + int how; + + if ((argc != 2) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP) || + !GET_UINT(env, argv[1], &ehow)) { + return enif_make_badarg(env); + } + + if (!ehow2how(ehow, &how)) + return enif_make_badarg(env); + + return nshutdown(env, descP, how); +} + + + +static +ERL_NIF_TERM nshutdown(ErlNifEnv* env, + SocketDescriptor* descP, + int how) +{ + ERL_NIF_TERM reply; + + if (sock_shutdown(descP->sock, how) == 0) { + switch (how) { + case SHUT_RD: + descP->isReadable = FALSE; + break; + case SHUT_WR: + descP->isWritable = FALSE; + break; + case SHUT_RDWR: + descP->isReadable = FALSE; + descP->isWritable = FALSE; + break; + } + reply = atom_ok; + } else { + reply = make_error2(env, sock_errno()); + } + + return reply; +} + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -3533,8 +3632,8 @@ BOOLEAN_T eproto2proto(int eproto, int* proto) return FALSE; } - return TRUE; -} + return TRUE; + } #ifdef HAVE_SETNS @@ -3685,6 +3784,35 @@ BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags) } + +/* eproto2proto - convert internal (erlang) protocol to (proper) protocol + * + * Note that only a subset is supported. + */ +static +BOOLEAN_T ehow2how(unsigned int ehow, int* how) +{ + switch (ehow) { + case SOCKET_SHUTDOWN_HOW_RD: + *how = SHUT_RD; + break; + + case SOCKET_SHUTDOWN_HOW_WR: + *how = SHUT_WR; + break; + + case SOCKET_SHUTDOWN_HOW_RDWR: + *how = SHUT_RDWR; + break; + + default: + return FALSE; + } + + return TRUE; + } + + #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) /* strnlen doesn't exist everywhere */ static @@ -3896,9 +4024,14 @@ void socket_dtor(ErlNifEnv* env, void* obj) * and a writer). * * + * * We do not handle linger-issues yet! So anything in the out * buffers will be left for the OS to solve... * Do we need a special "close"-thread? Dirty scheduler? + * + * What happens if we are "stopped" for another reason then 'close'? + * For instance, down? + * * */ static @@ -3985,6 +4118,11 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * * {close, CloseRef} * + * + * + * WHAT HAPPENS IF THE RECEIVER HAS DIED IN THE MEANTIME???? + * + * */ send_msg(env, MKT2(env, atom_close, descP->closeRef), &descP->closerPid); @@ -4093,6 +4231,7 @@ ErlNifFunc socket_funcs[] = {"nif_recv", 4, nif_recv, 0}, {"nif_recvfrom", 2, nif_recvfrom, 0}, {"nif_close", 1, nif_close, 0}, + {"nif_shutdown", 2, nif_shutdown, 0}, {"nif_setsockopt", 3, nif_setsockopt, 0}, {"nif_getsockopt", 2, nif_getsockopt, 0}, @@ -4121,38 +4260,74 @@ BOOLEAN_T extract_item_on_load(ErlNifEnv* env, return TRUE; } + static + BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) + { + ERL_NIF_TERM dbgKey = enif_make_atom(env, "debug"); + ERL_NIF_TERM dbgVal; + unsigned int len; + char d[16]; // Just in case... + + /* Extra the value of the debug property */ + if (!extract_item_on_load(env, map, dbgKey, &dbgVal)) + return def; + + /* Verify that the value is actually an atom */ + if (!enif_is_atom(env, dbgVal)) + return def; + + /* Verify that the value is of acceptable length */ + if (!(GET_ATOM_LEN(env, dbgVal, &len) && + (len > 0) && + (len <= sizeof("false")))) + return def; + + /* And finally try to extract the value */ + if (!GET_ATOM(env, dbgVal, d, sizeof(d))) + return def; + + if (strncmp(d, "true", len) == 0) + return TRUE; + else + return FALSE; + + } + + static -BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) +BOOLEAN_T extract_iow_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) { - ERL_NIF_TERM dbgKey = enif_make_atom(env, "debug"); - ERL_NIF_TERM dbgVal; - unsigned int len; - char d[16]; // Just in case... + ERL_NIF_TERM iowKey = enif_make_atom(env, "iow"); + ERL_NIF_TERM iowVal; + unsigned int len; + char b[16]; // Just in case... - /* Extra the value of the debug property */ - if (!extract_item_on_load(env, map, dbgKey, &dbgVal)) - return def; + /* Extra the value of the debug property */ + if (!extract_item_on_load(env, map, iowKey, &iowVal)) + return def; - /* Verify that the value is actually an atom */ - if (!enif_is_atom(env, dbgVal)) - return def; + /* Verify that the value is actually an atom */ + if (!enif_is_atom(env, iowVal)) + return def; - /* Verify that the value is of acceptable length */ - if (!(GET_ATOM_LEN(env, dbgVal, &len) && - (len > 0) && - (len <= sizeof("false")))) - return def; + /* Verify that the value is of acceptable length */ + if (!(GET_ATOM_LEN(env, iowVal, &len) && + (len > 0) && + (len <= sizeof("false")))) + return def; - /* And finally try to extract the value */ - if (!GET_ATOM(env, dbgVal, d, sizeof(d))) - return def; + /* And finally try to extract the value */ + if (!GET_ATOM(env, iowVal, b, sizeof(b))) + return def; + + if (strncmp(b, "true", len) == 0) + return TRUE; + else + return FALSE; + + } - if (strncmp(d, "true", len) == 0) - return TRUE; - else - return FALSE; -} /* ======================================================================= * load_info - A map of misc info (e.g global debug) @@ -4164,7 +4339,24 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) socketData.dbg = extract_debug_on_load(env, load_info, SOCKET_NIF_DEBUG_DEFAULT); - /* Misc atoms */ + /* +++ Global Counters +++ */ + socketData.cntMtx = MCREATE("socket[gcnt]"); + socketData.iow = extract_iow_on_load(env, + load_info, + SOCKET_NIF_IOW_DEFAULT); + socketData.numSockets = 0; + socketData.numTypeDGrams = 0; + socketData.numTypeStreams = 0; + socketData.numTypeSeqPkg = 0; + socketData.numDomainLocal = 0; + socketData.numDomainInet = 0; + socketData.numDomainInet6 = 0; + socketData.numProtoIP = 0; + socketData.numProtoTCP = 0; + socketData.numProtoUDP = 0; + socketData.numProtoSCTP = 0; + + /* +++ Misc atoms +++ */ // atom_active = MKA(env, str_active); // atom_active_n = MKA(env, str_active_n); // atom_active_once = MKA(env, str_active_once); -- cgit v1.2.3 From 8e14247bc5faf5abf67901b6ae5028f2b1897c65 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 4 May 2018 16:00:26 +0200 Subject: [socket-nif] Preliminary setopt *Very* partial setopt implementation. --- erts/emulator/nifs/common/socket_nif.c | 1029 ++++++++++++++++++++++++++++++-- 1 file changed, 992 insertions(+), 37 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index fdae18a513..532ac5c211 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -59,6 +59,7 @@ #include #include #include +#include #ifdef HAVE_UNISTD_H #include @@ -338,6 +339,26 @@ typedef union { #define SOCKET_SHUTDOWN_HOW_RDWR 2 +#define SOCKET_OPT_LEVEL_OTP 0 +#define SOCKET_OPT_LEVEL_SOCKET 1 +#define SOCKET_OPT_LEVEL_IP 2 +#define SOCKET_OPT_LEVEL_IPV6 3 +#define SOCKET_OPT_LEVEL_TCP 4 +#define SOCKET_OPT_LEVEL_UDP 5 +#define SOCKET_OPT_LEVEL_SCTP 6 + +#define SOCKET_OPT_OTP_DEBUG 0 +#define SOCKET_OPT_OTP_IOW 1 +#define SOCKET_OPT_SOCK_KEEPALIVE 0 +#define SOCKET_OPT_SOCK_LINGER 1 +#define SOCKET_OPT_IP_RECVTOS 0 +#define SOCKET_OPT_IP_ROUTER_ALERT 1 +#define SOCKET_OPT_IP_TOS 2 +#define SOCKET_OPT_IP_TTL 3 +#define SOCKET_OPT_IPV6_HOPLIMIT 0 +#define SOCKET_OPT_TCP_MAXSEG 0 + + /* =================================================================== * * * * Various enif macros * @@ -407,7 +428,7 @@ typedef union { #define sock_connect(s, addr, len) connect((s), (addr), (len)) #define sock_create_event(s) WSACreateEvent() #define sock_errno() WSAGetLastError() -#define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) +#define sock_getopt(s,l,o,v,ln) getsockopt((s),(l),(o),(v),(ln)) #define sock_htons(x) htons((x)) #define sock_htonl(x) htonl((x)) #define sock_listen(s, b) listen((s), (b)) @@ -421,6 +442,7 @@ typedef union { #define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) +#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln)) #define sock_shutdown(s, how) shutdown((s), (how)) @@ -458,6 +480,7 @@ static unsigned long one_value = 1; #define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) +#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln)) #define sock_shutdown(s, how) shutdown((s), (how)) #endif /* !__WIN32__ */ @@ -570,6 +593,27 @@ typedef struct { } SocketDescriptor; +#define SOCKET_OPT_VALUE_UNDEF 0 +#define SOCKET_OPT_VALUE_BOOL 1 +#define SOCKET_OPT_VALUE_INT 2 +#define SOCKET_OPT_VALUE_LINGER 3 +#define SOCKET_OPT_VALUE_BIN 4 + +typedef struct { + unsigned int tag; + union { + BOOLEAN_T boolVal; + int intVal; + struct linger lingerVal; + ErlNifBinary binVal; + } u; + /* + void* optValP; // Points to the actual data (above) + socklen_t optValLen; // The size of the option value + */ +} SocketOptValue; + + /* Global stuff (do we really need to "collect" * these things?) */ @@ -650,12 +694,12 @@ static ERL_NIF_TERM nif_close(ErlNifEnv* env, static ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM nif_setsockopt(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM nif_getsockopt(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_setopt(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_getopt(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[]); @@ -718,6 +762,22 @@ static ERL_NIF_TERM nclose(ErlNifEnv* env, static ERL_NIF_TERM nshutdown(ErlNifEnv* env, SocketDescriptor* descP, int how); +static ERL_NIF_TERM nsetopt(ErlNifEnv* env, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int opt, + SocketOptValue* valP); +static ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, + SocketDescriptor* descP, + int opt, + SocketOptValue* valP); +static ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + SocketOptValue* valP); static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -789,6 +849,15 @@ static void encode_address(ErlNifEnv* env, unsigned int fromAddrLen, ERL_NIF_TERM* fromDomainT, ERL_NIF_TERM* fromSourceT); +static BOOLEAN_T decode_sock_linger(ErlNifEnv* env, + ERL_NIF_TERM eVal, + struct linger* valP); +static BOOLEAN_T decode_ip_tos(ErlNifEnv* env, + ERL_NIF_TERM eVal, + int* val); +static BOOLEAN_T decode_bool(ErlNifEnv* env, + ERL_NIF_TERM eVal, + BOOLEAN_T* val); static void inform_waiting_procs(ErlNifEnv* env, SocketDescriptor* descP, @@ -796,6 +865,12 @@ static void inform_waiting_procs(ErlNifEnv* env, BOOLEAN_T free, ERL_NIF_TERM reason); +static int socket_setopt(int sock, + int level, + int opt, + const void* optVal, + const socklen_t optLen); + static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err); static SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event); @@ -812,6 +887,62 @@ static BOOLEAN_T eproto2proto(int eproto, int* proto); static BOOLEAN_T ehow2how(unsigned int ehow, int* how); static BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags); static BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags); +static BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, + int eLevel, + BOOLEAN_T* isOTP, + int* level); +static BOOLEAN_T eoptval2optval(ErlNifEnv* env, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* val); +static BOOLEAN_T eoptval2optval_otp(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP); +static BOOLEAN_T eoptval2optval_plain(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP); +static BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP); +static BOOLEAN_T eoptval2optval_ip(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP); +#if defined(SOL_IPV6) +static BOOLEAN_T eoptval2optval_ipv6(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP); +#endif +static BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP); +static BOOLEAN_T eoptval2optval_udp(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP); +#ifdef HAVE_SCTP +static BOOLEAN_T eoptval2optval_sctp(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP); +#endif #ifdef HAVE_SETNS static BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns); static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err); @@ -989,8 +1120,8 @@ static SocketData socketData; * * And some functions to manipulate and retrieve socket options: * ------------------------------------------------------------- - * nif_setsockopt/3 - * nif_getsockopt/2 + * nif_setopt/3 + * nif_getopt/2 * * And some socket admin functions: * ------------------------------------------------------------- @@ -2829,6 +2960,697 @@ ERL_NIF_TERM nshutdown(ErlNifEnv* env, } + + +/* ---------------------------------------------------------------------- + * nif_setopt + * + * Description: + * Set socket option. + * Its possible to use a "raw" mode (not encoded). That is, we do not + * interpret level, opt and value. They are passed "as is" to the + * setsockopt function call (the value arguments is assumed to be a + * binary, already encoded). + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * Encoded - Are the "arguments" encoded or not. + * Level - Level of the socket option. + * Opt - The socket option. + * Value - Value of the socket option (type depend on the option). + */ + +static +ERL_NIF_TERM nif_setopt(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + unsigned int eIsEncoded; + BOOLEAN_T isEncoded, isOTP; + int eLevel, level = -1; + int eOpt, opt = -1; + ERL_NIF_TERM eVal; + SocketOptValue val; + + if ((argc != 5) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP) || + !GET_UINT(env, argv[1], &eIsEncoded) || + !GET_INT(env, argv[2], &eLevel) || + !GET_INT(env, argv[3], &eOpt)) { + return enif_make_badarg(env); + } + eVal = argv[4]; + + isEncoded = ((eIsEncoded == 0) ? FALSE : TRUE); + + if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) + return make_error(env, atom_einval); + + if (!eoptval2optval(env, isEncoded, isOTP, level, eOpt, eVal, &opt, &val)) + return make_error(env, atom_einval); + + return nsetopt(env, descP, isEncoded, isOTP, level, opt, &val); +} + + +static +ERL_NIF_TERM nsetopt(ErlNifEnv* env, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int opt, + SocketOptValue* valP) +{ + ERL_NIF_TERM result; + int res; + + if (!isEncoded) { + res = socket_setopt(descP->sock, level, opt, + valP->u.binVal.data, valP->u.binVal.size); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + } else { + if (isOTP) { + /* These are not actual socket options, + * but options for our implementation. + */ + result = nsetopt_otp(env, descP, opt, valP); + } else { + /* Basically, call setsockopt(...) + * + * How do we know what type each option have? tag in value type? + * + */ + result = nsetopt_gen(env, descP, level, opt, valP); + } + } + + return result; +} + + +static +ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, + SocketDescriptor* descP, + int opt, + SocketOptValue* valP) +{ + ERL_NIF_TERM result; + + /* Make an idiot check just to be on the safe side... */ + if (valP->tag == SOCKET_OPT_VALUE_UNDEF) + return make_error(env, atom_einval); + + switch (opt) { + case SOCKET_OPT_OTP_DEBUG: + descP->dbg = valP->u.boolVal; + result = atom_ok; + break; + + case SOCKET_OPT_OTP_IOW: + descP->iow = valP->u.boolVal; + result = atom_ok; + break; + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +static +ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + SocketOptValue* valP) +{ + socklen_t optLen; + int res; + ERL_NIF_TERM result; + + switch (valP->tag) { + case SOCKET_OPT_VALUE_INT: + { + optLen = sizeof(valP->u.intVal); + res = socket_setopt(descP->sock, level, opt, + (void*) &valP->u.intVal, optLen); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + } + break; + + case SOCKET_OPT_VALUE_BIN: + { + optLen = valP->u.binVal.size; + res = socket_setopt(descP->sock, level, opt, + &valP->u.binVal.data, optLen); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + } + break; + + default: + result = make_error(env, atom_einval); + } + + return result; +} + + + +static +BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, + int eLevel, + BOOLEAN_T* isOTP, + int* level) +{ + BOOLEAN_T result; + + if (isEncoded) { + switch (eLevel) { + case SOCKET_OPT_LEVEL_OTP: + *isOTP = TRUE; + *level = -1; + result = TRUE; + break; + + case SOCKET_OPT_LEVEL_SOCKET: + *isOTP = FALSE; + *level = SOL_SOCKET; + result = TRUE; + break; + + case SOCKET_OPT_LEVEL_IP: + *isOTP = FALSE; +#if defined(SOL_IP) + *level = SOL_IP; +#else + *level = IPROTO_IP; +#endif + result = TRUE; + break; + +#if defined(SOL_IPV6) + case SOCKET_OPT_LEVEL_IPV6: + *isOTP = FALSE; + *level = SOL_IPV6; + result = TRUE; + break; +#endif + + case SOCKET_OPT_LEVEL_TCP: + *isOTP = FALSE; + *level = IPPROTO_TCP; + result = TRUE; + break; + + case SOCKET_OPT_LEVEL_UDP: + *isOTP = FALSE; + *level = IPPROTO_UDP; + result = TRUE; + break; + +#ifdef HAVE_SCTP + case SOCKET_OPT_LEVEL_SCTP: + *isOTP = FALSE; + *level = IPPROTO_SCTP; + result = TRUE; + break; +#endif + + default: + *isOTP = FALSE; + *level = -1; + result = FALSE; + break; + } + } else { + *isOTP = FALSE; + *level = eLevel; + result = TRUE; + } + + return result; +} + + +static +BOOLEAN_T eoptval2optval(ErlNifEnv* env, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP) +{ + if (isOTP) { + return eoptval2optval_otp(env, eOpt, eVal, opt, valP); + } else if (!isEncoded) { + return eoptval2optval_plain(env, eOpt, eVal, opt, valP); + } else { + switch (level) { + case SOL_SOCKET: + return eoptval2optval_socket(env, eOpt, eVal, opt, valP); + break; + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + return eoptval2optval_ip(env, eOpt, eVal, opt, valP); + break; + +#if defined(SOL_IPV6) + case SOL_IPV6: + return eoptval2optval_ipv6(env, eOpt, eVal, opt, valP); + break; +#endif + + case IPPROTO_TCP: + return eoptval2optval_tcp(env, eOpt, eVal, opt, valP); + break; + + case IPPROTO_UDP: + return eoptval2optval_udp(env, eOpt, eVal, opt, valP); + break; + +#ifdef HAVE_SCTP + case IPPROTO_SCTP: + return eoptval2optval_sctp(env, eOpt, eVal, opt, valP); + break; +#endif + + default: + *opt = -1; + return FALSE; + } + } +} + + + +static +BOOLEAN_T eoptval2optval_otp(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP) +{ + BOOLEAN_T result = FALSE; + + switch (eOpt) { + case SOCKET_OPT_OTP_IOW: + case SOCKET_OPT_OTP_DEBUG: + { + if (decode_bool(env, eVal, &valP->u.boolVal)) { + valP->tag = SOCKET_OPT_VALUE_BOOL; + result = TRUE; + } else { + result = FALSE; + } + *opt = eOpt; + } + break; + + default: + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + } + + return result; +} + + +static +BOOLEAN_T eoptval2optval_plain(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP) +{ + if (!GET_BIN(env, eVal, &valP->u.binVal)) + return FALSE; + valP->tag = SOCKET_OPT_VALUE_BIN; + *opt = eOpt; + + return TRUE; +} + + + +static +BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP) +{ + switch (eOpt) { +#if defined(SO_KEEPALIVE) + case SOCKET_OPT_SOCK_KEEPALIVE: + { + BOOLEAN_T val; + + if (decode_bool(env, eVal, &val)) { + *opt = SO_KEEPALIVE; + valP->tag = SOCKET_OPT_VALUE_INT; + valP->u.intVal = (val) ? 1 : 0; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + } + break; +#endif + +#if defined(SO_LINGER) + case SOCKET_OPT_SOCK_LINGER: + { + if (decode_sock_linger(env, eVal, &valP->u.lingerVal)) { + *opt = SO_LINGER; + valP->tag = SOCKET_OPT_VALUE_LINGER; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + } + break; +#endif + + default: + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } +} + + + +static +BOOLEAN_T eoptval2optval_ip(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP) +{ + switch (eOpt) { +#if defined(IP_RECVTOS) + case SOCKET_OPT_IP_RECVTOS: + { + BOOLEAN_T val; + + if (decode_bool(env, eVal, &val)) { + *opt = IP_RECVTOS; + valP->tag = SOCKET_OPT_VALUE_INT; + valP->u.intVal = (val) ? 1 : 0; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return TRUE; + } + } + break; +#endif + +#if defined(IP_ROUTER_ALERT) + case SOCKET_OPT_IP_ROUTER_ALERT: + if (GET_INT(env, eVal, &valP->u.intVal)) { + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = IP_ROUTER_ALERT; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + break; +#endif + +#if defined(IP_TOS) + case SOCKET_OPT_IP_TOS: + { + if (decode_ip_tos(env, eVal, &valP->u.intVal)) { + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = IP_TOS; + return TRUE; + } else { + *opt = -1; + return FALSE; + } + } + break; +#endif + +#if defined(IP_TTL) + case SOCKET_OPT_IP_TTL: + /* + * Should we care about the value? That is, if it is valid? + * And what is the valid range anyway for ttl? 0 - 255? + * + */ + if (!GET_INT(env, eVal, &valP->u.intVal)) + return FALSE; // PLACEHOLDER - We should really be more informative + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = IP_TTL; + return TRUE; + break; +#endif + + default: + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + +} + + + +#if defined(SOL_IPV6) +static +BOOLEAN_T eoptval2optval_ipv6(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP) +{ + BOOLEAN_T result = FALSE; + + switch (eOpt) { +#if defined(IPV6_HOPLIMIT) + case SOCKET_OPT_IPV6_HOPLIMIT: + { + BOOLEAN_T val; + + if (decode_bool(env, eVal, &val)) { + valP->tag = SOCKET_OPT_VALUE_INT; + valP->u.intVal = (val) ? 1 : 0; + *opt = IPV6_HOPLIMIT; + result = TRUE; + } else { + *opt = -1; + result = FALSE; + } + } + break; +#endif + + default: + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + result = FALSE; + break; + } + + return result; +} +#endif + + + +static +BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP) +{ + switch (eOpt) { +#if defined(TCP_MAXSEG) + case SOCKET_OPT_TCP_MAXSEG: + if (!GET_INT(env, eVal, &valP->u.intVal)) { + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = TCP_MAXSEG; + return TRUE; + } else { + return FALSE; + } + break; +#endif + + default: + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } +} + + + +/* +++ decode UDP socket options +++ + * Currently there are no such options, so this function + * is just a placeholder! + */ +static +BOOLEAN_T eoptval2optval_udp(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP) +{ + switch (eOpt) { + default: + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } +} + + + +#ifdef HAVE_SCTP +static +BOOLEAN_T eoptval2optval_sctp(ErlNifEnv* env, + int eOpt, + ERL_NIF_TERM eVal, + int* opt, + SocketOptValue* valP) +{ + switch (eOpt) { +#if defined(SCTP_AUTOCLOSE) + case SOCKET_OPT_SCTP_AUTOCLOSE: + if (!GET_INT(env, eVal, &valP->u.intVal)) + return FALSE; // PLACEHOLDER - We should really be more informative + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = SCTP_AUTOCLOSE; + return TRUE; + break; +#endif + + default: + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } +} +#endif + + + +/* +++ socket_setopt +++ + * + * + * The original code here had problems that possibly + * only occur if you abuse it for non-INET sockets, but anyway: + * a) If the getsockopt for SO_PRIORITY or IP_TOS failed, the actual + * requested setsockopt was never even attempted. + * b) If {get,set}sockopt for one of IP_TOS and SO_PRIORITY failed, + * but ditto for the other worked and that was actually the requested + * option, failure was still reported to erlang. + * + * + * + * The relations between SO_PRIORITY, TOS and other options + * is not what you (or at least I) would expect...: + * If TOS is set after priority, priority is zeroed. + * If any other option is set after tos, tos might be zeroed. + * Therefore, save tos and priority. If something else is set, + * restore both after setting, if tos is set, restore only + * prio and if prio is set restore none... All to keep the + * user feeling socket options are independent. + * + */ +static +int socket_setopt(int sock, int level, int opt, + const void* optVal, const socklen_t optLen) +{ + int res; + +#if defined(IP_TOS) && defined(SOL_IP) && defined(SO_PRIORITY) + int tmpIValPRIO; + int tmpIValTOS; + int resPRIO; + int resTOS; + SOCKLEN_T tmpArgSzPRIO = sizeof(tmpIValPRIO); + SOCKLEN_T tmpArgSzTOS = sizeof(tmpIValTOS); + + resPRIO = sock_getopt(sock, SOL_SOCKET, SO_PRIORITY, + &tmpIValPRIO, &tmpArgSzPRIO); + resTOS = sock_getopt(sock, SOL_IP, IP_TOS, + &tmpIValTOS, &tmpArgSzTOS); + + res = sock_setopt(sock, level, opt, optVal, optLen); + if (res == 0) { + + /* Ok, now we *maybe* need to "maybe" restore PRIO and TOS... + * maybe, possibly, ... + */ + + if (opt != SO_PRIORITY) { + if ((opt != IP_TOS) && (resTOS == 0)) { + resTOS = sock_setopt(sock, SOL_IP, IP_TOS, + (void *) &tmpIValTOS, + tmpArgSzTOS); + res = resTOS; + } + if ((res == 0) && (resPRIO == 0)) { + resPRIO = sock_setopt(sock, SOL_SOCKET, SO_PRIORITY, + &tmpIValPRIO, + tmpArgSzPRIO); + + /* Some kernels set a SO_PRIORITY by default + * that you are not permitted to reset, + * silently ignore this error condition. + */ + + if ((resPRIO != 0) && (sock_errno() == EPERM)) { + res = 0; + } else { + res = resPRIO; + } + } + } + } + +#else + + res = sock_setopt(sock, level, opt, optVal, optLen); + +#endif + + return res; +} + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -3468,6 +4290,139 @@ char* decode_address_atom(ErlNifEnv* env, } +static +BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val) +{ + unsigned int len; + char b[16]; // Just in case... + + /* Verify that the value is actually an atom */ + if (!IS_ATOM(env, eVal)) + return FALSE; + + /* Verify that the value is of acceptable length */ + if (!(GET_ATOM_LEN(env, eVal, &len) && + (len > 0) && + (len <= sizeof("false")))) + return FALSE; + + /* And finally try to extract the value */ + if (!GET_ATOM(env, eVal, b, sizeof(b))) + return FALSE; + + if (strncmp(b, "true", len) == 0) + *val = TRUE; + else + *val = FALSE; + + return TRUE; +} + + + +/* +++ decode the linger value +++ + * The (socket) linger option is provided as a two tuple: + * + * {OnOff :: boolean(), Time :: integer()} + * + */ +static +BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* valP) +{ + const ERL_NIF_TERM* lt; // The array of the elements of the tuple + int sz; // The size of the tuple - should be 2 + BOOLEAN_T onOff; + int secs; + + if (!GET_TUPLE(env, eVal, &sz, <)) + return FALSE; + + if (sz != 2) + return FALSE; + + + /* So fas so good - now check the two elements of the tuple. */ + + if (!decode_bool(env, lt[0], &onOff)) + return FALSE; + + if (!GET_INT(env, lt[1], &secs)) + return FALSE; + + valP->l_onoff = (onOff) ? 1 : 0; + valP->l_linger = secs; + + return TRUE; +} + + + +/* +++ decocde the ip socket option tos +++ + * The (ip) option can be provide in two ways: + * + * atom() | integer() + * + * When its an atom it can have the values: + * + * lowdelay | throughput | reliability | mincost + * + */ +static +BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) +{ + BOOLEAN_T result = FALSE; + + if (IS_ATOM(env, eVal)) { + unsigned int len; + char b[sizeof("reliability")+1]; // Just in case... + + if (!(GET_ATOM_LEN(env, eVal, &len) && + (len > 0) && + (len <= (sizeof("reliability"))))) { + *val = -1; + return FALSE; + } + + if (!GET_ATOM(env, eVal, b, sizeof(b))) { + *val = -1; + return FALSE; + } + + if (strncmp(b, "lowdelay", len) == 0) { + *val = IPTOS_LOWDELAY; + result = TRUE; + } else if (strncmp(b, "throughput", len) == 0) { + *val = IPTOS_THROUGHPUT; + result = TRUE; + } else if (strncmp(b, "reliability", len) == 0) { + *val = IPTOS_RELIABILITY; + result = TRUE; + } else if (strncmp(b, "mincost", len) == 0) { + *val = IPTOS_MINCOST; + result = TRUE; + } else { + *val = -1; + result = FALSE; + } + + } else if (IS_NUM(env, eVal)) { + + if (GET_INT(env, eVal, val)) { + result = TRUE; + } else { + *val = -1; + result = FALSE; + } + + } else { + *val = -1; + result = FALSE; + } + + return result; +} + + /* *** alloc_descriptor *** * Allocate and perform basic initialization of a socket descriptor. @@ -4232,8 +5187,8 @@ ErlNifFunc socket_funcs[] = {"nif_recvfrom", 2, nif_recvfrom, 0}, {"nif_close", 1, nif_close, 0}, {"nif_shutdown", 2, nif_shutdown, 0}, - {"nif_setsockopt", 3, nif_setsockopt, 0}, - {"nif_getsockopt", 2, nif_getsockopt, 0}, + {"nif_setopt", 3, nif_setopt, 0}, + {"nif_getopt", 2, nif_getopt, 0}, /* "Extra" functions to "complete" the socket interface. * For instance, the function nif_finalize_connection @@ -4260,38 +5215,38 @@ BOOLEAN_T extract_item_on_load(ErlNifEnv* env, return TRUE; } - static - BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) - { - ERL_NIF_TERM dbgKey = enif_make_atom(env, "debug"); - ERL_NIF_TERM dbgVal; - unsigned int len; - char d[16]; // Just in case... +static +BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) +{ + ERL_NIF_TERM dbgKey = enif_make_atom(env, "debug"); + ERL_NIF_TERM dbgVal; + unsigned int len; + char d[16]; // Just in case... - /* Extra the value of the debug property */ - if (!extract_item_on_load(env, map, dbgKey, &dbgVal)) - return def; + /* Extra the value of the debug property */ + if (!extract_item_on_load(env, map, dbgKey, &dbgVal)) + return def; - /* Verify that the value is actually an atom */ - if (!enif_is_atom(env, dbgVal)) - return def; + /* Verify that the value is actually an atom */ + if (!enif_is_atom(env, dbgVal)) + return def; - /* Verify that the value is of acceptable length */ - if (!(GET_ATOM_LEN(env, dbgVal, &len) && - (len > 0) && - (len <= sizeof("false")))) - return def; + /* Verify that the value is of acceptable length */ + if (!(GET_ATOM_LEN(env, dbgVal, &len) && + (len > 0) && + (len <= sizeof("false")))) + return def; - /* And finally try to extract the value */ - if (!GET_ATOM(env, dbgVal, d, sizeof(d))) - return def; + /* And finally try to extract the value */ + if (!GET_ATOM(env, dbgVal, d, sizeof(d))) + return def; - if (strncmp(d, "true", len) == 0) - return TRUE; - else - return FALSE; + if (strncmp(d, "true", len) == 0) + return TRUE; + else + return FALSE; - } +} static -- cgit v1.2.3 From bd7cab0e678ef3d327250a7dd7bd86faa685ec06 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 4 May 2018 18:49:42 +0200 Subject: [socket-nif] More setopt "Handle" the linger option "fully". We still have a lot of options to handle. We also need to analyze the nif-code for setopt... --- erts/emulator/nifs/common/socket_nif.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 532ac5c211..b01f26e4d4 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -3121,6 +3121,18 @@ ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env, } break; + case SOCKET_OPT_VALUE_LINGER: + { + optLen = sizeof(valP->u.lingerVal); + res = socket_setopt(descP->sock, level, opt, + &valP->u.lingerVal, optLen); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + } + break; + default: result = make_error(env, atom_einval); } -- cgit v1.2.3 From 0ab2ce1aad7596e81c1c59f71ccb3b2ad4f7f070 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 10:16:50 +0200 Subject: [socket-nif] Network interface mappings and sock addr Add functions for mapping network interface names and indexes. Also refined the types for socket addresses: in4_sockaddr and in6_sockaddr. --- erts/emulator/nifs/common/socket_nif.c | 209 +++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index b01f26e4d4..f4d2fbf021 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -371,6 +371,7 @@ typedef union { #define MKA(E,S) enif_make_atom((E), (S)) #define MKBIN(E,B) enif_make_binary((E), (B)) #define MKI(E,I) enif_make_int((E), (I)) +#define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) #define MKREF(E) enif_make_ref((E)) #define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) #define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) @@ -404,6 +405,8 @@ typedef union { enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) #define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) #define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) +#define GET_STR(E, L, B, SZ) \ + enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) #define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) @@ -700,6 +703,15 @@ 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_link_if2idx(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_link_ifs(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[]); @@ -778,6 +790,12 @@ static ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env, int level, int opt, SocketOptValue* valP); +static ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, + char* ifn); +static ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, + unsigned int id); +static ERL_NIF_TERM nlink_ifs(ErlNifEnv* env); +static unsigned int nlink_ifs_length(struct if_nameindex* p); static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -1123,6 +1141,12 @@ static SocketData socketData; * nif_setopt/3 * nif_getopt/2 * + * And some utility functions: + * ------------------------------------------------------------- + * nif_link_if2idx/1 + * nif_link_idx2if/1 + * nif_link_ifs/0 + * * And some socket admin functions: * ------------------------------------------------------------- * nif_cancel(Sock, Ref) @@ -3663,6 +3687,186 @@ int socket_setopt(int sock, int level, int opt, } + +/* ---------------------------------------------------------------------- + * nif_link_if2idx + * + * Description: + * Perform a Interface Name to Interface Index translation. + * + * Arguments: + * Ifn - Interface name to be translated. + */ + +static +ERL_NIF_TERM nif_link_if2idx(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM eifn; + char ifn[IF_NAMESIZE+1]; + + if (argc != 1) { + return enif_make_badarg(env); + } + eifn = argv[0]; + + if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) + return make_error2(env, atom_einval); + + return nlink_if2idx(env, ifn); +} + + + +static +ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, + char* ifn) +{ + unsigned int idx = if_nametoindex(ifn); + + if (idx == 0) + return make_error2(env, sock_errno()); + else + return make_ok2(env, idx); + +} + + + +/* ---------------------------------------------------------------------- + * nif_link_idx2if + * + * Description: + * Perform a Interface Index to Interface Name translation. + * + * Arguments: + * Idx - Interface index to be translated. + */ + +static +ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + unsigned int idx; + + if ((argc != 1) || + !GET_UINT(env, argv[0], &idx)) { + return enif_make_badarg(env); + } + + return nlink_idx2if(env, idx); +} + + + +static +ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, + unsigned int idx) +{ + ERL_NIF_TERM result; + char* ifn = MALLOC(IF_NAMESIZE+1); + + if (ifn == NULL) + return enif_make_badarg(env); // PLACEHOLDER + + if (NULL == if_indextoname(idx, ifn)) { + result = make_ok2(env, MKS(env, ifn)); + } else { + result = make_error2(env, sock_errno()); + } + + FREE(ifn); // OR IS THIS AUTOMATIC? + + return result; +} + + + +/* ---------------------------------------------------------------------- + * nif_link_idx2if + * + * Description: + * Get network interface names and indexes. + * + */ + +static +ERL_NIF_TERM nif_link_ifs(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 0) { + return enif_make_badarg(env); + } + + return nlink_ifs(env); +} + + + +static +ERL_NIF_TERM nlink_ifs(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + struct if_nameindex* ifs = if_nameindex(); + + if (ifs == NULL) { + result = make_error2(env, sock_errno()); + } else { + /* + * We got some interfaces: + * 1) Calculate how many - the only way is to iterate through the list + * until its end (which is indicated by an entry with index = zero + * and if_name = NULL). + * 2) Allocate an ERL_NIF_TERM array of the calculated length. + * 3) Iterate through the array of interfaces and for each create + * a two tuple: {Idx, If} + * + * Or shall we instead build a list in reverse order and then when + * its done, reverse that? Check + */ + unsigned int len = nlink_ifs_length(ifs); + + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i; + + for (i = 0; i < len; i++) { + array[i] = MKT2(env, + MKI(env, ifs[i].if_index), + MKS(env, ifs[i].if_name)); + } + + result = make_ok2(env, MKLA(env, array, len)); + FREE(array); + } else { + result = make_ok2(env, enif_make_list(env, 0)); + } + } + + if (ifs != NULL) + if_freenameindex(ifs); + + return result; +} + + +static +unsigned int nlink_ifs_length(struct if_nameindex* p) +{ + unsigned int len = 0; + + while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { + len++; + } + + return len; +} + + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -5202,6 +5406,11 @@ ErlNifFunc socket_funcs[] = {"nif_setopt", 3, nif_setopt, 0}, {"nif_getopt", 2, nif_getopt, 0}, + /* Misc utility functions */ + {"nif_link_if2idx", 1, nif_link_if2idx, 0}, + {"nif_link_idx2if", 1, nif_link_idx2if, 0}, + {"nif_link_ifs", 0, nif_link_ifs, 0}, + /* "Extra" functions to "complete" the socket interface. * For instance, the function nif_finalize_connection * is called after the connect *select* has "completed". -- cgit v1.2.3 From f7f70b94f90b1f500cb884be8ae722e1a22acf2e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 11:47:43 +0200 Subject: [socket-nif] setopt of (tcp) congestion --- erts/emulator/nifs/common/socket_nif.c | 54 +++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f4d2fbf021..11085ddab5 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -236,6 +236,14 @@ typedef unsigned long long llu_t; /* *** Misc macros and defines *** */ +#if defined(TCP_CA_NAME_MAX) +#define SOCKET_OPT_TCP_CONGESTION_NAME_MAX TCP_CA_NAME_MAX +#else +/* This is really excessive, but just in case... */ +#define SOCKET_OPT_TCP_CONGESTION_NAME_MAX 256 +#endif + + /* *** Socket state defs *** */ #define SOCKET_FLAG_OPEN 0x0001 @@ -349,14 +357,19 @@ typedef union { #define SOCKET_OPT_OTP_DEBUG 0 #define SOCKET_OPT_OTP_IOW 1 + #define SOCKET_OPT_SOCK_KEEPALIVE 0 #define SOCKET_OPT_SOCK_LINGER 1 + #define SOCKET_OPT_IP_RECVTOS 0 #define SOCKET_OPT_IP_ROUTER_ALERT 1 #define SOCKET_OPT_IP_TOS 2 #define SOCKET_OPT_IP_TTL 3 + #define SOCKET_OPT_IPV6_HOPLIMIT 0 -#define SOCKET_OPT_TCP_MAXSEG 0 + +#define SOCKET_OPT_TCP_CONGESTION 0 +#define SOCKET_OPT_TCP_MAXSEG 1 /* =================================================================== * @@ -601,6 +614,7 @@ typedef struct { #define SOCKET_OPT_VALUE_INT 2 #define SOCKET_OPT_VALUE_LINGER 3 #define SOCKET_OPT_VALUE_BIN 4 +#define SOCKET_OPT_VALUE_STR 5 typedef struct { unsigned int tag; @@ -609,6 +623,10 @@ typedef struct { int intVal; struct linger lingerVal; ErlNifBinary binVal; + struct { + unsigned int len; + char* str; + } strVal; } u; /* void* optValP; // Points to the actual data (above) @@ -3157,6 +3175,18 @@ ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env, } break; + case SOCKET_OPT_VALUE_STR: + { + optLen = valP->u.strVal.len; + res = socket_setopt(descP->sock, level, opt, + valP->u.strVal.str, optLen); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + } + break; + default: result = make_error(env, atom_einval); } @@ -3531,6 +3561,26 @@ BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env, SocketOptValue* valP) { switch (eOpt) { +#if defined(TCP_CONGESTION) + case SOCKET_OPT_TCP_CONGESTION: + { + valP->u.strVal.len = SOCKET_OPT_TCP_CONGESTION_NAME_MAX+1; + valP->u.strVal.str = MALLOC(valP->u.strVal.len); + + if (GET_STR(env, eVal, valP->u.strVal.str, valP->u.strVal.len) > 0) { + valP->tag = SOCKET_OPT_VALUE_STR; + *opt = TCP_CONGESTION; + return TRUE; + } else { + FREE(valP->u.strVal.str); + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + } + break; +#endif + #if defined(TCP_MAXSEG) case SOCKET_OPT_TCP_MAXSEG: if (!GET_INT(env, eVal, &valP->u.intVal)) { @@ -3538,6 +3588,8 @@ BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env, *opt = TCP_MAXSEG; return TRUE; } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; return FALSE; } break; -- cgit v1.2.3 From fece5e536ba4e814bcfe5896e23c2ef979468c7b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 12:16:11 +0200 Subject: [socket-nif] More setopt Add handling of nodelay tcp option setopt. Added placeholder sctp options autoclose and nodelay (since my machine does not actually have sctp installed...). --- erts/emulator/nifs/common/socket_nif.c | 64 +++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 13 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 11085ddab5..9887047135 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -366,10 +366,14 @@ typedef union { #define SOCKET_OPT_IP_TOS 2 #define SOCKET_OPT_IP_TTL 3 -#define SOCKET_OPT_IPV6_HOPLIMIT 0 +#define SOCKET_OPT_IPV6_HOPLIMIT 12 #define SOCKET_OPT_TCP_CONGESTION 0 #define SOCKET_OPT_TCP_MAXSEG 1 +#define SOCKET_OPT_TCP_NODELAY 2 + +#define SOCKET_OPT_SCTP_AUTOCLOSE 7 +#define SOCKET_OPT_SCTP_NODELAY 22 /* =================================================================== * @@ -3344,11 +3348,13 @@ BOOLEAN_T eoptval2optval_otp(ErlNifEnv* env, { if (decode_bool(env, eVal, &valP->u.boolVal)) { valP->tag = SOCKET_OPT_VALUE_BOOL; - result = TRUE; + *opt = eOpt; + result = TRUE; } else { - result = FALSE; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + *opt = -1; + result = FALSE; } - *opt = eOpt; } break; @@ -3582,14 +3588,28 @@ BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env, #endif #if defined(TCP_MAXSEG) - case SOCKET_OPT_TCP_MAXSEG: - if (!GET_INT(env, eVal, &valP->u.intVal)) { - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = TCP_MAXSEG; + case SOCKET_OPT_TCP_MAXSEG: + if (!GET_INT(env, eVal, &valP->u.intVal)) { + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = TCP_MAXSEG; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + break; +#endif + +#if defined(TCP_NODELAY) + case SOCKET_OPT_TCP_NODELAY: + if (decode_bool(env, eVal, &valP->u.boolVal)) { + valP->tag = SOCKET_OPT_VALUE_BOOL; + *opt = TCP_NODELAY; return TRUE; } else { - *opt = -1; valP->tag = SOCKET_OPT_VALUE_UNDEF; + *opt = -1; return FALSE; } break; @@ -3636,11 +3656,29 @@ BOOLEAN_T eoptval2optval_sctp(ErlNifEnv* env, switch (eOpt) { #if defined(SCTP_AUTOCLOSE) case SOCKET_OPT_SCTP_AUTOCLOSE: - if (!GET_INT(env, eVal, &valP->u.intVal)) + if (GET_INT(env, eVal, &valP->u.intVal)) { + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = SCTP_AUTOCLOSE; + return TRUE; + } else { + valP->tag = SOCKET_OPT_VALUE_UNDEF; + *opt = -1; return FALSE; // PLACEHOLDER - We should really be more informative - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = SCTP_AUTOCLOSE; - return TRUE; + } + break; +#endif + +#if defined(SCTP_NODELAY) + case SOCKET_OPT_SCTP_NODELAY: + if (decode_bool(env, eVal, &valP->u.boolVal)) { + valP->tag = SOCKET_OPT_VALUE_BOOL; + *opt = SCTP_NODELAY; + return TRUE; + } else { + valP->tag = SOCKET_OPT_VALUE_UNDEF; + *opt = -1; + return FALSE; + } break; #endif -- cgit v1.2.3 From d6b051d9b09aeba00d1bbd0d448dde6e551c4442 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 12:36:35 +0200 Subject: [socket-nif] More setopt - udp Add setopt for one udp option: cork. This is not platform independent. Aas fas as I know, it only works on linux, so for now this serves as a placeholder. --- erts/emulator/nifs/common/socket_nif.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 9887047135..14d7cd2611 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -133,6 +133,7 @@ #endif #include +#include #include #include @@ -372,6 +373,8 @@ typedef union { #define SOCKET_OPT_TCP_MAXSEG 1 #define SOCKET_OPT_TCP_NODELAY 2 +#define SOCKET_OPT_UDP_CORK 0 + #define SOCKET_OPT_SCTP_AUTOCLOSE 7 #define SOCKET_OPT_SCTP_NODELAY 22 @@ -3625,8 +3628,8 @@ BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env, /* +++ decode UDP socket options +++ - * Currently there are no such options, so this function - * is just a placeholder! + * Currently there are only one option, cork, and that may only + * work on linux... */ static BOOLEAN_T eoptval2optval_udp(ErlNifEnv* env, @@ -3636,6 +3639,20 @@ BOOLEAN_T eoptval2optval_udp(ErlNifEnv* env, SocketOptValue* valP) { switch (eOpt) { +#if defined(UDP_CORK) + case SOCKET_OPT_UDP_CORK: + if (decode_bool(env, eVal, &valP->u.boolVal)) { + valP->tag = SOCKET_OPT_VALUE_BOOL; + *opt = UDP_CORK; + return TRUE; + } else { + valP->tag = SOCKET_OPT_VALUE_UNDEF; + *opt = -1; + return FALSE; + } + break; +#endif + default: *opt = -1; valP->tag = SOCKET_OPT_VALUE_UNDEF; -- cgit v1.2.3 From 7bc2d3e8618f561185694ae11afe4fc83b1f72f3 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 12:58:56 +0200 Subject: [socket-nif] setopt of socket option reuseaddr --- erts/emulator/nifs/common/socket_nif.c | 52 +++++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 16 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 14d7cd2611..843543b42b 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -359,8 +359,9 @@ typedef union { #define SOCKET_OPT_OTP_DEBUG 0 #define SOCKET_OPT_OTP_IOW 1 -#define SOCKET_OPT_SOCK_KEEPALIVE 0 -#define SOCKET_OPT_SOCK_LINGER 1 +#define SOCKET_OPT_SOCK_KEEPALIVE 9 +#define SOCKET_OPT_SOCK_LINGER 10 +#define SOCKET_OPT_SOCK_REUSEADDR 21 #define SOCKET_OPT_IP_RECVTOS 0 #define SOCKET_OPT_IP_ROUTER_ALERT 1 @@ -3396,22 +3397,22 @@ BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, { switch (eOpt) { #if defined(SO_KEEPALIVE) - case SOCKET_OPT_SOCK_KEEPALIVE: - { - BOOLEAN_T val; + case SOCKET_OPT_SOCK_KEEPALIVE: + { + BOOLEAN_T val; - if (decode_bool(env, eVal, &val)) { - *opt = SO_KEEPALIVE; - valP->tag = SOCKET_OPT_VALUE_INT; - valP->u.intVal = (val) ? 1 : 0; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } + if (decode_bool(env, eVal, &val)) { + *opt = SO_KEEPALIVE; + valP->tag = SOCKET_OPT_VALUE_INT; + valP->u.intVal = (val) ? 1 : 0; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; } - break; + } + break; #endif #if defined(SO_LINGER) @@ -3430,6 +3431,25 @@ BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, break; #endif +#if defined(SO_REUSEADDR) + case SOCKET_OPT_SOCK_REUSEADDR: + { + BOOLEAN_T val; + + if (decode_bool(env, eVal, &val)) { + *opt = SO_REUSEADDR; + valP->tag = SOCKET_OPT_VALUE_INT; + valP->u.intVal = (val) ? 1 : 0; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + } + break; +#endif + default: *opt = -1; valP->tag = SOCKET_OPT_VALUE_UNDEF; -- cgit v1.2.3 From 56641fca11c50306c6c62266844f7828e325ae7d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 15:26:41 +0200 Subject: [socket-nif] setopt of socket option priority --- erts/emulator/nifs/common/socket_nif.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 843543b42b..905e04848c 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -361,6 +361,7 @@ typedef union { #define SOCKET_OPT_SOCK_KEEPALIVE 9 #define SOCKET_OPT_SOCK_LINGER 10 +#define SOCKET_OPT_SOCK_PRIORITY 16 #define SOCKET_OPT_SOCK_REUSEADDR 21 #define SOCKET_OPT_IP_RECVTOS 0 @@ -3431,6 +3432,20 @@ BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, break; #endif +#if defined(SO_PRIORITY) + case SOCKET_OPT_SOCK_PRIORITY: + if (GET_INT(env, eVal, &valP->u.intVal)) { + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = SO_PRIORITY; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + break; +#endif + #if defined(SO_REUSEADDR) case SOCKET_OPT_SOCK_REUSEADDR: { -- cgit v1.2.3 From 3d719906dd0ad8d07547c3a20a2190a416dda364 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 15:40:07 +0200 Subject: [socket-nif] setopt of socket option dontroute --- erts/emulator/nifs/common/socket_nif.c | 60 ++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 20 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 905e04848c..d8e274341f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -348,36 +348,37 @@ typedef union { #define SOCKET_SHUTDOWN_HOW_RDWR 2 -#define SOCKET_OPT_LEVEL_OTP 0 -#define SOCKET_OPT_LEVEL_SOCKET 1 -#define SOCKET_OPT_LEVEL_IP 2 -#define SOCKET_OPT_LEVEL_IPV6 3 -#define SOCKET_OPT_LEVEL_TCP 4 -#define SOCKET_OPT_LEVEL_UDP 5 -#define SOCKET_OPT_LEVEL_SCTP 6 - -#define SOCKET_OPT_OTP_DEBUG 0 -#define SOCKET_OPT_OTP_IOW 1 - +#define SOCKET_OPT_LEVEL_OTP 0 +#define SOCKET_OPT_LEVEL_SOCKET 1 +#define SOCKET_OPT_LEVEL_IP 2 +#define SOCKET_OPT_LEVEL_IPV6 3 +#define SOCKET_OPT_LEVEL_TCP 4 +#define SOCKET_OPT_LEVEL_UDP 5 +#define SOCKET_OPT_LEVEL_SCTP 6 + +#define SOCKET_OPT_OTP_DEBUG 0 +#define SOCKET_OPT_OTP_IOW 1 + +#define SOCKET_OPT_SOCK_DONTROUTE 7 #define SOCKET_OPT_SOCK_KEEPALIVE 9 #define SOCKET_OPT_SOCK_LINGER 10 #define SOCKET_OPT_SOCK_PRIORITY 16 #define SOCKET_OPT_SOCK_REUSEADDR 21 -#define SOCKET_OPT_IP_RECVTOS 0 -#define SOCKET_OPT_IP_ROUTER_ALERT 1 -#define SOCKET_OPT_IP_TOS 2 -#define SOCKET_OPT_IP_TTL 3 +#define SOCKET_OPT_IP_RECVTOS 25 +#define SOCKET_OPT_IP_ROUTER_ALERT 28 +#define SOCKET_OPT_IP_TOS 30 +#define SOCKET_OPT_IP_TTL 32 #define SOCKET_OPT_IPV6_HOPLIMIT 12 -#define SOCKET_OPT_TCP_CONGESTION 0 -#define SOCKET_OPT_TCP_MAXSEG 1 -#define SOCKET_OPT_TCP_NODELAY 2 +#define SOCKET_OPT_TCP_CONGESTION 0 +#define SOCKET_OPT_TCP_MAXSEG 1 +#define SOCKET_OPT_TCP_NODELAY 2 -#define SOCKET_OPT_UDP_CORK 0 +#define SOCKET_OPT_UDP_CORK 0 -#define SOCKET_OPT_SCTP_AUTOCLOSE 7 +#define SOCKET_OPT_SCTP_AUTOCLOSE 7 #define SOCKET_OPT_SCTP_NODELAY 22 @@ -3397,6 +3398,25 @@ BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, SocketOptValue* valP) { switch (eOpt) { +#if defined(SO_DONTROUTE) + case SOCKET_OPT_SOCK_DONTROUTE: + { + BOOLEAN_T val; + + if (decode_bool(env, eVal, &val)) { + *opt = SO_DONTROUTE; + valP->tag = SOCKET_OPT_VALUE_INT; + valP->u.intVal = (val) ? 1 : 0; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + } + break; +#endif + #if defined(SO_KEEPALIVE) case SOCKET_OPT_SOCK_KEEPALIVE: { -- cgit v1.2.3 From 0f2b8a76edaa462fca388a55eaf449a36296820a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 15:45:48 +0200 Subject: [socket-nif] setopt of socket option broadcast --- erts/emulator/nifs/common/socket_nif.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index d8e274341f..f9727d41b1 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -359,6 +359,7 @@ typedef union { #define SOCKET_OPT_OTP_DEBUG 0 #define SOCKET_OPT_OTP_IOW 1 +#define SOCKET_OPT_SOCK_BROADCAST 4 #define SOCKET_OPT_SOCK_DONTROUTE 7 #define SOCKET_OPT_SOCK_KEEPALIVE 9 #define SOCKET_OPT_SOCK_LINGER 10 @@ -3398,6 +3399,25 @@ BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, SocketOptValue* valP) { switch (eOpt) { +#if defined(SO_BROADCAST) + case SOCKET_OPT_SOCK_BROADCAST: + { + BOOLEAN_T val; + + if (decode_bool(env, eVal, &val)) { + *opt = SO_BROADCAST; + valP->tag = SOCKET_OPT_VALUE_INT; + valP->u.intVal = (val) ? 1 : 0; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + } + break; +#endif + #if defined(SO_DONTROUTE) case SOCKET_OPT_SOCK_DONTROUTE: { -- cgit v1.2.3 From 83dd8317ea3534fb9353f0d5159a0b1cb485f38c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 8 May 2018 16:49:58 +0200 Subject: [socket-nif] setopt for socket option(s) rcvbuf and sndnuf --- erts/emulator/nifs/common/socket_nif.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f9727d41b1..eee96ee844 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -364,7 +364,9 @@ typedef union { #define SOCKET_OPT_SOCK_KEEPALIVE 9 #define SOCKET_OPT_SOCK_LINGER 10 #define SOCKET_OPT_SOCK_PRIORITY 16 +#define SOCKET_OPT_SOCK_RCVBUF 17 #define SOCKET_OPT_SOCK_REUSEADDR 21 +#define SOCKET_OPT_SOCK_SNDBUF 27 #define SOCKET_OPT_IP_RECVTOS 25 #define SOCKET_OPT_IP_ROUTER_ALERT 28 @@ -3486,6 +3488,20 @@ BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, break; #endif +#if defined(SO_RCVBUF) + case SOCKET_OPT_SOCK_RCVBUF: + if (GET_INT(env, eVal, &valP->u.intVal)) { + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = SO_RCVBUF; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + break; +#endif + #if defined(SO_REUSEADDR) case SOCKET_OPT_SOCK_REUSEADDR: { @@ -3505,6 +3521,20 @@ BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, break; #endif +#if defined(SO_SNDBUF) + case SOCKET_OPT_SOCK_SNDBUF: + if (GET_INT(env, eVal, &valP->u.intVal)) { + valP->tag = SOCKET_OPT_VALUE_INT; + *opt = SO_SNDBUF; + return TRUE; + } else { + *opt = -1; + valP->tag = SOCKET_OPT_VALUE_UNDEF; + return FALSE; + } + break; +#endif + default: *opt = -1; valP->tag = SOCKET_OPT_VALUE_UNDEF; -- cgit v1.2.3 From bbf415b274224e273a89a706ca5b0250a1523302 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 16 May 2018 14:36:39 +0200 Subject: [socket-nif] more setopt Rewrote the setopt handling. Still not complete, but now the structure is "complete". That is, there is atleast one option handled for each level. --- erts/emulator/nifs/common/socket_nif.c | 1378 +++++++++++++++++++------------- 1 file changed, 804 insertions(+), 574 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index eee96ee844..18375caf60 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -810,17 +810,169 @@ static ERL_NIF_TERM nsetopt(ErlNifEnv* env, BOOLEAN_T isEncoded, BOOLEAN_T isOTP, int level, - int opt, - SocketOptValue* valP); + int eOpt, + ERL_NIF_TERM eVal); static ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, SocketDescriptor* descP, - int opt, - SocketOptValue* valP); -static ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - SocketOptValue* valP); + int eOpt, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); +#if defined(SO_BROADCAST) +static ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(SO_DONTROUTE) +static ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(SO_KEEPALIVE) +static ERL_NIF_TERM nsetopt_lvl_sock_keepalive(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(SO_LINGER) +static ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(SO_PRIORITY) +static ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(SO_RCVBUF) +static ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(SO_REUSEADDR) +static ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(SO_SNDBUF) +static ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); +#if defined(IP_RECVTOS) +static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(IP_ROUTER_ALERT) +static ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(IP_TOS) +static ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(IP_TTL) +static ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(SOL_IPV6) +static ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); +#if defined(IPV6_HOPLIMIT) +static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#endif // defined(SOL_IPV6) +static ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); +#if defined(TCP_CONGESTION) +static ERL_NIF_TERM nsetopt_lvl_tcp_congestion(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(TCP_MAXSEG) +static ERL_NIF_TERM nsetopt_lvl_tcp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(TCP_NODELAY) +static ERL_NIF_TERM nsetopt_lvl_tcp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +static ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); +#if defined(UDP_CORK) +static ERL_NIF_TERM nsetopt_lvl_udp_cork(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(HAVE_SCTP) +static ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); +#if defined(SCTP_AUTOCLOSE) +static ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(SCTP_NODELAY) +static ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#endif // defined(HAVE_SCTP) + +static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + int max, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); + static ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, char* ifn); static ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, @@ -940,58 +1092,6 @@ static BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, int eLevel, BOOLEAN_T* isOTP, int* level); -static BOOLEAN_T eoptval2optval(ErlNifEnv* env, - BOOLEAN_T isEncoded, - BOOLEAN_T isOTP, - int level, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* val); -static BOOLEAN_T eoptval2optval_otp(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP); -static BOOLEAN_T eoptval2optval_plain(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP); -static BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP); -static BOOLEAN_T eoptval2optval_ip(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP); -#if defined(SOL_IPV6) -static BOOLEAN_T eoptval2optval_ipv6(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP); -#endif -static BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP); -static BOOLEAN_T eoptval2optval_udp(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP); -#ifdef HAVE_SCTP -static BOOLEAN_T eoptval2optval_sctp(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP); -#endif #ifdef HAVE_SETNS static BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns); static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err); @@ -3041,31 +3141,28 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { SocketDescriptor* descP; - unsigned int eIsEncoded; - BOOLEAN_T isEncoded, isOTP; int eLevel, level = -1; - int eOpt, opt = -1; + int eOpt; + ERL_NIF_TERM eIsEncoded; ERL_NIF_TERM eVal; - SocketOptValue val; + BOOLEAN_T isEncoded, isOTP; if ((argc != 5) || !enif_get_resource(env, argv[0], sockets, (void**) &descP) || - !GET_UINT(env, argv[1], &eIsEncoded) || !GET_INT(env, argv[2], &eLevel) || !GET_INT(env, argv[3], &eOpt)) { return enif_make_badarg(env); } - eVal = argv[4]; - - isEncoded = ((eIsEncoded == 0) ? FALSE : TRUE); + eIsEncoded = argv[1]; + eVal = argv[4]; - if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) + if (!decode_bool(env, eIsEncoded, &isEncoded)) return make_error(env, atom_einval); - if (!eoptval2optval(env, isEncoded, isOTP, level, eOpt, eVal, &opt, &val)) + if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) return make_error(env, atom_einval); - return nsetopt(env, descP, isEncoded, isOTP, level, opt, &val); + return nsetopt(env, descP, isEncoded, isOTP, level, eOpt, eVal); } @@ -3075,60 +3172,44 @@ ERL_NIF_TERM nsetopt(ErlNifEnv* env, BOOLEAN_T isEncoded, BOOLEAN_T isOTP, int level, - int opt, - SocketOptValue* valP) + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; - int res; - if (!isEncoded) { - res = socket_setopt(descP->sock, level, opt, - valP->u.binVal.data, valP->u.binVal.size); - if (res != 0) - result = make_error2(env, res); - else - result = atom_ok; + if (isOTP) { + /* These are not actual socket options, + * but options for our implementation. + */ + result = nsetopt_otp(env, descP, eOpt, eVal); + } else if (!isEncoded) { + result = nsetopt_native(env, descP, level, eOpt, eVal); } else { - if (isOTP) { - /* These are not actual socket options, - * but options for our implementation. - */ - result = nsetopt_otp(env, descP, opt, valP); - } else { - /* Basically, call setsockopt(...) - * - * How do we know what type each option have? tag in value type? - * - */ - result = nsetopt_gen(env, descP, level, opt, valP); - } + result = nsetopt_level(env, descP, level, eOpt, eVal); } return result; } + +/* nsetopt_otp - Handle OTP (level) options + */ static ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, SocketDescriptor* descP, - int opt, - SocketOptValue* valP) + int eOpt, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; - /* Make an idiot check just to be on the safe side... */ - if (valP->tag == SOCKET_OPT_VALUE_UNDEF) - return make_error(env, atom_einval); - - switch (opt) { + switch (eOpt) { case SOCKET_OPT_OTP_DEBUG: - descP->dbg = valP->u.boolVal; - result = atom_ok; + result = nsetopt_otp_debug(env, descP, eVal); break; case SOCKET_OPT_OTP_IOW: - descP->iow = valP->u.boolVal; - result = atom_ok; + result = nsetopt_otp_iow(env, descP, eVal); break; default: @@ -3140,67 +3221,18 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, } +/* nsetopt_otp_debug - Handle the OTP (level) debug options + */ static -ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - SocketOptValue* valP) +ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) { - socklen_t optLen; - int res; ERL_NIF_TERM result; - switch (valP->tag) { - case SOCKET_OPT_VALUE_INT: - { - optLen = sizeof(valP->u.intVal); - res = socket_setopt(descP->sock, level, opt, - (void*) &valP->u.intVal, optLen); - if (res != 0) - result = make_error2(env, res); - else - result = atom_ok; - } - break; - - case SOCKET_OPT_VALUE_BIN: - { - optLen = valP->u.binVal.size; - res = socket_setopt(descP->sock, level, opt, - &valP->u.binVal.data, optLen); - if (res != 0) - result = make_error2(env, res); - else - result = atom_ok; - } - break; - - case SOCKET_OPT_VALUE_LINGER: - { - optLen = sizeof(valP->u.lingerVal); - res = socket_setopt(descP->sock, level, opt, - &valP->u.lingerVal, optLen); - if (res != 0) - result = make_error2(env, res); - else - result = atom_ok; - } - break; - - case SOCKET_OPT_VALUE_STR: - { - optLen = valP->u.strVal.len; - res = socket_setopt(descP->sock, level, opt, - valP->u.strVal.str, optLen); - if (res != 0) - result = make_error2(env, res); - else - result = atom_ok; - } - break; - - default: + if (decode_bool(env, eVal, &descP->dbg)) { + result = atom_ok; + } else { result = make_error(env, atom_einval); } @@ -3208,611 +3240,809 @@ ERL_NIF_TERM nsetopt_gen(ErlNifEnv* env, } - +/* nsetopt_otp_iow - Handle the OTP (level) iow options + */ static -BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, - int eLevel, - BOOLEAN_T* isOTP, - int* level) +ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) { - BOOLEAN_T result; - - if (isEncoded) { - switch (eLevel) { - case SOCKET_OPT_LEVEL_OTP: - *isOTP = TRUE; - *level = -1; - result = TRUE; - break; - - case SOCKET_OPT_LEVEL_SOCKET: - *isOTP = FALSE; - *level = SOL_SOCKET; - result = TRUE; - break; + ERL_NIF_TERM result; - case SOCKET_OPT_LEVEL_IP: - *isOTP = FALSE; -#if defined(SOL_IP) - *level = SOL_IP; -#else - *level = IPROTO_IP; -#endif - result = TRUE; - break; + if (decode_bool(env, eVal, &descP->iow)) { + result = atom_ok; + } else { + result = make_error(env, atom_einval); + } -#if defined(SOL_IPV6) - case SOCKET_OPT_LEVEL_IPV6: - *isOTP = FALSE; - *level = SOL_IPV6; - result = TRUE; - break; -#endif + return result; +} - case SOCKET_OPT_LEVEL_TCP: - *isOTP = FALSE; - *level = IPPROTO_TCP; - result = TRUE; - break; - case SOCKET_OPT_LEVEL_UDP: - *isOTP = FALSE; - *level = IPPROTO_UDP; - result = TRUE; - break; -#ifdef HAVE_SCTP - case SOCKET_OPT_LEVEL_SCTP: - *isOTP = FALSE; - *level = IPPROTO_SCTP; - result = TRUE; - break; -#endif +/* The option has *not* been encoded. Instead it has been provided + * in "native mode" (option is provided as is and value as a binary). + */ +static +ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal) +{ + ErlNifBinary val; + ERL_NIF_TERM result; - default: - *isOTP = FALSE; - *level = -1; - result = FALSE; - break; - } + if (GET_BIN(env, eVal, &val)) { + int res = socket_setopt(descP->sock, level, opt, + val.data, val.size); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; } else { - *isOTP = FALSE; - *level = eLevel; - result = TRUE; + result = make_error(env, atom_einval); } return result; } + +/* nsetopt_level - A "proper" level (option) has been specified + */ static -BOOLEAN_T eoptval2optval(ErlNifEnv* env, - BOOLEAN_T isEncoded, - BOOLEAN_T isOTP, - int level, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP) +ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt, + ERL_NIF_TERM eVal) { - if (isOTP) { - return eoptval2optval_otp(env, eOpt, eVal, opt, valP); - } else if (!isEncoded) { - return eoptval2optval_plain(env, eOpt, eVal, opt, valP); - } else { - switch (level) { - case SOL_SOCKET: - return eoptval2optval_socket(env, eOpt, eVal, opt, valP); - break; + ERL_NIF_TERM result; + + switch (level) { + case SOL_SOCKET: + result = nsetopt_lvl_socket(env, descP, eOpt, eVal); + break; #if defined(SOL_IP) - case SOL_IP: + case SOL_IP: #else - case IPPROTO_IP: + case IPPROTO_IP: #endif - return eoptval2optval_ip(env, eOpt, eVal, opt, valP); - break; + result = nsetopt_lvl_ip(env, descP, eOpt, eVal); + break; #if defined(SOL_IPV6) - case SOL_IPV6: - return eoptval2optval_ipv6(env, eOpt, eVal, opt, valP); - break; + case SOL_IPV6: + result = nsetopt_lvl_ipv6(env, descP, eOpt, eVal); + break; #endif - case IPPROTO_TCP: - return eoptval2optval_tcp(env, eOpt, eVal, opt, valP); - break; + case IPPROTO_TCP: + result = nsetopt_lvl_tcp(env, descP, eOpt, eVal); + break; - case IPPROTO_UDP: - return eoptval2optval_udp(env, eOpt, eVal, opt, valP); - break; + case IPPROTO_UDP: + result = nsetopt_lvl_udp(env, descP, eOpt, eVal); + break; -#ifdef HAVE_SCTP - case IPPROTO_SCTP: - return eoptval2optval_sctp(env, eOpt, eVal, opt, valP); - break; +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + result = nsetopt_lvl_sctp(env, descP, eOpt, eVal); + break; #endif - default: - *opt = -1; - return FALSE; - } - } -} - - - -static -BOOLEAN_T eoptval2optval_otp(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP) -{ - BOOLEAN_T result = FALSE; - - switch (eOpt) { - case SOCKET_OPT_OTP_IOW: - case SOCKET_OPT_OTP_DEBUG: - { - if (decode_bool(env, eVal, &valP->u.boolVal)) { - valP->tag = SOCKET_OPT_VALUE_BOOL; - *opt = eOpt; - result = TRUE; - } else { - valP->tag = SOCKET_OPT_VALUE_UNDEF; - *opt = -1; - result = FALSE; - } - } - break; - default: - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; + result = make_error(env, atom_einval); + break; } return result; } -static -BOOLEAN_T eoptval2optval_plain(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP) -{ - if (!GET_BIN(env, eVal, &valP->u.binVal)) - return FALSE; - valP->tag = SOCKET_OPT_VALUE_BIN; - *opt = eOpt; - - return TRUE; -} - - +/* nsetopt_lvl_socket - Level *SOCKET* option + */ static -BOOLEAN_T eoptval2optval_socket(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP) +ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { + ERL_NIF_TERM result; + switch (eOpt) { #if defined(SO_BROADCAST) case SOCKET_OPT_SOCK_BROADCAST: - { - BOOLEAN_T val; - - if (decode_bool(env, eVal, &val)) { - *opt = SO_BROADCAST; - valP->tag = SOCKET_OPT_VALUE_INT; - valP->u.intVal = (val) ? 1 : 0; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } - } + result = nsetopt_lvl_sock_broadcast(env, descP, eVal); break; #endif #if defined(SO_DONTROUTE) case SOCKET_OPT_SOCK_DONTROUTE: - { - BOOLEAN_T val; - - if (decode_bool(env, eVal, &val)) { - *opt = SO_DONTROUTE; - valP->tag = SOCKET_OPT_VALUE_INT; - valP->u.intVal = (val) ? 1 : 0; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } - } + result = nsetopt_lvl_sock_dontroute(env, descP, eVal); break; #endif #if defined(SO_KEEPALIVE) case SOCKET_OPT_SOCK_KEEPALIVE: - { - BOOLEAN_T val; - - if (decode_bool(env, eVal, &val)) { - *opt = SO_KEEPALIVE; - valP->tag = SOCKET_OPT_VALUE_INT; - valP->u.intVal = (val) ? 1 : 0; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } - } + result = nsetopt_lvl_sock_keepalive(env, descP, eVal); break; #endif #if defined(SO_LINGER) case SOCKET_OPT_SOCK_LINGER: - { - if (decode_sock_linger(env, eVal, &valP->u.lingerVal)) { - *opt = SO_LINGER; - valP->tag = SOCKET_OPT_VALUE_LINGER; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } - } + result = nsetopt_lvl_sock_linger(env, descP, eVal); break; #endif #if defined(SO_PRIORITY) case SOCKET_OPT_SOCK_PRIORITY: - if (GET_INT(env, eVal, &valP->u.intVal)) { - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = SO_PRIORITY; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } + result = nsetopt_lvl_sock_priority(env, descP, eVal); break; #endif #if defined(SO_RCVBUF) case SOCKET_OPT_SOCK_RCVBUF: - if (GET_INT(env, eVal, &valP->u.intVal)) { - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = SO_RCVBUF; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } + result = nsetopt_lvl_sock_rcvbuf(env, descP, eVal); break; #endif #if defined(SO_REUSEADDR) case SOCKET_OPT_SOCK_REUSEADDR: - { - BOOLEAN_T val; - - if (decode_bool(env, eVal, &val)) { - *opt = SO_REUSEADDR; - valP->tag = SOCKET_OPT_VALUE_INT; - valP->u.intVal = (val) ? 1 : 0; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } - } + result = nsetopt_lvl_sock_reuseaddr(env, descP, eVal); break; #endif #if defined(SO_SNDBUF) case SOCKET_OPT_SOCK_SNDBUF: - if (GET_INT(env, eVal, &valP->u.intVal)) { - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = SO_SNDBUF; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } + result = nsetopt_lvl_sock_sndbuf(env, descP, eVal); break; #endif default: - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; + result = make_error(env, atom_einval); + break; } + + return result; +} + + +#if defined(SO_BROADCAST) +static +ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST, eVal); +} +#endif + + +#if defined(SO_DONTROUTE) +static +ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_DONTROUTE, eVal); +} +#endif + + +#if defined(SO_KEEPALIVE) +static +ERL_NIF_TERM nsetopt_lvl_sock_keepalive(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_KEEPALIVE, eVal); +} +#endif + + +#if defined(SO_LINGER) +static +ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + struct linger val; + + if (decode_sock_linger(env, eVal, &val)) { + int optLen = sizeof(val); + int res = socket_setopt(descP->sock, SOL_SOCKET, SO_LINGER, + (void*) &val, optLen); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + } else { + result = make_error(env, atom_einval); + } + + return result; +} +#endif + + +#if defined(SO_PRIORITY) +static +ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY, eVal); } +#endif +#if defined(SO_RCVBUF) +static +ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVBUF, eVal); +} +#endif + +#if defined(SO_REUSEADDR) static -BOOLEAN_T eoptval2optval_ip(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP) +ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) { + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR, eVal); +} +#endif + + +#if defined(SO_SNDBUF) +static +ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF, eVal); +} +#endif + + + +/* nsetopt_lvl_ip - Level *IP* option(s) + */ +static +ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + switch (eOpt) { #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: - { - BOOLEAN_T val; - - if (decode_bool(env, eVal, &val)) { - *opt = IP_RECVTOS; - valP->tag = SOCKET_OPT_VALUE_INT; - valP->u.intVal = (val) ? 1 : 0; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return TRUE; - } - } + result = nsetopt_lvl_ip_recvtos(env, descP, eVal); break; #endif #if defined(IP_ROUTER_ALERT) case SOCKET_OPT_IP_ROUTER_ALERT: - if (GET_INT(env, eVal, &valP->u.intVal)) { - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = IP_ROUTER_ALERT; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } + result = nsetopt_lvl_ip_router_alert(env, descP, eVal); break; #endif #if defined(IP_TOS) case SOCKET_OPT_IP_TOS: - { - if (decode_ip_tos(env, eVal, &valP->u.intVal)) { - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = IP_TOS; - return TRUE; - } else { - *opt = -1; - return FALSE; - } - } + result = nsetopt_lvl_ip_tos(env, descP, eVal); break; #endif #if defined(IP_TTL) case SOCKET_OPT_IP_TTL: - /* - * Should we care about the value? That is, if it is valid? - * And what is the valid range anyway for ttl? 0 - 255? - * - */ - if (!GET_INT(env, eVal, &valP->u.intVal)) - return FALSE; // PLACEHOLDER - We should really be more informative - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = IP_TTL; - return TRUE; + result = nsetopt_lvl_ip_ttl(env, descP, eVal); break; #endif default: - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* nsetopt_lvl_ip_recvtos - Level IP RECVTOS option + */ +#if defined(IP_RECVTOS) +static +ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_RECVTOS, eVal); +} +#endif + + +/* nsetopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option + */ +#if defined(IP_ROUTER_ALERT) +static +ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_int_opt(env, descP, level, IP_ROUTER_ALERT, eVal); +} +#endif + + +/* nsetopt_lvl_ip_tos - Level IP TOS option + */ +#if defined(IP_TOS) +static +ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + ERL_NIF_TERM result; + int val; + + if (decode_ip_tos(env, eVal, &val)) { + int res = socket_setopt(descP->sock, level, IP_TOS, &val, sizeof(val)); + + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + + } else { + result = make_error(env, atom_einval); } + return result; +} +#endif + + +/* nsetopt_lvl_ip_ttl - Level IP TTL option + */ +#if defined(IP_TTL) +static +ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_int_opt(env, descP, level, IP_TTL, eVal); } +#endif +/* nsetopt_lvl_ipv6 - Level *IPv6* option(s) + */ #if defined(SOL_IPV6) static -BOOLEAN_T eoptval2optval_ipv6(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP) +ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { - BOOLEAN_T result = FALSE; + ERL_NIF_TERM result; switch (eOpt) { #if defined(IPV6_HOPLIMIT) case SOCKET_OPT_IPV6_HOPLIMIT: - { - BOOLEAN_T val; - - if (decode_bool(env, eVal, &val)) { - valP->tag = SOCKET_OPT_VALUE_INT; - valP->u.intVal = (val) ? 1 : 0; - *opt = IPV6_HOPLIMIT; - result = TRUE; - } else { - *opt = -1; - result = FALSE; - } - } + result = nsetopt_lvl_ipv6_hoplimit(env, descP, eVal); break; #endif default: - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - result = FALSE; + result = make_error(env, atom_einval); break; } return result; } + + +#if defined(IPV6_HOPLIMIT) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_HOPLIMIT, eVal); +} #endif +#endif // defined(SOL_IPV6) + + +/* nsetopt_lvl_tcp - Level *TCP* option(s) + */ static -BOOLEAN_T eoptval2optval_tcp(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP) +ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { + ERL_NIF_TERM result; + switch (eOpt) { #if defined(TCP_CONGESTION) case SOCKET_OPT_TCP_CONGESTION: - { - valP->u.strVal.len = SOCKET_OPT_TCP_CONGESTION_NAME_MAX+1; - valP->u.strVal.str = MALLOC(valP->u.strVal.len); - - if (GET_STR(env, eVal, valP->u.strVal.str, valP->u.strVal.len) > 0) { - valP->tag = SOCKET_OPT_VALUE_STR; - *opt = TCP_CONGESTION; - return TRUE; - } else { - FREE(valP->u.strVal.str); - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } - } + result = nsetopt_lvl_tcp_congestion(env, descP, eVal); break; #endif #if defined(TCP_MAXSEG) case SOCKET_OPT_TCP_MAXSEG: - if (!GET_INT(env, eVal, &valP->u.intVal)) { - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = TCP_MAXSEG; - return TRUE; - } else { - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; - } + result = nsetopt_lvl_tcp_maxseg(env, descP, eVal); break; #endif #if defined(TCP_NODELAY) case SOCKET_OPT_TCP_NODELAY: - if (decode_bool(env, eVal, &valP->u.boolVal)) { - valP->tag = SOCKET_OPT_VALUE_BOOL; - *opt = TCP_NODELAY; - return TRUE; - } else { - valP->tag = SOCKET_OPT_VALUE_UNDEF; - *opt = -1; - return FALSE; - } + result = nsetopt_lvl_tcp_nodelay(env, descP, eVal); break; #endif default: - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; + result = make_error(env, atom_einval); + break; } + + return result; } +/* nsetopt_lvl_tcp_congestion - Level TCP CONGESTION option + */ +#if defined(TCP_CONGESTION) +static +ERL_NIF_TERM nsetopt_lvl_tcp_congestion(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + int max = SOCKET_OPT_TCP_CONGESTION_NAME_MAX+1; + + return nsetopt_str_opt(env, descP, IPPROTO_TCP, TCP_CONGESTION, max, eVal); +} +#endif -/* +++ decode UDP socket options +++ - * Currently there are only one option, cork, and that may only - * work on linux... + +/* nsetopt_lvl_tcp_maxseg - Level TCP MAXSEG option + */ +#if defined(TCP_MAXSEG) +static +ERL_NIF_TERM nsetopt_lvl_tcp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, IPPROTO_TCP, TCP_MAXSEG, eVal); +} +#endif + + +/* nsetopt_lvl_tcp_nodelay - Level TCP NODELAY option */ +#if defined(TCP_NODELAY) static -BOOLEAN_T eoptval2optval_udp(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP) +ERL_NIF_TERM nsetopt_lvl_tcp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) { + return nsetopt_bool_opt(env, descP, IPPROTO_TCP, TCP_NODELAY, eVal); +} +#endif + + + +/* nsetopt_lvl_udp - Level *UDP* option(s) + */ +static +ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + switch (eOpt) { #if defined(UDP_CORK) case SOCKET_OPT_UDP_CORK: - if (decode_bool(env, eVal, &valP->u.boolVal)) { - valP->tag = SOCKET_OPT_VALUE_BOOL; - *opt = UDP_CORK; - return TRUE; - } else { - valP->tag = SOCKET_OPT_VALUE_UNDEF; - *opt = -1; - return FALSE; - } + result = nsetopt_lvl_udp_cork(env, descP, eVal); break; #endif default: - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; + result = make_error(env, atom_einval); + break; } + + return result; } +/* nsetopt_lvl_udp_cork - Level UDP CORK option + */ +#if defined(UDP_CORK) +static +ERL_NIF_TERM nsetopt_lvl_udp_cork(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, IPPROTO_UDP, UDP_CORK, eVal); +} +#endif -#ifdef HAVE_SCTP + + + +/* nsetopt_lvl_sctp - Level *SCTP* option(s) + */ +#if defined(HAVE_SCTP) static -BOOLEAN_T eoptval2optval_sctp(ErlNifEnv* env, - int eOpt, - ERL_NIF_TERM eVal, - int* opt, - SocketOptValue* valP) +ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) { + ERL_NIF_TERM result; + switch (eOpt) { #if defined(SCTP_AUTOCLOSE) case SOCKET_OPT_SCTP_AUTOCLOSE: - if (GET_INT(env, eVal, &valP->u.intVal)) { - valP->tag = SOCKET_OPT_VALUE_INT; - *opt = SCTP_AUTOCLOSE; - return TRUE; - } else { - valP->tag = SOCKET_OPT_VALUE_UNDEF; - *opt = -1; - return FALSE; // PLACEHOLDER - We should really be more informative - } + result = nsetopt_lvl_sctp_autoclose(env, descP, eVal); break; #endif #if defined(SCTP_NODELAY) case SOCKET_OPT_SCTP_NODELAY: - if (decode_bool(env, eVal, &valP->u.boolVal)) { - valP->tag = SOCKET_OPT_VALUE_BOOL; - *opt = SCTP_NODELAY; - return TRUE; - } else { - valP->tag = SOCKET_OPT_VALUE_UNDEF; - *opt = -1; - return FALSE; - } + result = nsetopt_lvl_sctp_nodelay(env, descP, eVal); break; #endif default: - *opt = -1; - valP->tag = SOCKET_OPT_VALUE_UNDEF; - return FALSE; + result = make_error(env, atom_einval); + break; } + + return result; +} + + +/* nsetopt_lvl_sctp_autoclose - Level SCTP AUTOCLOSE option + */ +#if defined(SCTP_AUTOCLOSE) +static +ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_AUTOCLOSE, eVal); } #endif +/* nsetopt_lvl_sctp_nodelay - Level SCTP NODELAY option + */ +#if defined(SCTP_NODELAY) +static +ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY, eVal); +} +#endif + + + +#endif // defined(HAVE_SCTP) + + + + +/* nsetopt_str_opt - set an option that has an string value + */ +static +ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + int max, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + char* val = MALLOC(max); + + if (GET_STR(env, eVal, val, max) > 0) { + int optLen = strlen(val); + int res = socket_setopt(descP->sock, level, opt, &val, optLen); + + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + + } else { + result = make_error(env, atom_einval); + } + + FREE(val); + + return result; +} + + +/* nsetopt_bool_opt - set an option that has an (integer) bool value + */ +static +ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + BOOLEAN_T val; + + if (decode_bool(env, eVal, &val)) { + int ival = (val) ? 1 : 0; + int res = socket_setopt(descP->sock, level, opt, &ival, sizeof(ival)); + + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + + } else { + result = make_error(env, atom_einval); + } + + return result; +} + + +/* nsetopt_int_opt - set an option that has an integer value + */ +static +ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + int val; + + if (GET_INT(env, eVal, &val)) { + int res = socket_setopt(descP->sock, level, opt, &val, sizeof(val)); + + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + + } else { + result = make_error(env, atom_einval); + } + + return result; +} + + +static +BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, + int eLevel, + BOOLEAN_T* isOTP, + int* level) +{ + BOOLEAN_T result; + + if (isEncoded) { + switch (eLevel) { + case SOCKET_OPT_LEVEL_OTP: + *isOTP = TRUE; + *level = -1; + result = TRUE; + break; + + case SOCKET_OPT_LEVEL_SOCKET: + *isOTP = FALSE; + *level = SOL_SOCKET; + result = TRUE; + break; + + case SOCKET_OPT_LEVEL_IP: + *isOTP = FALSE; +#if defined(SOL_IP) + *level = SOL_IP; +#else + *level = IPROTO_IP; +#endif + result = TRUE; + break; + +#if defined(SOL_IPV6) + case SOCKET_OPT_LEVEL_IPV6: + *isOTP = FALSE; + *level = SOL_IPV6; + result = TRUE; + break; +#endif + + case SOCKET_OPT_LEVEL_TCP: + *isOTP = FALSE; + *level = IPPROTO_TCP; + result = TRUE; + break; + + case SOCKET_OPT_LEVEL_UDP: + *isOTP = FALSE; + *level = IPPROTO_UDP; + result = TRUE; + break; + +#ifdef HAVE_SCTP + case SOCKET_OPT_LEVEL_SCTP: + *isOTP = FALSE; + *level = IPPROTO_SCTP; + result = TRUE; + break; +#endif + + default: + *isOTP = FALSE; + *level = -1; + result = FALSE; + break; + } + } else { + *isOTP = FALSE; + *level = eLevel; + result = TRUE; + } + + return result; +} + + /* +++ socket_setopt +++ * -- cgit v1.2.3 From 24bcbd2040fad723648e25af87cd848da8aa27bc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 17 May 2018 14:22:52 +0200 Subject: [socket-nif] getopt partially implemented There are still many options not implemented (just as for setopt), but this will have to do for now... --- erts/emulator/nifs/common/socket_nif.c | 1334 +++++++++++++++++++++++++++++--- 1 file changed, 1207 insertions(+), 127 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 18375caf60..d87492dd54 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -376,8 +376,9 @@ typedef union { #define SOCKET_OPT_IPV6_HOPLIMIT 12 #define SOCKET_OPT_TCP_CONGESTION 0 -#define SOCKET_OPT_TCP_MAXSEG 1 -#define SOCKET_OPT_TCP_NODELAY 2 +#define SOCKET_OPT_TCP_CORK 1 +#define SOCKET_OPT_TCP_MAXSEG 2 +#define SOCKET_OPT_TCP_NODELAY 3 #define SOCKET_OPT_UDP_CORK 0 @@ -437,6 +438,7 @@ typedef union { #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) #define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) +#define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) /* =================================================================== * @@ -520,6 +522,11 @@ static unsigned long one_value = 1; # define SOCKLEN_T size_t #endif +#ifdef __WIN32__ +#define SOCKOPTLEN_T int +#else +#define SOCKOPTLEN_T SOCKLEN_T +#endif /* The general purpose sockaddr */ typedef union { @@ -805,6 +812,7 @@ static ERL_NIF_TERM nclose(ErlNifEnv* env, static ERL_NIF_TERM nshutdown(ErlNifEnv* env, SocketDescriptor* descP, int how); + static ERL_NIF_TERM nsetopt(ErlNifEnv* env, SocketDescriptor* descP, BOOLEAN_T isEncoded, @@ -956,6 +964,126 @@ static ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, #endif #endif // defined(HAVE_SCTP) +static ERL_NIF_TERM ngetopt(ErlNifEnv* env, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt); +static ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +static ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt); +static ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt); +static ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(SO_BROADCAST) +static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_DONTROUTE) +static ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_KEEPALIVE) +static ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_LINGER) +static ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_PRIORITY) +static ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_RCVBUF) +static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_REUSEADDR) +static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SO_SNDBUF) +static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(IP_RECVTOS) +static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(IP_ROUTER_ALERT) +static ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(IP_TOS) +static ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(IP_TTL) +static ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SOL_IPV6) +static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(IPV6_HOPLIMIT) +static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#endif // defined(SOL_IPV6) +static ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(TCP_CONGESTION) +static ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(TCP_MAXSEG) +static ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(TCP_NODELAY) +static ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +static ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(UDP_CORK) +static ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(HAVE_SCTP) +static ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +#if defined(SCTP_AUTOCLOSE) +static ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(SCTP_NODELAY) +static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#endif // defined(HAVE_SCTP) + static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -973,6 +1101,20 @@ static ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, int opt, ERL_NIF_TERM eVal); +static ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + int max); +static ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt); +static ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt); + static ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, char* ifn); static ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, @@ -1059,6 +1201,12 @@ static BOOLEAN_T decode_ip_tos(ErlNifEnv* env, static BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val); +static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, + ERL_NIF_TERM eVal, + int* opt, + int* valueSz); +static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal); +static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val); static void inform_waiting_procs(ErlNifEnv* env, SocketDescriptor* descP, @@ -1189,6 +1337,11 @@ 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"; static char str_eafnosupport[] = "eafnosupport"; @@ -1217,6 +1370,11 @@ static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_undefined; +static ERL_NIF_TERM atom_lowdelay; +static ERL_NIF_TERM atom_throughput; +static ERL_NIF_TERM atom_reliability; +static ERL_NIF_TERM atom_mincost; + static ERL_NIF_TERM atom_eagain; static ERL_NIF_TERM atom_eafnosupport; static ERL_NIF_TERM atom_einval; @@ -3710,9 +3868,9 @@ ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, #endif #if defined(TCP_MAXSEG) - case SOCKET_OPT_TCP_MAXSEG: - result = nsetopt_lvl_tcp_maxseg(env, descP, eVal); - break; + case SOCKET_OPT_TCP_MAXSEG: + result = nsetopt_lvl_tcp_maxseg(env, descP, eVal); + break; #endif #if defined(TCP_NODELAY) @@ -4074,12 +4232,12 @@ int socket_setopt(int sock, int level, int opt, int res; #if defined(IP_TOS) && defined(SOL_IP) && defined(SO_PRIORITY) - int tmpIValPRIO; - int tmpIValTOS; - int resPRIO; - int resTOS; - SOCKLEN_T tmpArgSzPRIO = sizeof(tmpIValPRIO); - SOCKLEN_T tmpArgSzTOS = sizeof(tmpIValTOS); + int tmpIValPRIO; + int tmpIValTOS; + int resPRIO; + int resTOS; + SOCKOPTLEN_T tmpArgSzPRIO = sizeof(tmpIValPRIO); + SOCKOPTLEN_T tmpArgSzTOS = sizeof(tmpIValTOS); resPRIO = sock_getopt(sock, SOL_SOCKET, SO_PRIORITY, &tmpIValPRIO, &tmpArgSzPRIO); @@ -4131,177 +4289,1009 @@ int socket_setopt(int sock, int level, int opt, /* ---------------------------------------------------------------------- - * nif_link_if2idx + * nif_getopt * * Description: - * Perform a Interface Name to Interface Index translation. + * Get socket option. + * Its possible to use a "raw" mode (not encoded). That is, we do not + * interpret level and opt. They are passed "as is" to the + * getsockopt function call. The value in this case will "copied" as + * is and provided to the user in the form of a binary. * * Arguments: - * Ifn - Interface name to be translated. + * Socket (ref) - Points to the socket descriptor. + * IsEncoded - Are the "arguments" encoded or not. + * Level - Level of the socket option. + * Opt - The socket option. */ static -ERL_NIF_TERM nif_link_if2idx(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) +ERL_NIF_TERM nif_getopt(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM eifn; - char ifn[IF_NAMESIZE+1]; + SocketDescriptor* descP; + int eLevel, level = -1; + int eOpt; + ERL_NIF_TERM eIsEncoded; + BOOLEAN_T isEncoded, isOTP; - if (argc != 1) { - return enif_make_badarg(env); - } - eifn = argv[0]; + if ((argc != 4) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP) || + !GET_INT(env, argv[2], &eLevel) || + !GET_INT(env, argv[3], &eOpt)) { + return enif_make_badarg(env); + } + eIsEncoded = argv[1]; - if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) - return make_error2(env, atom_einval); + if (!decode_bool(env, eIsEncoded, &isEncoded)) + return make_error(env, atom_einval); - return nlink_if2idx(env, ifn); + if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) + return make_error(env, atom_einval); + + return ngetopt(env, descP, isEncoded, isOTP, level, eOpt); } static -ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, - char* ifn) +ERL_NIF_TERM ngetopt(ErlNifEnv* env, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt) { - unsigned int idx = if_nametoindex(ifn); + ERL_NIF_TERM result; - if (idx == 0) - return make_error2(env, sock_errno()); - else - return make_ok2(env, idx); + if (isOTP) { + /* These are not actual socket options, + * but options for our implementation. + */ + result = ngetopt_otp(env, descP, eOpt); + } else if (!isEncoded) { + result = ngetopt_native(env, descP, level, eOpt); + } else { + result = ngetopt_level(env, descP, level, eOpt); + } + return result; } -/* ---------------------------------------------------------------------- - * nif_link_idx2if - * - * Description: - * Perform a Interface Index to Interface Name translation. - * - * Arguments: - * Idx - Interface index to be translated. +/* ngetopt_otp - Handle OTP (level) options */ - static -ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) +ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) { - unsigned int idx; + ERL_NIF_TERM result; - if ((argc != 1) || - !GET_UINT(env, argv[0], &idx)) { - return enif_make_badarg(env); + switch (eOpt) { + case SOCKET_OPT_OTP_DEBUG: + result = ngetopt_otp_debug(env, descP); + break; + + case SOCKET_OPT_OTP_IOW: + result = ngetopt_otp_iow(env, descP); + break; + + default: + result = make_error(env, atom_einval); + break; } - return nlink_idx2if(env, idx); + return result; } - +/* ngetopt_otp_debug - Handle the OTP (level) debug options + */ static -ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, - unsigned int idx) +ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, + SocketDescriptor* descP) { - ERL_NIF_TERM result; - char* ifn = MALLOC(IF_NAMESIZE+1); - - if (ifn == NULL) - return enif_make_badarg(env); // PLACEHOLDER - - if (NULL == if_indextoname(idx, ifn)) { - result = make_ok2(env, MKS(env, ifn)); - } else { - result = make_error2(env, sock_errno()); - } + ERL_NIF_TERM eVal; - FREE(ifn); // OR IS THIS AUTOMATIC? + encode_bool(descP->dbg, &eVal); - return result; + return make_ok2(env, eVal); } - -/* ---------------------------------------------------------------------- - * nif_link_idx2if - * - * Description: - * Get network interface names and indexes. - * +/* ngetopt_otp_iow - Handle the OTP (level) iow options */ - static -ERL_NIF_TERM nif_link_ifs(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) +ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, + SocketDescriptor* descP) { - if (argc != 0) { - return enif_make_badarg(env); - } + ERL_NIF_TERM eVal; - return nlink_ifs(env); + encode_bool(descP->iow, &eVal); + + return make_ok2(env, eVal); } +/* The option has *not* been encoded. Instead it has been provided + * in "native mode" (option is provided as is). In this case it will have the + * format: {NativeOpt :: integer(), ValueSize :: non_neg_integer()} + */ static -ERL_NIF_TERM nlink_ifs(ErlNifEnv* env) +ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt) { - ERL_NIF_TERM result; - struct if_nameindex* ifs = if_nameindex(); - - if (ifs == NULL) { - result = make_error2(env, sock_errno()); - } else { - /* - * We got some interfaces: - * 1) Calculate how many - the only way is to iterate through the list - * until its end (which is indicated by an entry with index = zero - * and if_name = NULL). - * 2) Allocate an ERL_NIF_TERM array of the calculated length. - * 3) Iterate through the array of interfaces and for each create - * a two tuple: {Idx, If} - * - * Or shall we instead build a list in reverse order and then when - * its done, reverse that? Check - */ - unsigned int len = nlink_ifs_length(ifs); + ERL_NIF_TERM result = enif_make_badarg(env); + int opt; + SOCKOPTLEN_T valueSz; - if (len > 0) { - ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); - unsigned int i; + /* + * We should really make it possible to specify common specific types, + * such as integer or boolean (instead of the size)... + * + */ - for (i = 0; i < len; i++) { - array[i] = MKT2(env, - MKI(env, ifs[i].if_index), - MKS(env, ifs[i].if_name)); - } + if (decode_native_get_opt(env, eOpt, &opt, (int*) &valueSz)) { + int res; - result = make_ok2(env, MKLA(env, array, len)); - FREE(array); + if (valueSz == 0) { + res = sock_getopt(descP->sock, level, opt, NULL, NULL); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; } else { - result = make_ok2(env, enif_make_list(env, 0)); + ErlNifBinary val; + + if (ALLOC_BIN(valueSz, &val)) { + res = sock_getopt(descP->sock, level, opt, val.data, &valueSz); + if (res != 0) { + result = make_error2(env, res); + } else { + if (valueSz < val.size) { + if (REALLOC_BIN(&val, valueSz)) { + result = make_ok2(env, MKBIN(env, &val)); + } else { + result = enif_make_badarg(env); + } + } + } + } else { + result = enif_make_badarg(env); + } } - } - if (ifs != NULL) - if_freenameindex(ifs); + } else { + result = make_error(env, atom_einval); + } return result; } +/* ngetopt_level - A "proper" level (option) has been specified + */ static -unsigned int nlink_ifs_length(struct if_nameindex* p) +ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt) { - unsigned int len = 0; + ERL_NIF_TERM result; - while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { - len++; + switch (level) { + case SOL_SOCKET: + result = ngetopt_lvl_socket(env, descP, eOpt); + break; + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + result = ngetopt_lvl_ip(env, descP, eOpt); + break; + +#if defined(SOL_IPV6) + case SOL_IPV6: + result = ngetopt_lvl_ipv6(env, descP, eOpt); + break; +#endif + + case IPPROTO_TCP: + result = ngetopt_lvl_tcp(env, descP, eOpt); + break; + + case IPPROTO_UDP: + result = ngetopt_lvl_udp(env, descP, eOpt); + break; + +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + result = ngetopt_lvl_sctp(env, descP, eOpt); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_socket - Level *SOCKET* option + */ +static +ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(SO_BROADCAST) + case SOCKET_OPT_SOCK_BROADCAST: + result = ngetopt_lvl_sock_broadcast(env, descP); + break; +#endif + +#if defined(SO_DONTROUTE) + case SOCKET_OPT_SOCK_DONTROUTE: + result = ngetopt_lvl_sock_dontroute(env, descP); + break; +#endif + +#if defined(SO_KEEPALIVE) + case SOCKET_OPT_SOCK_KEEPALIVE: + result = ngetopt_lvl_sock_keepalive(env, descP); + break; +#endif + +#if defined(SO_LINGER) + case SOCKET_OPT_SOCK_LINGER: + result = ngetopt_lvl_sock_linger(env, descP); + break; +#endif + +#if defined(SO_PRIORITY) + case SOCKET_OPT_SOCK_PRIORITY: + result = ngetopt_lvl_sock_priority(env, descP); + break; +#endif + +#if defined(SO_RCVBUF) + case SOCKET_OPT_SOCK_RCVBUF: + result = ngetopt_lvl_sock_rcvbuf(env, descP); + break; +#endif + +#if defined(SO_REUSEADDR) + case SOCKET_OPT_SOCK_REUSEADDR: + result = ngetopt_lvl_sock_reuseaddr(env, descP); + break; +#endif + +#if defined(SO_SNDBUF) + case SOCKET_OPT_SOCK_SNDBUF: + result = ngetopt_lvl_sock_sndbuf(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +#if defined(SO_BROADCAST) +static +ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST); +} +#endif + + +#if defined(SO_DONTROUTE) +static +ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_DONTROUTE); +} +#endif + + +#if defined(SO_KEEPALIVE) +static +ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_KEEPALIVE); +} +#endif + + +#if defined(SO_LINGER) +static +ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + struct linger val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + sys_memzero((void *) &val, sizeof(val)); + + res = sock_getopt(descP->sock, SOL_SOCKET, SO_LINGER, + &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + ERL_NIF_TERM lOnOff = MKI(env, val.l_onoff); + ERL_NIF_TERM lSecs = MKI(env, val.l_linger); + ERL_NIF_TERM linger = MKT2(env, lOnOff, lSecs); + + result = make_ok2(env, linger); + } + + return result; +} +#endif + + +#if defined(SO_PRIORITY) +static +ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY); +} +#endif + + +#if defined(SO_RCVBUF) +static +ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVBUF); +} +#endif + + +#if defined(SO_REUSEADDR) +static +ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR); +} +#endif + + +#if defined(SO_SNDBUF) +static +ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF); +} +#endif + + +/* ngetopt_lvl_ip - Level *IP* option(s) + */ +static +ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(IP_RECVTOS) + case SOCKET_OPT_IP_RECVTOS: + result = ngetopt_lvl_ip_recvtos(env, descP); + break; +#endif + +#if defined(IP_ROUTER_ALERT) + case SOCKET_OPT_IP_ROUTER_ALERT: + result = ngetopt_lvl_ip_router_alert(env, descP); + break; +#endif + +#if defined(IP_TOS) + case SOCKET_OPT_IP_TOS: + result = ngetopt_lvl_ip_tos(env, descP); + break; +#endif + +#if defined(IP_TTL) + case SOCKET_OPT_IP_TTL: + result = ngetopt_lvl_ip_ttl(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_ip_recvtos - Level IP RECVTOS option + */ +#if defined(IP_RECVTOS) +static +ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_RECVTOS); +} +#endif + + +/* ngetopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option + */ +#if defined(IP_ROUTER_ALERT) +static +ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_int_opt(env, descP, level, IP_ROUTER_ALERT); +} +#endif + + +/* ngetopt_lvl_ip_tos - Level IP TOS option + */ +#if defined(IP_TOS) +static +ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + ERL_NIF_TERM result; + int val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + res = sock_getopt(descP->sock, level, IP_TOS, &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + result = encode_ip_tos(env, val); + } + + return result; +} +#endif + + +/* ngetopt_lvl_ip_ttl - Level IP TTL option + */ +#if defined(IP_TTL) +static +ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_int_opt(env, descP, level, IP_TTL); +} +#endif + + + +/* ngetopt_lvl_ipv6 - Level *IPv6* option(s) + */ +#if defined(SOL_IPV6) +static +ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(IPV6_HOPLIMIT) + case SOCKET_OPT_IPV6_HOPLIMIT: + result = ngetopt_lvl_ipv6_hoplimit(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +#if defined(IPV6_HOPLIMIT) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_HOPLIMIT); +} +#endif + + +#endif // defined(SOL_IPV6) + + + +/* ngetopt_lvl_tcp - Level *TCP* option(s) + */ +static +ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(TCP_CONGESTION) + case SOCKET_OPT_TCP_CONGESTION: + result = ngetopt_lvl_tcp_congestion(env, descP); + break; +#endif + +#if defined(TCP_MAXSEG) + case SOCKET_OPT_TCP_MAXSEG: + result = ngetopt_lvl_tcp_maxseg(env, descP); + break; +#endif + +#if defined(TCP_NODELAY) + case SOCKET_OPT_TCP_NODELAY: + result = ngetopt_lvl_tcp_nodelay(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_tcp_congestion - Level TCP CONGESTION option + */ +#if defined(TCP_CONGESTION) +static +ERL_NIF_TERM ngetopt_lvl_tcp_congestion(ErlNifEnv* env, + SocketDescriptor* descP) +{ + int max = SOCKET_OPT_TCP_CONGESTION_NAME_MAX+1; + + return ngetopt_str_opt(env, descP, IPPROTO_TCP, TCP_CONGESTION, max); +} +#endif + + +/* ngetopt_lvl_tcp_maxseg - Level TCP MAXSEG option + */ +#if defined(TCP_MAXSEG) +static +ERL_NIF_TERM ngetopt_lvl_tcp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, IPPROTO_TCP, TCP_MAXSEG); +} +#endif + + +/* ngetopt_lvl_tcp_nodelay - Level TCP NODELAY option + */ +#if defined(TCP_NODELAY) +static +ERL_NIF_TERM ngetopt_lvl_tcp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, IPPROTO_TCP, TCP_NODELAY); +} +#endif + + + +/* ngetopt_lvl_udp - Level *UDP* option(s) + */ +static +ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(UDP_CORK) + case SOCKET_OPT_UDP_CORK: + result = ngetopt_lvl_udp_cork(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_udp_cork - Level UDP CORK option + */ +#if defined(UDP_CORK) +static +ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, IPPROTO_UDP, UDP_CORK); +} +#endif + + + +/* ngetopt_lvl_sctp - Level *SCTP* option(s) + */ +#if defined(HAVE_SCTP) +static +ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + switch (eOpt) { +#if defined(SCTP_AUTOCLOSE) + case SOCKET_OPT_SCTP_AUTOCLOSE: + result = ngetopt_lvl_sctp_autoclose(env, descP); + break; +#endif + +#if defined(SCTP_NODELAY) + case SOCKET_OPT_SCTP_NODELAY: + result = ngetopt_lvl_sctp_nodelay(env, descP); + break; +#endif + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ngetopt_lvl_sctp_autoclose - Level SCTP AUTOCLOSE option + */ +#if defined(SCTP_AUTOCLOSE) +static +ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_AUTOCLOSE); +} +#endif + + +/* ngetopt_lvl_sctp_nodelay - Level SCTP NODELAY option + */ +#if defined(SCTP_NODELAY) +static +ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY); +} +#endif + + +#endif // defined(HAVE_SCTP) + + + +/* ngetopt_str_opt - get an string option + */ +static +ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + int max) +{ + ERL_NIF_TERM result; + char* val = MALLOC(max); + SOCKOPTLEN_T valSz = max; + int res; + + res = sock_getopt(descP->sock, level, opt, &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + ERL_NIF_TERM sval = MKSL(env, val, valSz); + + result = make_ok2(env, sval); + } + + FREE(val); + + return result; +} + + +/* nsetopt_bool_opt - get an (integer) bool option + */ +static +ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt) +{ + ERL_NIF_TERM result; + int val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + res = sock_getopt(descP->sock, level, opt, &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + ERL_NIF_TERM bval = ((val) ? atom_true : atom_false); + + result = make_ok2(env, bval); + } + + return result; +} + + +/* nsetopt_int_opt - get an integer option + */ +static +ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt) +{ + ERL_NIF_TERM result; + int val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + res = sock_getopt(descP->sock, level, opt, &val, &valSz); + + if (res != 0) { + result = make_error2(env, res); + } else { + result = make_ok2(env, MKI(env, val)); + } + + return result; +} + + + + +/* ---------------------------------------------------------------------- + * nif_link_if2idx + * + * Description: + * Perform a Interface Name to Interface Index translation. + * + * Arguments: + * Ifn - Interface name to be translated. + */ + +static +ERL_NIF_TERM nif_link_if2idx(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM eifn; + char ifn[IF_NAMESIZE+1]; + + if (argc != 1) { + return enif_make_badarg(env); + } + eifn = argv[0]; + + if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) + return make_error2(env, atom_einval); + + return nlink_if2idx(env, ifn); +} + + + +static +ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, + char* ifn) +{ + unsigned int idx = if_nametoindex(ifn); + + if (idx == 0) + return make_error2(env, sock_errno()); + else + return make_ok2(env, idx); + +} + + + +/* ---------------------------------------------------------------------- + * nif_link_idx2if + * + * Description: + * Perform a Interface Index to Interface Name translation. + * + * Arguments: + * Idx - Interface index to be translated. + */ + +static +ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + unsigned int idx; + + if ((argc != 1) || + !GET_UINT(env, argv[0], &idx)) { + return enif_make_badarg(env); + } + + return nlink_idx2if(env, idx); +} + + + +static +ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, + unsigned int idx) +{ + ERL_NIF_TERM result; + char* ifn = MALLOC(IF_NAMESIZE+1); + + if (ifn == NULL) + return enif_make_badarg(env); // PLACEHOLDER + + if (NULL == if_indextoname(idx, ifn)) { + result = make_ok2(env, MKS(env, ifn)); + } else { + result = make_error2(env, sock_errno()); + } + + FREE(ifn); // OR IS THIS AUTOMATIC? + + return result; +} + + + +/* ---------------------------------------------------------------------- + * nif_link_idx2if + * + * Description: + * Get network interface names and indexes. + * + */ + +static +ERL_NIF_TERM nif_link_ifs(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 0) { + return enif_make_badarg(env); + } + + return nlink_ifs(env); +} + + + +static +ERL_NIF_TERM nlink_ifs(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + struct if_nameindex* ifs = if_nameindex(); + + if (ifs == NULL) { + result = make_error2(env, sock_errno()); + } else { + /* + * We got some interfaces: + * 1) Calculate how many - the only way is to iterate through the list + * until its end (which is indicated by an entry with index = zero + * and if_name = NULL). + * 2) Allocate an ERL_NIF_TERM array of the calculated length. + * 3) Iterate through the array of interfaces and for each create + * a two tuple: {Idx, If} + * + * Or shall we instead build a list in reverse order and then when + * its done, reverse that? Check + */ + unsigned int len = nlink_ifs_length(ifs); + + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i; + + for (i = 0; i < len; i++) { + array[i] = MKT2(env, + MKI(env, ifs[i].if_index), + MKS(env, ifs[i].if_name)); + } + + result = make_ok2(env, MKLA(env, array, len)); + FREE(array); + } else { + result = make_ok2(env, enif_make_list(env, 0)); + } + } + + if (ifs != NULL) + if_freenameindex(ifs); + + return result; +} + + +static +unsigned int nlink_ifs_length(struct if_nameindex* p) +{ + unsigned int len = 0; + + while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { + len++; } return len; @@ -4977,7 +5967,6 @@ BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val) } - /* +++ decode the linger value +++ * The (socket) linger option is provided as a two tuple: * @@ -5015,7 +6004,7 @@ BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* v -/* +++ decocde the ip socket option tos +++ +/* +++ decode the ip socket option tos +++ * The (ip) option can be provide in two ways: * * atom() | integer() @@ -5032,11 +6021,11 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) if (IS_ATOM(env, eVal)) { unsigned int len; - char b[sizeof("reliability")+1]; // Just in case... + char b[sizeof(str_reliability)+1]; // Just in case... if (!(GET_ATOM_LEN(env, eVal, &len) && (len > 0) && - (len <= (sizeof("reliability"))))) { + (len <= (sizeof(str_reliability))))) { *val = -1; return FALSE; } @@ -5046,16 +6035,16 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) return FALSE; } - if (strncmp(b, "lowdelay", len) == 0) { + if (strncmp(b, str_lowdelay, len) == 0) { *val = IPTOS_LOWDELAY; result = TRUE; - } else if (strncmp(b, "throughput", len) == 0) { + } else if (strncmp(b, str_throughput, len) == 0) { *val = IPTOS_THROUGHPUT; result = TRUE; - } else if (strncmp(b, "reliability", len) == 0) { + } else if (strncmp(b, str_reliability, len) == 0) { *val = IPTOS_RELIABILITY; result = TRUE; - } else if (strncmp(b, "mincost", len) == 0) { + } else if (strncmp(b, str_mincost, len) == 0) { *val = IPTOS_MINCOST; result = TRUE; } else { @@ -5082,6 +6071,92 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) +/* +++ decocde the native getopt option +++ + * The option is in this case provide in the form of a two tuple: + * + * {NativeOpt, ValueSize} + * + * NativeOpt :: integer() + * ValueSize :: non_neg_integer() + * + */ +static +BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, + int* opt, int* valueSz) +{ + const ERL_NIF_TERM* nativeOptT; + int nativeOptTSz; + + /* First, get the tuple and verify its size (2) */ + + if (!GET_TUPLE(env, eVal, &nativeOptTSz, &nativeOptT)) + return FALSE; + + if (nativeOptTSz != 2) + return FALSE; + + /* So far so good. Both elements should be integers */ + + if (!GET_INT(env, nativeOptT[0], opt)) + return FALSE; + + if (!GET_INT(env, nativeOptT[1], valueSz)) + return FALSE; + + return TRUE; +} + + +static +void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal) +{ + if (val) + *eVal = atom_true; + else + *eVal = atom_false; +} + + +/* +++ encode the ip socket option tos +++ + * The (ip) option can be provide as: + * + * lowdelay | throughput | reliability | mincost | integer() + * + */ +static +ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val) +{ + ERL_NIF_TERM result; + + switch (val) { + case IPTOS_LOWDELAY: + result = make_ok2(env, atom_lowdelay); + break; + + case IPTOS_THROUGHPUT: + result = make_ok2(env, atom_throughput); + break; + + case IPTOS_RELIABILITY: + result = make_ok2(env, atom_reliability); + break; + + case IPTOS_MINCOST: + result = make_ok2(env, atom_mincost); + break; + + default: + result = make_ok2(env, MKI(env, val)); + break; + } + + return result; +} + + + + + /* *** alloc_descriptor *** * Allocate and perform basic initialization of a socket descriptor. * @@ -5999,6 +7074,11 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_undefined = MKA(env, str_undefined); // atom_version = MKA(env, str_version); + atom_lowdelay = MKA(env, str_lowdelay); + atom_throughput = MKA(env, str_throughput); + atom_reliability = MKA(env, str_reliability); + atom_mincost = MKA(env, str_mincost); + /* Error codes */ atom_eagain = MKA(env, str_eagain); atom_eafnosupport = MKA(env, str_eafnosupport); -- cgit v1.2.3 From 993f22cab851d704c40f04cda5d5c3730539a6f8 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 17 May 2018 15:16:57 +0200 Subject: [socket-nif] More getopt Its now possible to specify some "base" type(s) when retreiving an "native" option (base type instead of value size). --- erts/emulator/nifs/common/socket_nif.c | 135 +++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 30 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index d87492dd54..c63eff40ec 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -295,6 +295,10 @@ typedef unsigned long long llu_t; #define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048 +#define SOCKET_OPT_VALLUE_TYPE_UNSPEC 0 +#define SOCKET_OPT_VALLUE_TYPE_INT 1 +#define SOCKET_OPT_VALLUE_TYPE_BOOL 2 + typedef union { struct { unsigned int open:1; @@ -981,6 +985,11 @@ static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, int eOpt); +static ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + SOCKOPTLEN_T valueSz); static ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -1204,6 +1213,7 @@ static BOOLEAN_T decode_bool(ErlNifEnv* env, static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, int* opt, + uint16_t* valueType, int* valueSz); static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal); static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val); @@ -4429,6 +4439,7 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, { ERL_NIF_TERM result = enif_make_badarg(env); int opt; + uint16_t valueType; SOCKOPTLEN_T valueSz; /* @@ -4437,44 +4448,71 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, * */ - if (decode_native_get_opt(env, eOpt, &opt, (int*) &valueSz)) { - int res; + if (decode_native_get_opt(env, eOpt, &opt, &valueType, (int*) &valueSz)) { + switch (valueType) { + case SOCKET_OPT_VALLUE_TYPE_UNSPEC: + result = ngetopt_native_unspec(env, descP, level, opt, valueSz); + break; + case SOCKET_OPT_VALLUE_TYPE_INT: + result = ngetopt_int_opt(env, descP, level, opt); + break; + case SOCKET_OPT_VALLUE_TYPE_BOOL: + result = ngetopt_bool_opt(env, descP, level, opt); + break; + default: + result = make_error(env, atom_einval); + break; + } + } else { + result = make_error(env, atom_einval); + } - if (valueSz == 0) { - res = sock_getopt(descP->sock, level, opt, NULL, NULL); - if (res != 0) - result = make_error2(env, res); - else - result = atom_ok; - } else { - ErlNifBinary val; + return result; +} - if (ALLOC_BIN(valueSz, &val)) { - res = sock_getopt(descP->sock, level, opt, val.data, &valueSz); - if (res != 0) { - result = make_error2(env, res); - } else { - if (valueSz < val.size) { - if (REALLOC_BIN(&val, valueSz)) { - result = make_ok2(env, MKBIN(env, &val)); - } else { - result = enif_make_badarg(env); - } + +static +ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + SOCKOPTLEN_T valueSz) +{ + ERL_NIF_TERM result = enif_make_badarg(env); + int res; + + if (valueSz == 0) { + res = sock_getopt(descP->sock, level, opt, NULL, NULL); + if (res != 0) + result = make_error2(env, res); + else + result = atom_ok; + } else { + ErlNifBinary val; + + if (ALLOC_BIN(valueSz, &val)) { + res = sock_getopt(descP->sock, level, opt, val.data, &valueSz); + if (res != 0) { + result = make_error2(env, res); + } else { + if (valueSz < val.size) { + if (REALLOC_BIN(&val, valueSz)) { + result = make_ok2(env, MKBIN(env, &val)); + } else { + result = enif_make_badarg(env); } } - } else { - result = enif_make_badarg(env); } + } else { + result = enif_make_badarg(env); } - - } else { - result = make_error(env, atom_einval); } return result; } + /* ngetopt_level - A "proper" level (option) has been specified */ static @@ -6077,12 +6115,12 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) * {NativeOpt, ValueSize} * * NativeOpt :: integer() - * ValueSize :: non_neg_integer() + * ValueSize :: int | bool | non_neg_integer() * */ static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, - int* opt, int* valueSz) + int* opt, uint16_t* valueType, int* valueSz) { const ERL_NIF_TERM* nativeOptT; int nativeOptTSz; @@ -6095,13 +6133,50 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, if (nativeOptTSz != 2) return FALSE; - /* So far so good. Both elements should be integers */ + /* So far so good. + * First element is an integer. + * Second element is an atom or an integer. + * The only "types" that we support at the moment are: + * + * bool - Which is actually a integer + * (but will be *returned* as a boolean()) + * int - Just short for integer + */ if (!GET_INT(env, nativeOptT[0], opt)) return FALSE; - if (!GET_INT(env, nativeOptT[1], valueSz)) + if (IS_ATOM(env, nativeOptT[1])) { + unsigned int len; + char t[16]; // Just in case + + if (!(GET_ATOM_LEN(env, nativeOptT[1], &len) && + (len > 0) && + (len <= (sizeof("bool"))))) + return FALSE; + + if (!GET_ATOM(env, nativeOptT[1], t, sizeof(t))) + return FALSE; + + if (strncmp(t, "bool", len) == 0) { + *valueType = SOCKET_OPT_VALLUE_TYPE_BOOL; + *valueSz = sizeof(int); // Just to be sure + } else if (strncmp(t, "int", len) == 0) { + *valueType = SOCKET_OPT_VALLUE_TYPE_INT; + *valueSz = sizeof(int); // Just to be sure + } else { + return FALSE; + } + + } else if (IS_NUM(env, nativeOptT[1])) { + if (GET_INT(env, nativeOptT[1], valueSz)) { + *valueType = SOCKET_OPT_VALLUE_TYPE_UNSPEC; + } else { + return FALSE; + } + } else { return FALSE; + } return TRUE; } -- cgit v1.2.3 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/emulator/nifs/common/socket_nif.c | 535 +++++++++++++++++++++++++++------ 1 file changed, 435 insertions(+), 100 deletions(-) (limited to 'erts/emulator/nifs') 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); -- cgit v1.2.3 From 70b74f57a360c2b2a1edfe46c61d2ea170d73f91 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 7 Jun 2018 17:50:38 +0200 Subject: [socket-nif-doc] More polishing Also added (a very) temporary example. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index ed961d691d..7bafe38273 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -7315,6 +7315,8 @@ ErlNifFunc socket_funcs[] = // {"nif_debug", 1, nif_debug_, 0}, // The proper "socket" interface + // This is used when we already have a file descriptor + // {"nif_open", 1, nif_open, 0}, {"nif_open", 4, nif_open, 0}, {"nif_bind", 2, nif_bind, 0}, {"nif_connect", 2, nif_connect, 0}, -- cgit v1.2.3 From cb858ed68f2cc21014f37c8f6c1cb0dfc20f6184 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 13 Jun 2018 10:54:01 +0200 Subject: [socket-nif] The info function now returns a non-empty map The map returned by the info function is now populated with the "global stuff". That is, debug, iow and the (global) counters (which are still not actually incremented). Also added debug functions and macros (not yet used). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 436 +++++++++++++++------------------ 1 file changed, 196 insertions(+), 240 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 7bafe38273..3205ed1720 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -62,6 +62,7 @@ #include #include #include +#include #ifdef HAVE_UNISTD_H #include @@ -405,6 +406,7 @@ typedef union { #define MKBIN(E,B) enif_make_binary((E), (B)) #define MKI(E,I) enif_make_int((E), (I)) #define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) +#define MKMA(E,KA,VA,L,M) enif_make_map_from_arrays((E), (KA), (VA), (L), (M)) #define MKREF(E) enif_make_ref((E)) #define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) #define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) @@ -449,6 +451,14 @@ typedef union { #define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) +#define SDEBUG( ___COND___ , proto ) \ + if ( ___COND___ ) { \ + dbg_printf proto; \ + fflush(stdout); \ + } +#define SDBG( proto ) SDEBUG( data.dbg , proto ) + + /* =================================================================== * * * * Basic socket operations * @@ -672,13 +682,13 @@ typedef struct { ERL_NIF_TERM version; ERL_NIF_TERM buildDate; BOOLEAN_T dbg; + BOOLEAN_T iow; ErlNifMutex* cntMtx; - BOOLEAN_T iow; uint32_t numSockets; uint32_t numTypeDGrams; uint32_t numTypeStreams; - uint32_t numTypeSeqPkg; + uint32_t numTypeSeqPkgs; uint32_t numDomainLocal; uint32_t numDomainInet; uint32_t numDomainInet6; @@ -750,15 +760,6 @@ 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_link_if2idx(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM nif_link_ifs(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[]); @@ -1129,13 +1130,6 @@ static ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, int level, int opt); -static ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, - char* ifn); -static ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, - unsigned int id); -static ERL_NIF_TERM nlink_ifs(ErlNifEnv* env); -static unsigned int nlink_ifs_length(struct if_nameindex* p); - static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, ssize_t written, @@ -1346,6 +1340,10 @@ static void xabort(const char* expr, const char* file, int line); +static void dbg_printf( const char* format, ... ); +static int dbg_realtime(struct timespec* tsP); +static int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts); + static BOOLEAN_T extract_item_on_load(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, @@ -1389,14 +1387,28 @@ static char str_any[] = "any"; static char str_close[] = "close"; static char str_closed[] = "closed"; static char str_closing[] = "closing"; +static char str_debug[] = "debug"; static char str_error[] = "error"; static char str_false[] = "false"; +static char str_global_counters[] = "global_counters"; static char str_in4_sockaddr[] = "in4_sockaddr"; static char str_in6_sockaddr[] = "in6_sockaddr"; +static char str_iow[] = "iow"; 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_num_dlocal[] = "num_domain_local"; +static char str_num_dinet[] = "num_domain_inet"; +static char str_num_dinet6[] = "num_domain_inet6"; +static char str_num_pip[] = "num_proto_ip"; +static char str_num_psctp[] = "num_proto_sctp"; +static char str_num_ptcp[] = "num_proto_tcp"; +static char str_num_pudp[] = "num_proto_udp"; +static char str_num_sockets[] = "num_sockets"; +static char str_num_tdgrams[] = "num_type_dgram"; +static char str_num_tseqpkgs[] = "num_type_seqpacket"; +static char str_num_tstreams[] = "num_type_stream"; static char str_timeout[] = "timeout"; static char str_true[] = "true"; static char str_undefined[] = "undefined"; @@ -1426,12 +1438,26 @@ 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_debug; static ERL_NIF_TERM atom_error; static ERL_NIF_TERM atom_false; +static ERL_NIF_TERM atom_global_counters; static ERL_NIF_TERM atom_in4_sockaddr; static ERL_NIF_TERM atom_in6_sockaddr; +static ERL_NIF_TERM atom_iow; static ERL_NIF_TERM atom_loopback; static ERL_NIF_TERM atom_nif_abort; +static ERL_NIF_TERM atom_num_dinet; +static ERL_NIF_TERM atom_num_dinet6; +static ERL_NIF_TERM atom_num_dlocal; +static ERL_NIF_TERM atom_num_pip; +static ERL_NIF_TERM atom_num_psctp; +static ERL_NIF_TERM atom_num_ptcp; +static ERL_NIF_TERM atom_num_pudp; +static ERL_NIF_TERM atom_num_sockets; +static ERL_NIF_TERM atom_num_tdgrams; +static ERL_NIF_TERM atom_num_tseqpkgs; +static ERL_NIF_TERM atom_num_tstreams; static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_timeout; @@ -1466,7 +1492,7 @@ static ErlNifResourceTypeInit socketInit = { }; // Initiated when the nif is loaded -static SocketData socketData; +static SocketData data; /* ---------------------------------------------------------------------- @@ -1500,9 +1526,6 @@ static SocketData socketData; * * And some utility functions: * ------------------------------------------------------------- - * nif_link_if2idx/1 - * nif_link_idx2if/1 - * nif_link_ifs/0 * * And some socket admin functions: * ------------------------------------------------------------- @@ -1537,13 +1560,46 @@ ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, * Description: * This is currently just a placeholder... */ +#define MKCT(E, T, C) MKT2((E), (T), MKI((E), (C))) + static ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM info = enif_make_new_map(env); - return info; + if (argc != 0) { + return enif_make_badarg(env); + } else { + ERL_NIF_TERM numSockets = MKCT(env, atom_num_sockets, data.numSockets); + ERL_NIF_TERM numTypeDGrams = MKCT(env, atom_num_tdgrams, data.numTypeDGrams); + ERL_NIF_TERM numTypeStreams = MKCT(env, atom_num_tstreams, data.numTypeStreams); + ERL_NIF_TERM numTypeSeqPkgs = MKCT(env, atom_num_tseqpkgs, data.numTypeSeqPkgs); + ERL_NIF_TERM numDomLocal = MKCT(env, atom_num_dlocal, data.numDomainLocal); + ERL_NIF_TERM numDomInet = MKCT(env, atom_num_dinet, data.numDomainInet); + ERL_NIF_TERM numDomInet6 = MKCT(env, atom_num_dinet6, data.numDomainInet6); + ERL_NIF_TERM numProtoIP = MKCT(env, atom_num_pip, data.numProtoIP); + ERL_NIF_TERM numProtoTCP = MKCT(env, atom_num_ptcp, data.numProtoTCP); + ERL_NIF_TERM numProtoUDP = MKCT(env, atom_num_pudp, data.numProtoUDP); + ERL_NIF_TERM numProtoSCTP = MKCT(env, atom_num_psctp, data.numProtoSCTP); + ERL_NIF_TERM gcnt[] = {numSockets, + numTypeDGrams, numTypeStreams, numTypeSeqPkgs, + numDomLocal, numDomInet, numDomInet6, + numProtoIP, numProtoTCP, numProtoUDP, numProtoSCTP}; + unsigned int lenGCnt = sizeof(gcnt) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM lgcnt = MKLA(env, gcnt, lenGCnt); + ERL_NIF_TERM keys[] = {atom_debug, atom_iow, atom_global_counters}; + ERL_NIF_TERM vals[] = {BOOL2ATOM(data.dbg), BOOL2ATOM(data.iow), lgcnt}; + ERL_NIF_TERM info; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(keys) / sizeof(ERL_NIF_TERM); + + SASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &info)) + return enif_make_badarg(env); + + return info; + } } @@ -5185,186 +5241,6 @@ ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, - -/* ---------------------------------------------------------------------- - * nif_link_if2idx - * - * Description: - * Perform a Interface Name to Interface Index translation. - * - * Arguments: - * Ifn - Interface name to be translated. - */ - -static -ERL_NIF_TERM nif_link_if2idx(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) -{ - ERL_NIF_TERM eifn; - char ifn[IF_NAMESIZE+1]; - - if (argc != 1) { - return enif_make_badarg(env); - } - eifn = argv[0]; - - if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) - return make_error2(env, atom_einval); - - return nlink_if2idx(env, ifn); -} - - - -static -ERL_NIF_TERM nlink_if2idx(ErlNifEnv* env, - char* ifn) -{ - unsigned int idx = if_nametoindex(ifn); - - if (idx == 0) - return make_error2(env, sock_errno()); - else - return make_ok2(env, idx); - -} - - - -/* ---------------------------------------------------------------------- - * nif_link_idx2if - * - * Description: - * Perform a Interface Index to Interface Name translation. - * - * Arguments: - * Idx - Interface index to be translated. - */ - -static -ERL_NIF_TERM nif_link_idx2if(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) -{ - unsigned int idx; - - if ((argc != 1) || - !GET_UINT(env, argv[0], &idx)) { - return enif_make_badarg(env); - } - - return nlink_idx2if(env, idx); -} - - - -static -ERL_NIF_TERM nlink_idx2if(ErlNifEnv* env, - unsigned int idx) -{ - ERL_NIF_TERM result; - char* ifn = MALLOC(IF_NAMESIZE+1); - - if (ifn == NULL) - return enif_make_badarg(env); // PLACEHOLDER - - if (NULL == if_indextoname(idx, ifn)) { - result = make_ok2(env, MKS(env, ifn)); - } else { - result = make_error2(env, sock_errno()); - } - - FREE(ifn); // OR IS THIS AUTOMATIC? - - return result; -} - - - -/* ---------------------------------------------------------------------- - * nif_link_idx2if - * - * Description: - * Get network interface names and indexes. - * - */ - -static -ERL_NIF_TERM nif_link_ifs(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) -{ - if (argc != 0) { - return enif_make_badarg(env); - } - - return nlink_ifs(env); -} - - - -static -ERL_NIF_TERM nlink_ifs(ErlNifEnv* env) -{ - ERL_NIF_TERM result; - struct if_nameindex* ifs = if_nameindex(); - - if (ifs == NULL) { - result = make_error2(env, sock_errno()); - } else { - /* - * We got some interfaces: - * 1) Calculate how many - the only way is to iterate through the list - * until its end (which is indicated by an entry with index = zero - * and if_name = NULL). - * 2) Allocate an ERL_NIF_TERM array of the calculated length. - * 3) Iterate through the array of interfaces and for each create - * a two tuple: {Idx, If} - * - * Or shall we instead build a list in reverse order and then when - * its done, reverse that? Check - */ - unsigned int len = nlink_ifs_length(ifs); - - if (len > 0) { - ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); - unsigned int i; - - for (i = 0; i < len; i++) { - array[i] = MKT2(env, - MKI(env, ifs[i].if_index), - MKS(env, ifs[i].if_name)); - } - - result = make_ok2(env, MKLA(env, array, len)); - FREE(array); - } else { - result = make_ok2(env, enif_make_list(env, 0)); - } - } - - if (ifs != NULL) - if_freenameindex(ifs); - - return result; -} - - -static -unsigned int nlink_ifs_length(struct if_nameindex* p) -{ - unsigned int len = 0; - - while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { - len++; - } - - return len; -} - - - /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -7301,6 +7177,89 @@ void socket_down(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * D e b u g F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* + * Print a debug format string *with* both a timestamp and the + * the name of the *current* thread. + */ +static +void dbg_printf( const char* format, ... ) +{ + va_list args; + char f[512 + sizeof(format)]; // This has to suffice... + char stamp[30]; + struct timespec ts; + int res; + + /* + * We should really include self in the printout, so we can se which process + * are executing the code. But then I must change the API.... + * ....something for later. + */ + + if (!dbg_realtime(&ts)) { + if (dbg_timespec2str(stamp, sizeof(stamp), &ts) != 0) { + // res = enif_snprintf(f, sizeof(f), "NET [%s] %s", TSNAME(), format); + res = enif_snprintf(f, sizeof(f), "NET [%s]", format); + } else { + // res = enif_snprintf(f, sizeof(f), "NET[%s] [%s] %s", stamp, TSNAME(), format); + res = enif_snprintf(f, sizeof(f), "NET [%s] %s", stamp, format); + } + + if (res > 0) { + va_start (args, format); + erts_vfprintf (stdout, f, args); // TMP: use enif_vfprintf + va_end (args); + fflush(stdout); + } + } + + return; +} + + +static +int dbg_realtime(struct timespec* tsP) +{ + return clock_gettime(CLOCK_REALTIME, tsP); +} + + + + +/* + * Convert a timespec struct into a readable/printable string + */ +static +int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts) +{ + int ret, buflen; + struct tm t; + + tzset(); + if (localtime_r(&(ts->tv_sec), &t) == NULL) + return 1; + + ret = strftime(buf, len, "%F %T", &t); + if (ret == 0) + return 2; + len -= ret - 1; + buflen = strlen(buf); + + ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); + if (ret >= len) + return 3; + + return 0; +} + + + + /* ---------------------------------------------------------------------- * L o a d / u n l o a d / u p g r a d e F u n c t i o n s * ---------------------------------------------------------------------- @@ -7332,9 +7291,6 @@ ErlNifFunc socket_funcs[] = {"nif_getopt", 4, nif_getopt, 0}, /* Misc utility functions */ - {"nif_link_if2idx", 1, nif_link_if2idx, 0}, - {"nif_link_idx2if", 1, nif_link_idx2if, 0}, - {"nif_link_ifs", 0, nif_link_ifs, 0}, /* "Extra" functions to "complete" the socket interface. * For instance, the function nif_finalize_connection @@ -7437,54 +7393,55 @@ BOOLEAN_T extract_iow_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { - socketData.dbg = extract_debug_on_load(env, load_info, - SOCKET_NIF_DEBUG_DEFAULT); + data.dbg = extract_debug_on_load(env, load_info, + SOCKET_NIF_DEBUG_DEFAULT); + data.iow = extract_iow_on_load(env, load_info, + SOCKET_NIF_IOW_DEFAULT); /* +++ Global Counters +++ */ - socketData.cntMtx = MCREATE("socket[gcnt]"); - socketData.iow = extract_iow_on_load(env, - load_info, - SOCKET_NIF_IOW_DEFAULT); - socketData.numSockets = 0; - socketData.numTypeDGrams = 0; - socketData.numTypeStreams = 0; - socketData.numTypeSeqPkg = 0; - socketData.numDomainLocal = 0; - socketData.numDomainInet = 0; - socketData.numDomainInet6 = 0; - socketData.numProtoIP = 0; - socketData.numProtoTCP = 0; - socketData.numProtoUDP = 0; - socketData.numProtoSCTP = 0; + data.cntMtx = MCREATE("socket[gcnt]"); + data.numSockets = 0; + data.numTypeDGrams = 0; + data.numTypeStreams = 0; + data.numTypeSeqPkgs = 0; + data.numDomainLocal = 0; + data.numDomainInet = 0; + data.numDomainInet6 = 0; + data.numProtoIP = 0; + data.numProtoTCP = 0; + data.numProtoUDP = 0; + data.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); - // atom_binary = MKA(env, str_binary); - // atom_buildDate = MKA(env, str_buildDate); atom_close = MKA(env, str_close); atom_closed = MKA(env, str_closed); atom_closing = MKA(env, str_closing); + atom_debug = MKA(env, str_debug); atom_error = MKA(env, str_error); atom_false = MKA(env, str_false); + atom_global_counters = MKA(env, str_global_counters); atom_in4_sockaddr = MKA(env, str_in4_sockaddr); atom_in6_sockaddr = MKA(env, str_in6_sockaddr); + atom_iow = MKA(env, str_iow); 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); + atom_num_dinet = MKA(env, str_num_dinet); + atom_num_dinet6 = MKA(env, str_num_dinet6); + atom_num_dlocal = MKA(env, str_num_dlocal); + atom_num_pip = MKA(env, str_num_pip); + atom_num_psctp = MKA(env, str_num_psctp); + atom_num_ptcp = MKA(env, str_num_ptcp); + atom_num_pudp = MKA(env, str_num_pudp); + atom_num_sockets = MKA(env, str_num_sockets); + atom_num_tdgrams = MKA(env, str_num_tdgrams); + atom_num_tseqpkgs = MKA(env, str_num_tseqpkgs); + atom_num_tstreams = MKA(env, str_num_tstreams); atom_ok = MKA(env, str_ok); - // atom_once = MKA(env, str_once); - // atom_passive = MKA(env, str_passive); - // atom_receiver = MKA(env, str_receiver); atom_select = MKA(env, str_select); - // atom_tcp_closed = MKA(env, str_tcp_closed); atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); atom_undefined = MKA(env, str_undefined); - // atom_version = MKA(env, str_version); atom_lowdelay = MKA(env, str_lowdelay); atom_throughput = MKA(env, str_throughput); @@ -7501,15 +7458,14 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_exalloc = MKA(env, str_exalloc); atom_exbadstate = MKA(env, str_exbadstate); atom_exbusy = MKA(env, str_exbusy); - // atom_exnotopen = MKA(env, str_exnotopen); atom_exmon = MKA(env, str_exmon); atom_exself = MKA(env, str_exself); atom_exsend = MKA(env, str_exsend); // For storing "global" things... - // socketData.env = enif_alloc_env(); // We should really check - // socketData.version = MKA(env, ERTS_VERSION); - // socketData.buildDate = MKA(env, ERTS_BUILD_DATE); + // data.env = enif_alloc_env(); // We should really check + // data.version = MKA(env, ERTS_VERSION); + // data.buildDate = MKA(env, ERTS_BUILD_DATE); sockets = enif_open_resource_type_x(env, "sockets", -- cgit v1.2.3 From 1f4a0eb6629d813be8656db239ee5b98a78088a9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 13 Jun 2018 12:42:56 +0200 Subject: [socket-nif] Added some use of debug and fixed bind Added (some) use of the debug printouts in (nif-) open and bind. Also fixed handling of the address argument in the bind function(s) (since it was changed to be of the in_sockaddr()). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 426 +++++++++++++++------------------ 1 file changed, 193 insertions(+), 233 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 3205ed1720..badaa8d988 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -433,6 +433,7 @@ typedef union { #define IS_ATOM(E, TE) enif_is_atom((E), (TE)) #define IS_BIN(E, TE) enif_is_binary((E), (TE)) +#define IS_MAP(E, TE) enif_is_map((E), (TE)) #define IS_NUM(E, TE) enif_is_number((E), (TE)) #define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) @@ -456,7 +457,8 @@ typedef union { dbg_printf proto; \ fflush(stdout); \ } -#define SDBG( proto ) SDEBUG( data.dbg , proto ) +#define SGDBG( proto ) SDEBUG( data.dbg , proto ) +#define SSDBG( __D__ , proto ) SDEBUG( (__D__)->dbg , proto ) /* =================================================================== * @@ -1155,43 +1157,45 @@ 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, +static char* decode_in_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +static char* decode_in4_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* eIn4SockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +static char* decode_in4_sockaddr_atomaddr(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, +static char* decode_in4_sockaddr_addr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +#if defined(HAVE_IN6) && defined(AF_INET6) +static char* decode_in6_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* eIn6SockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +static char* 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 char* decode_in6_sockaddr_addr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + unsigned int flowInfo, + unsigned int scopeId, + SocketAddress* sockAddrP, + unsigned int* addrLenP); +#endif static char* decode_laddress(ErlNifEnv* env, int domain, ERL_NIF_TERM localAddr, @@ -1202,11 +1206,7 @@ static char* decode_laddress_binary(ErlNifEnv* env, ERL_NIF_TERM localAddr, SocketAddress* localP, unsigned int* addrLenP); -static char* decode_laddress_tuple(ErlNifEnv* env, - int domain, - ERL_NIF_TERM laddr, - SocketAddress* localP, - unsigned int* addrLenP); +/* static char* decode_address_tuple(ErlNifEnv* env, int domain, const ERL_NIF_TERM* addrt, @@ -1220,6 +1220,7 @@ static char* decode_address_atom(ErlNifEnv* env, int port, SocketAddress* localP, unsigned int* addrLenP); +*/ /* static char* decode_send_addr(ErlNifEnv* env, int domain, @@ -1610,10 +1611,10 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, * Create an endpoint for communication. * * Arguments: - * Domain - * Type - * Protocol - * Extra - A map with obscure options. + * Domain - The domain, for example 'inet' + * Type - Type of socket, for example 'stream' + * Protocol - The protocol, for example 'tcp' + * Extra - A map with "obscure" options. * Currently the only allowed option is netns (network namespace). * This is *only* allowed on linux! */ @@ -1626,18 +1627,28 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, int domain, type, proto; char* netns; ERL_NIF_TERM emap; + ERL_NIF_TERM result; + SGDBG( ("nif_open -> entry with %d args\r\n", argc) ); + /* Extract arguments and perform preliminary validation */ if ((argc != 4) || - !enif_get_int(env, argv[0], &edomain) || - !enif_get_int(env, argv[1], &etype) || - !enif_get_int(env, argv[2], &eproto) || - !enif_is_map(env, argv[3])) { + !GET_INT(env, argv[0], &edomain) || + !GET_INT(env, argv[1], &etype) || + !GET_INT(env, argv[2], &eproto) || + !IS_MAP(env, argv[3])) { return enif_make_badarg(env); } emap = argv[3]; + SGDBG( ("nif_open -> " + "\r\n edomain: %T" + "\r\n etype: %T" + "\r\n eproto: %T" + "\r\n extra: %T" + "\r\n", argv[0], argv[1], argv[2], argv[3]) ); + if (!edomain2domain(edomain, &domain)) return enif_make_badarg(env); @@ -1655,13 +1666,23 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, netns = NULL; #endif - return nopen(env, domain, type, proto, netns); + result = nopen(env, domain, type, proto, netns); + + SGDBG( ("nif_open -> done with result: " + "\r\n %T" + "\r\n", result) ); + + return result; } /* nopen - create an endpoint for communication * * Assumes the input has been validated. + * + * Normally we want debugging on (individual) sockets to be controlled + * by the sockets own debug flag. But since we don't even have a socket + * yet, we must use the global debug flag. */ static ERL_NIF_TERM nopen(ErlNifEnv* env, @@ -1677,9 +1698,16 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, int current_ns; #endif + SGDBG( ("nopen -> entry with" + "\r\n domain: %d" + "\r\n type: %d" + "\r\n protocol: %d" + "\r\n netns: %s" + "\r\n", domain, type, protocol, ((netns == NULL) ? "NULL" : netns)) ); #ifdef HAVE_SETNS - if (!change_network_namespace(netns, ¤t_ns, &save_errno)) + if ((netns != NULL) && + !change_network_namespace(netns, ¤t_ns, &save_errno)) return make_error2(env, save_errno); #endif @@ -1687,7 +1715,8 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, return make_error2(env, sock_errno()); #ifdef HAVE_SETNS - if (!restore_network_namespace(current_ns, sock, &save_errno)) + if ((netns != NULL) && + !restore_network_namespace(current_ns, sock, &save_errno)) return make_error2(env, save_errno); if (netns != NULL) @@ -1736,12 +1765,16 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, #ifdef __WIN32__ - /* What is the point of this? + /* */ SELECT(env, event, @@ -1769,6 +1802,9 @@ BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err) int current_ns = 0; int new_ns = 0; + SGDBG( ("change_network_namespace -> entry with" + "\r\n new ns: %s", netns) ); + if (netns != NULL) { current_ns = open("/proc/self/ns/net", O_RDONLY); if (current_ns == INVALID_SOCKET) { @@ -1816,6 +1852,10 @@ static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err) { int save_errno; + + SGDBG( ("restore_network_namespace -> entry with" + "\r\n ns: %d", ns) ); + if (ns != INVALID_SOCKET) { if (setns(ns, CLONE_NEWNET) != 0) { /* XXX Failed to restore network namespace. @@ -1879,6 +1919,12 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, return enif_make_badarg(env); } + SSDBG( descP, + ("nif_bind -> " + "\r\n Socket: %T" + "\r\n Addr: %T" + "\r\n", argv[0], argv[1]) ); + /* Basic arg validation: * - if binary domain must be local (unix) * - if tuple domain must be either inet or inet6 @@ -1910,11 +1956,19 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, { SocketAddress local; unsigned int addrLen = 0; - char* err; + char* xerr; int port; - if ((err = decode_laddress(env, descP->domain, addr, &local, &addrLen)) != NULL) - return make_error1(env, err); + SSDBG( descP, + ("nbind -> entry with" + "\r\n addr: %T" + "\r\n", addr) ); + + if ((xerr = decode_laddress(env, + descP->domain, addr, &local, &addrLen)) != NULL) + return make_error1(env, xerr); + + SSDBG( descP, ("nbind -> try bind\r\n") ); if (IS_SOCKET_ERROR(sock_bind(descP->sock, (struct sockaddr*) &local, addrLen))) { @@ -1931,15 +1985,17 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, port = 0; } - return make_ok2(env, enif_make_int(env, port)); + SSDBG( descP, ("nbind -> done with port = %d\r\n", port) ); + + return make_ok2(env, MKI(env, port)); } /* Decode the (local) address. The format of the address should - * either be an binary (domain = local) or a two tuple (first - * part the actual address tuple and the second part the port - * number) if the domain is either INET or INET6. + * either be an binary (domain = local) or an in_sockaddr(), which + * is either a in4_sockaddr record or a int6_sockaddr record + * (if domain is either inet or inet6). */ static char* decode_laddress(ErlNifEnv* env, @@ -1951,7 +2007,7 @@ char* decode_laddress(ErlNifEnv* env, if (IS_BIN(env, localAddr)) { return decode_laddress_binary(env, domain, localAddr, localP, addrLenP); } else if (IS_TUPLE(env, localAddr)) { - return decode_laddress_tuple(env, domain, localAddr, localP, addrLenP); + return decode_in_sockaddr(env, localAddr, localP, addrLenP); } else { return str_einval; } @@ -1960,7 +2016,7 @@ char* decode_laddress(ErlNifEnv* env, /* Only for domain = local (unix) - * The erlang interface module ensures that the size of the + * The erlang interface module (socket) ensures that the size of the * binary is > 0, so we need not do that here. */ static @@ -2014,107 +2070,6 @@ char* decode_laddress_binary(ErlNifEnv* env, } -/* Only for domain = INET and INET6 - * The (local) address here is a two tuple: - * {Addr, Port} - * where - * Addr: - * - a tuple of size 4 (INET) or size 8 (INET6) - * - the atoms 'any' or 'loopback' - * Port: - * - the port number (int) - * - */ -static -char* decode_laddress_tuple(ErlNifEnv* env, - int domain, - ERL_NIF_TERM laddr, - SocketAddress* localP, - unsigned int* addrLenP) -{ - const ERL_NIF_TERM* laddrt; - int laddrtSz; - int port; - - /* First, get the tuple and verify its size (2) */ - - if (!GET_TUPLE(env, laddr, &laddrtSz, &laddrt)) - return str_einval; - - if (laddrtSz != 2) - return str_einval; - - /* So far so good. The first element is either a tuple or an atom */ - - if (IS_TUPLE(env, laddrt[0]) && - IS_NUM(env, laddrt[1])) { - - /* We handle two different tuples: - * - size 4 (INET) - * - size 8 (INET6) - */ - - const ERL_NIF_TERM* addrt; - int addrtSz; - - if (!GET_TUPLE(env, laddrt[0], &addrtSz, &addrt)) - return str_einval; // PLACEHOLDER - - if (!GET_INT(env, laddrt[1], &port)) - return str_einval; // PLACEHOLDER - - switch (domain) { - case AF_INET: - if (addrtSz != 4) - return str_einval; - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - if (addrtSz != 8) - return str_einval; - break; -#endif - - default: - return str_eafnosupport; - break; - } - - return decode_address_tuple(env, - domain, addrt, port, - localP, addrLenP); - - } else if (IS_ATOM(env, laddrt[0]) && - IS_NUM(env, laddrt[1])) { - - /* There are only two atoms we handle: - * - any - * - loopback - */ - - unsigned int len; - char a[16]; // Just in case... - - if (!(GET_ATOM_LEN(env, laddrt[0], &len) && - (len > 0) && - (len <= (sizeof("loopback"))))) - return str_einval; - - if (!GET_ATOM(env, laddrt[0], a, sizeof(a))) - return str_einval; - - return decode_address_atom(env, - domain, a, len, port, - localP, addrLenP); - - } else { - return str_einval; - } - -} - - /* ---------------------------------------------------------------------- * nif_connect @@ -2136,6 +2091,7 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, { SocketDescriptor* descP; ERL_NIF_TERM eSockAddr; + char* xres; /* Extract arguments and perform preliminary validation */ @@ -2145,9 +2101,9 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, } eSockAddr = argv[1]; - if (!decode_in_sockaddr(env, eSockAddr, - &descP->remote, &descP->addrLen)) { - return enif_make_badarg(env); + if ((xres = decode_in_sockaddr(env, eSockAddr, + &descP->remote, &descP->addrLen)) != NULL) { + return make_error1(env, xres); } return nconnect(env, descP); @@ -2768,6 +2724,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, ERL_NIF_TERM eSockAddr; SocketAddress remoteAddr; unsigned int remoteAddrLen; + char* xres; /* Extract arguments and perform preliminary validation */ @@ -2787,10 +2744,10 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, if (!esendflags2sendflags(eflags, &flags)) return make_error(env, atom_einval); - if (!decode_in_sockaddr(env, eSockAddr, - &remoteAddr, - &remoteAddrLen)) - return make_error(env, atom_einval); + if ((xres = decode_in_sockaddr(env, eSockAddr, + &remoteAddr, + &remoteAddrLen)) != NULL) + return make_error1(env, xres); return nsendto(env, descP, sendRef, &data, flags, &remoteAddr, remoteAddrLen); } @@ -5622,17 +5579,17 @@ char* decode_send_addr_tuple(ErlNifEnv* env, * */ static -BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - SocketAddress* sockAddrP, - unsigned int* addrLenP) +char* 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; + char* result = NULL; if (!GET_TUPLE(env, eSockAddr, &addrtSz, &addrt)) - return FALSE; + return str_einval; /* * We use the tuple size to figure out which @@ -5650,7 +5607,7 @@ BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, #endif default: - result = FALSE; + result = str_eafnosupport; break; } @@ -5665,20 +5622,20 @@ BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, * 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) +char* 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; + return str_einval; /* 2: Get the port number */ if (!GET_INT(env, eIn4SockAddr[1], &port)) - return FALSE; + return str_einval; /* 3: Get the address. * It can either be the atoms: any | loopback, @@ -5691,18 +5648,18 @@ BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, return decode_in4_sockaddr_addr(env, eIn4SockAddr[2], port, sockAddrP, addrLenP); } else { - return FALSE; + return str_einval; } } static -BOOLEAN_T decode_in4_sockaddr_atomaddr(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - int port, - SocketAddress* sockAddrP, - unsigned int* addrLenP) +char* decode_in4_sockaddr_atomaddr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + SocketAddress* sockAddrP, + unsigned int* addrLenP) { struct in_addr addr; @@ -5711,7 +5668,7 @@ BOOLEAN_T decode_in4_sockaddr_atomaddr(ErlNifEnv* env, } else if (COMPARE(atom_any, eAddr) == 0) { addr.s_addr = sock_htonl(INADDR_ANY); } else { - return FALSE; + return str_einval; } sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); @@ -5721,9 +5678,9 @@ BOOLEAN_T decode_in4_sockaddr_atomaddr(ErlNifEnv* env, 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); + *addrLenP = sizeof(struct sockaddr_in); - return TRUE; + return NULL; } @@ -5731,11 +5688,11 @@ BOOLEAN_T decode_in4_sockaddr_atomaddr(ErlNifEnv* env, * 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) +char* decode_in4_sockaddr_addr(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + int port, + SocketAddress* sockAddrP, + unsigned int* addrLenP) { const ERL_NIF_TERM* ip4AddrT; int ip4AddrTSz; @@ -5744,10 +5701,10 @@ BOOLEAN_T decode_in4_sockaddr_addr(ErlNifEnv* env, /* This shall be a 4 tuple */ if (!GET_TUPLE(env, eAddr, &ip4AddrTSz, &ip4AddrT)) - return FALSE; + return str_einval; if (ip4AddrTSz != 4) - return FALSE; + return str_einval; sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in)); #ifndef NO_SA_LEN @@ -5757,13 +5714,13 @@ BOOLEAN_T decode_in4_sockaddr_addr(ErlNifEnv* env, sockAddrP->sai.sin_port = sock_htons(port); for (a = 0; a < 4; a++) { if (!GET_INT(env, ip4AddrT[a], &v)) - return FALSE; + return str_einval; addr[a] = v; } sys_memcpy(&sockAddrP->sai.sin_addr, &addr, sizeof(addr)); *addrLenP = sizeof(struct sockaddr_in); - return TRUE; + return NULL; } @@ -5777,29 +5734,29 @@ BOOLEAN_T decode_in4_sockaddr_addr(ErlNifEnv* env, */ #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) +char* 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; + return str_einval; /* 2: Get the port number */ if (!GET_INT(env, eIn6SockAddr[1], &port)) - return FALSE; + return str_einval; /* 4: Get the flowinfo */ if (!GET_UINT(env, eIn6SockAddr[3], &flowInfo)) - return FALSE; + return str_einval; /* 5: Get the scope_id */ if (!GET_UINT(env, eIn6SockAddr[4], &scopeId)) - return FALSE; + return str_einval; /* 3: Get the address. * It can either be the atoms: any | loopback, @@ -5814,7 +5771,7 @@ BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, flowInfo, scopeId, sockAddrP, addrLenP); } else { - return FALSE; + return str_einval; } } #endif @@ -5822,13 +5779,13 @@ BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, #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) +char* 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; @@ -5837,7 +5794,7 @@ BOOLEAN_T decode_in6_sockaddr_atomaddr(ErlNifEnv* env, } else if (COMPARE(atom_any, eAddr) == 0) { addr = &in6addr_any; } else { - return FALSE; + return str_einval; } sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); @@ -5851,7 +5808,7 @@ BOOLEAN_T decode_in6_sockaddr_atomaddr(ErlNifEnv* env, sockAddrP->sai6.sin6_addr = *addr; *addrLenP = sizeof(struct sockaddr_in6); - return TRUE; + return NULL; } #endif @@ -5860,13 +5817,13 @@ BOOLEAN_T decode_in6_sockaddr_atomaddr(ErlNifEnv* env, #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) +char* 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; @@ -5875,10 +5832,10 @@ BOOLEAN_T decode_in6_sockaddr_addr(ErlNifEnv* env, /* This shall be a 8 tuple */ if (!GET_TUPLE(env, eAddr, &ip6AddrTSz, &ip6AddrT)) - return FALSE; + return str_einval; if (ip6AddrTSz != 8) - return FALSE; + return str_einval; sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN @@ -5893,14 +5850,14 @@ BOOLEAN_T decode_in6_sockaddr_addr(ErlNifEnv* env, */ for (a = 0; a < 8; a++) { if (!GET_INT(env, ip6AddrT[a], &v)) - return FALSE; + return str_einval; 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; + return NULL; } #endif @@ -5908,6 +5865,7 @@ BOOLEAN_T decode_in6_sockaddr_addr(ErlNifEnv* env, /* Decode the 4- or 8-element address tuple * and initiate the socket address structure. */ +/* static char* decode_address_tuple(ErlNifEnv* env, int domain, @@ -5917,9 +5875,9 @@ char* decode_address_tuple(ErlNifEnv* env, unsigned int* addrLenP) { - /* We now *know* that the size of the tuple is correct, + / * We now *know* that the size of the tuple is correct, * so we don't need to check anything here, just unpack. - */ + * / switch (domain) { case AF_INET: @@ -5957,9 +5915,9 @@ char* decode_address_tuple(ErlNifEnv* env, addrP->sai6.sin6_family = domain; addrP->sai6.sin6_port = sock_htons(port); addrP->sai6.sin6_flowinfo = 0; - /* The address tuple is of size 8 + / * 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, addrt[a], &v)) return str_einval; @@ -5973,11 +5931,12 @@ char* decode_address_tuple(ErlNifEnv* env, break; #endif - } /* switch (domain) */ + } / * switch (domain) * / return str_eafnosupport; } +*/ /* Encode the 4- or 8-element address tuple from the socket address structure. * @@ -6108,6 +6067,7 @@ void encode_address(ErlNifEnv* env, /* Decode the address when its an atom. * Currently we only accept two atoms: 'any' and 'loopback' */ +/* static char* decode_address_atom(ErlNifEnv* env, int domain, @@ -6127,7 +6087,7 @@ char* decode_address_atom(ErlNifEnv* env, return str_einval; } - /* If we get this far, we *know* its either 'any' or 'loopback' */ + / * If we get this far, we *know* its either 'any' or 'loopback' * / switch (domain) { case AF_INET: @@ -6178,7 +6138,7 @@ char* decode_address_atom(ErlNifEnv* env, return NULL; } - +*/ static BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val) @@ -7203,11 +7163,11 @@ void dbg_printf( const char* format, ... ) if (!dbg_realtime(&ts)) { if (dbg_timespec2str(stamp, sizeof(stamp), &ts) != 0) { - // res = enif_snprintf(f, sizeof(f), "NET [%s] %s", TSNAME(), format); - res = enif_snprintf(f, sizeof(f), "NET [%s]", format); + // res = enif_snprintf(f, sizeof(f), "SOCKET [%s] %s", TSNAME(), format); + res = enif_snprintf(f, sizeof(f), "SOCKET [%s]", format); } else { - // res = enif_snprintf(f, sizeof(f), "NET[%s] [%s] %s", stamp, TSNAME(), format); - res = enif_snprintf(f, sizeof(f), "NET [%s] %s", stamp, format); + // res = enif_snprintf(f, sizeof(f), "SOCKET [%s] [%s] %s", stamp, TSNAME(), format); + res = enif_snprintf(f, sizeof(f), "SOCKET [%s] %s", stamp, format); } if (res > 0) { -- cgit v1.2.3 From 6b2c750c53a288d999c0f63dc6fe26a22d63ed00 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 29 May 2018 10:54:55 +0200 Subject: [net-nif] Added preliminary net nif file OTP-14831 --- erts/emulator/nifs/common/net_nif.c | 1788 +++++++++++++++++++++++++++++++++++ 1 file changed, 1788 insertions(+) create mode 100644 erts/emulator/nifs/common/net_nif.c (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c new file mode 100644 index 0000000000..b8fa6628a7 --- /dev/null +++ b/erts/emulator/nifs/common/net_nif.c @@ -0,0 +1,1788 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. 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. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : The NIF (C) part of the net interface + * This is a module of miscellaneous functions. + * ---------------------------------------------------------------------- + * + */ + +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* If we HAVE_SCTP_H and Solaris, we need to define the following in + * order to get SCTP working: + */ +#if (defined(HAVE_SCTP_H) && defined(__sun) && defined(__SVR4)) +#define SOLARIS10 1 +/* WARNING: This is not quite correct, it may also be Solaris 11! */ +#define _XPG4_2 +#define __EXTENSIONS__ +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_UIO_H +#include +#endif + +#ifdef HAVE_NET_IF_DL_H +#include +#endif + +#ifdef HAVE_IFADDRS_H +#include +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +#include +#endif + +#ifdef HAVE_SYS_UN_H +#include +#endif + +/* SENDFILE STUFF HERE IF WE NEED IT... */ + +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define __DARWIN__ 1 +#endif + + +#ifdef __WIN32__ +#define STRNCASECMP strncasecmp +#define INCL_WINSOCK_API_TYPEDEFS 1 + +#ifndef WINDOWS_H_INCLUDES_WINSOCK2_H +#include +#endif +#include +#include /* NEED VC 6.0 or higher */ + +/* Visual studio 2008+: NTDDI_VERSION needs to be set for iphlpapi.h + * to define the right structures. It needs to be set to WINXP (or LONGHORN) + * for IPV6 to work and it's set lower by default, so we need to change it. + */ +#ifdef HAVE_SDKDDKVER_H +# include +# ifdef NTDDI_VERSION +# undef NTDDI_VERSION +# endif +# define NTDDI_VERSION NTDDI_WINXP +#endif +#include + +#undef WANT_NONBLOCKING +#include "sys.h" + +#else /* !__WIN32__ */ + +#include +#ifdef NETDB_H_NEEDS_IN_H +#include +#endif +#include + +#include +#include + +#ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H +#include +#endif + +#include +#include +#include + +#include +#ifdef HAVE_ARPA_NAMESER_H +#include +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#include + +#ifdef HAVE_SCHED_H +#include +#endif + +#ifdef HAVE_SETNS_H +#include +#endif + +#define HAVE_UDP + +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif +#include "sys.h" + +#endif + +#include + + +/* All platforms fail on malloc errors. */ +#define FATAL_MALLOC + + + +/* *** Boolean *type* stuff... *** */ +typedef unsigned int BOOLEAN_T; +#define TRUE 1 +#define FALSE 0 +#define BOOL2STR(__B__) ((__B__) ? "true" : "false") +#define BOOL2ATOM(__B__) ((__B__) ? atom_true : atom_false) + +/* Two byte integer decoding */ +#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ + (((unsigned char*) (s))[1])) + +#define SASSERT(e) \ + ((void) ((e) ? 1 : (xabort(#e, __func__, __FILE__, __LINE__), 0))) + + +/* Debug stuff... */ +#define SOCKET_NIF_DEBUG_DEFAULT TRUE + +/* Various defaults... */ +#define SOCKET_DEBUG_DEFAULT TRUE +#define SOCKET_IOW_DEFAULT FALSE + +/* Counters and stuff (Don't know where to sent this stuff anyway) */ +#define SOCKET_NIF_IOW_DEFAULT FALSE + + +/* Used in debug printouts */ +#ifdef __WIN32__ +#define LLU "%I64u" +#else +#define LLU "%llu" +#endif +typedef unsigned long long llu_t; + + + +/* Socket stuff */ +// #define INVALID_SOCKET -1 +// #define INVALID_EVENT -1 +// #define SOCKET_ERROR -1 + +// #define SOCKET int +// #define HANDLE long int + + +/* *** Misc macros and defines *** */ + +#ifdef __WIN32__ +#define get_errno() WSAGetLastError() +#else +#define get_errno() errno +#endif + +// #if defined(TCP_CA_NAME_MAX) +// #define SOCKET_OPT_TCP_CONGESTION_NAME_MAX TCP_CA_NAME_MAX +// #else +/* This is really excessive, but just in case... */ +// #define SOCKET_OPT_TCP_CONGESTION_NAME_MAX 256 +// #endif + +#define HOSTNAME_LEN 256 +#define SERVICE_LEN 256 + + +/* =================================================================== * + * * + * Various enif macros * + * * + * =================================================================== */ + +#define MALLOC(SZ) enif_alloc((SZ)) +#define FREE(P) enif_free((P)) + +#define MKA(E,S) enif_make_atom((E), (S)) +#define MKBIN(E,B) enif_make_binary((E), (B)) +#define MKI(E,I) enif_make_int((E), (I)) +#define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) +#define MKEL(E) enif_make_list((E), 0) +#define MKREF(E) enif_make_ref((E)) +#define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) +#define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) +#define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) +#define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) +#define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) +#define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) +#define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ + enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) + +#define MCREATE(N) enif_mutex_create((N)) +#define MDESTROY(M) enif_mutex_destroy((M)) +#define MLOCK(M) enif_mutex_lock((M)) +#define MUNLOCK(M) enif_mutex_unlock((M)) + +#define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) +#define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) + +#define SELECT(E,FD,M,O,P,R) \ + enif_select((E), (FD), (M), (O), (P), (R)) +#define SELECT_READ(E, DP, P, R) \ + SELECT((E), (DP)->sock, (ERL_NIF_SELECT_READ), (DP), (P), (R)) +#define SELECT_WRITE(E, DP, P, R) \ + SELECT((E), (DP)->sock, (ERL_NIF_SELECT_WRITE), (DP), (P), (R)) +#define SELECT_STOP(E, DP) \ + enif_select((E), (DP)->sock, (ERL_NIF_SELECT_STOP), (DP), NULL, atom_undefined) + +#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)) +#define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) +#define IS_LIST(E, TE) enif_is_list((E), (TE)) + +#define COMPARE(L, R) enif_compare((L), (R)) + +#define GET_ATOM_LEN(E, TE, LP) \ + enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) +#define GET_ATOM(E, TE, BP, MAX) \ + enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) +#define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) +#define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) +#define GET_LIST_ELEM(E, L, HP, TP) enif_get_list_cell((E), (L), (HP), (TP)) +#define GET_LIST_LEN(E, L, LP) enif_get_list_length((E), (L), (LP)) +#define GET_STR(E, L, B, SZ) \ + enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) +#define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) +#define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) + +#define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) +#define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) + + +#ifdef HAVE_SOCKLEN_T +# define SOCKLEN_T socklen_t +#else +# define SOCKLEN_T size_t +#endif + + +/* The general purpose socket address */ +typedef union { + struct sockaddr sa; + + struct sockaddr_in in; + +#ifdef HAVE_IN6 + struct sockaddr_in6 in6; +#endif + +} SockAddress; + + + +/* ---------------------------------------------------------------------- + * F o r w a r d s + * ---------------------------------------------------------------------- + */ + +/* THIS IS JUST TEMPORARY */ +extern char* erl_errno_id(int error); + + +static ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_info(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); + +static ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); + +static ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); + +static ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_if_names(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); + +static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, + const SockAddress* saP, + socklen_t saLen, + int flags); +static ERL_NIF_TERM nif_name2index(ErlNifEnv* env, + char* ifn); +static ERL_NIF_TERM nif_index2name(ErlNifEnv* env, + unsigned int id); +static ERL_NIF_TERM nif_names(ErlNifEnv* env); +static unsigned int nif_names_length(struct if_nameindex* p); + +/* +static void net_dtor(ErlNifEnv* env, void* obj); +static void net_stop(ErlNifEnv* env, + void* obj, + int fd, + int is_direct_call); +static void net_down(ErlNifEnv* env, + void* obj, + const ErlNifPid* pid, + const ErlNifMonitor* mon); +*/ +static BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM eAddr, + SockAddress* saP, + socklen_t* saLen); +static BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* addrt, + SockAddress* saP, + socklen_t* saLen); +#if defined(HAVE_IN6) && defined(AF_INET6) +static BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* addrt, + SockAddress* saP, + socklen_t* saLen); +#endif +static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, + const ERL_NIF_TERM eflags, + int* flags); +static BOOLEAN_T decode_nameinfo_flags_list(ErlNifEnv* env, + const ERL_NIF_TERM eflags, + int* flags); +static ERL_NIF_TERM encode_address_info(ErlNifEnv* env, + struct addrinfo* addrInfo); +static unsigned int address_info_length(struct addrinfo* addrInfoP); + +static ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM val); +// static ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2); +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 void xabort(const char* expr, + const char* func, + const char* file, + int line); +*/ + +static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); + + +#if HAVE_IN6 +# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY +# if HAVE_DECL_IN6ADDR_ANY_INIT +static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } }; +# else +static const struct in6_addr in6addr_any = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; +# endif /* HAVE_IN6ADDR_ANY_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_ANY */ + +# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK +# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT +static const struct in6_addr in6addr_loopback = + { { IN6ADDR_LOOPBACK_INIT } }; +# else +static const struct in6_addr in6addr_loopback = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }; +# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */ +#endif /* HAVE_IN6 */ + + + +/* *** String constants *** */ +static char str_dgram[] = "dgram"; +static char str_error[] = "error"; +static char str_idn[] = "idn"; +static char str_idna_allow_unassigned[] = "idna_allow_unassigned"; +static char str_idna_use_std3_ascii_rules[] = "idna_use_std3_ascii_rules"; +static char str_namereqd[] = "namereqd"; +static char str_name_info[] = "name_info"; +static char str_nofqdn[] = "nofqdn"; +static char str_numerichost[] = "numerichost"; +static char str_numericserv[] = "numericserv"; +static char str_ok[] = "ok"; +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_eafnosupport[] = "eafnosupport"; +static char str_eagain[] = "eagain"; +static char str_ebadflags[] = "ebadflags"; +static char str_efail[] = "efail"; +static char str_efamily[] = "efamily"; +static char str_einval[] = "einval"; +// static char str_eisconn[] = "eisconn"; +static char str_emem[] = "emem"; +static char str_enoname[] = "enoname"; +// static char str_enotclosing[] = "enotclosing"; +// static char str_enotconn[] = "enotconn"; +static char str_eoverflow[] = "eoverflow"; +// static char str_exalloc[] = "exalloc"; +// static char str_exbadstate[] = "exbadstate"; +// static char str_exbusy[] = "exbusy"; +// static char str_exmon[] = "exmonitor"; // failed monitor +// static char str_exself[] = "exself"; // failed self +// static char str_exsend[] = "exsend"; // failed send + + +/* *** Atoms *** */ + +static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_dgram; +static ERL_NIF_TERM atom_idn; +static ERL_NIF_TERM atom_idna_allow_unassigned; +static ERL_NIF_TERM atom_idna_use_std3_ascii_rules; +static ERL_NIF_TERM atom_namereqd; +static ERL_NIF_TERM atom_name_info; +static ERL_NIF_TERM atom_nofqdn; +static ERL_NIF_TERM atom_numerichost; +static ERL_NIF_TERM atom_numericserv; +static ERL_NIF_TERM atom_ok; +// static ERL_NIF_TERM atom_select; +// static ERL_NIF_TERM atom_timeout; +static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_undefined; + +// static ERL_NIF_TERM atom_lowdelay; +// static ERL_NIF_TERM atom_throughput; +// static ERL_NIF_TERM atom_reliability; +// static ERL_NIF_TERM atom_mincost; + +// static ERL_NIF_TERM atom_eafnosupport; +static ERL_NIF_TERM atom_eagain; +static ERL_NIF_TERM atom_ebadflags; +static ERL_NIF_TERM atom_efail; +static ERL_NIF_TERM atom_efamily; +static ERL_NIF_TERM atom_einval; +// static ERL_NIF_TERM atom_eisconn; +static ERL_NIF_TERM atom_emem; +static ERL_NIF_TERM atom_enoname; +// static ERL_NIF_TERM atom_enotclosing; +// static ERL_NIF_TERM atom_enotconn; +static ERL_NIF_TERM atom_eoverflow; +// static ERL_NIF_TERM atom_exalloc; +// static ERL_NIF_TERM atom_exbadstate; +// static ERL_NIF_TERM atom_exbusy; +// static ERL_NIF_TERM atom_exmon; +// static ERL_NIF_TERM atom_exself; +// static ERL_NIF_TERM atom_exsend; + +/* *** net *** */ +static ErlNifResourceType* net; +/* Maybe all of these whould be NULL? */ +static ErlNifResourceTypeInit netInit = { + NULL, // net_dtor, + NULL, // net_stop, + NULL // (ErlNifResourceDown*) net_down +}; + + + +/* ---------------------------------------------------------------------- + * N I F F u n c t i o n s + * ---------------------------------------------------------------------- + * + * Utility and admin functions: + * ---------------------------- + * nif_is_loaded/0 + * nif_info/0 + * + * The "proper" net functions: + * ------------------------------ + * nif_getnameinfo/2 + * nif_getaddrinfo/3 + * nif_if_name2index/1 + * nif_if_index2name/1 + * nif_if_names/0 + * + */ + + +/* ---------------------------------------------------------------------- + * nif_is_loaded + * + * Description: + * This functions only purpose is to return the atom 'true'. + * This will happen *if* the (socket) nif library is loaded. + * If its not, the erlang (nif_is_loaded) will instead return + * 'false'. + */ +static +ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 0) + return enif_make_badarg(env); + + return atom_true; +} + + +/* ---------------------------------------------------------------------- + * nif_info + * + * Description: + * This is currently just a placeholder... + */ +static +ERL_NIF_TERM nif_info(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM info = enif_make_new_map(env); + return info; +} + + + +/* ---------------------------------------------------------------------- + * nif_getnameinfo + * + * Description: + * Address-to-name translation in protocol-independent manner. + * + * Arguments: + * SockAddr - Socket Address (address and port) + * Flags - The flags argument modifies the behavior of getnameinfo(). + * Not used! + */ + +static +ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM eSockAddr; + unsigned int eFlags; + int flags = 0; // Just in case... + SockAddress sa; + socklen_t saLen = 0; // Just in case... + + if ((argc != 2) || + !GET_UINT(env, argv[1], &eFlags)) { + return enif_make_badarg(env); + } + eSockAddr = argv[0]; + + if (!decode_nameinfo_flags(env, eFlags, &flags)) + return enif_make_badarg(env); + + if (decode_in_sockaddr(env, eSockAddr, &sa, &saLen)) + return enif_make_badarg(env); + + return ngetnameinfo(env, &sa, saLen, flags); +} + + + +/* Given the provided sock(et) address (and honts), retreive the host and + * service info. + */ +static +ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, + const SockAddress* saP, + socklen_t saLen, + int flags) +{ + ERL_NIF_TERM result; + char host[HOSTNAME_LEN]; + socklen_t hostLen = sizeof(host); + char serv[SERVICE_LEN]; + socklen_t servLen = sizeof(serv); + + int res = getnameinfo((struct sockaddr*) saP, saLen, + host, hostLen, + serv, servLen, + flags); + + switch (res) { + case 0: + { + ERL_NIF_TERM info = MKT3(env, + atom_name_info, + MKS(env, host), + MKS(env, serv)); + result = make_ok2(env, info); + } + break; + + case EAI_AGAIN: + result = make_error(env, atom_eagain); + break; + + case EAI_BADFLAGS: + result = make_error(env, atom_ebadflags); + break; + + case EAI_FAIL: + result = make_error(env, atom_efail); + break; + + case EAI_FAMILY: + result = make_error(env, atom_efamily); + break; + + case EAI_MEMORY: + result = make_error(env, atom_emem); + break; + + case EAI_NONAME: + result = make_error(env, atom_enoname); + break; + + case EAI_OVERFLOW: + result = make_error(env, atom_eoverflow); + break; + + case EAI_SYSTEM: + result = make_error2(env, get_errno()); + break; + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + + +/* ---------------------------------------------------------------------- + * nif_getaddrinfo + * + * Description: + * Network address and service translation. + * + * Arguments: + * Host - Host name (either a string or the atom undefined) + * Service - Service name (either a string or the atom undefined) + * Hints - Hints for the lookup (address info record) (currently *ignored*) + */ + +static +ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM result; + char* hostName; + char* servName; + // struct addrinfo* hints; + + if (argc != 3) { + return enif_make_badarg(env); + } + eHostName = argv[0]; + eServName = argv[1]; + eHints = argv[2]; + + if (!decode_addrinfo_string(env, eHostName, &hostName)) + return enif_make_badarg(env); + + if (!decode_addrinfo_string(env, eServName, &servName)) + return enif_make_badarg(env); + + /* + if (decode_addrinfo_hints(env, eHints, &hints)) + return enif_make_badarg(env); + */ + + if ((hostName == NULL) && (servName == NULL)) + return enif_make_badarg(env); + + result = ngetaddrinfo(env, hostName, servName); + + if (hostName != NULL) + FREE(hostName); + + if (servName != NULL) + FREE(servName); + + /* + if (hints != NULL) + FREE(hints); + */ + + return result; +} + + +static +ERL_NIF_TERM nifgetaddrinfo(ErlNifEnv* env, + char* host, + char* serv) +{ + struct addrinfo* addrInfoP; + int res; + + res = getaddrinfo(host, serv, NULL, &addrInfoP); + + switch (res) { + case 0: + { + ERL_NIF_TERM addrInfo = encode_address_info(env, addrInfoP); + freeaddrinfo(addrInfoP); + result = make_ok2(env, addrInfo); + } + break; + + case EAI_ADDRFAMILY: + result = make_error(env, atom_eaddrfamily); + break; + + case EAI_AGAIN: + result = make_error(env, atom_eagain); + break; + + case EAI_BADFLAGS: + result = make_error(env, atom_ebadflags); + break; + + case EAI_FAIL: + result = make_error(env, atom_efail); + break; + + case EAI_FAMILY: + result = make_error(env, atom_efamily); + break; + + case EAI_MEMORY: + result = make_error(env, atom_emem); + break; + + case EAI_NODATA: + result = make_error(env, atom_enodata); + break; + + case EAI_NONAME: + result = make_error(env, atom_enoname); + break; + + case EAI_SERCVICE: + result = make_error(env, atom_eservice); + break; + + case EAI_SOCKTYPE: + result = make_error(env, atom_esocktype); + break; + + case EAI_SYSTEM: + result = make_error(env, atom_esystem); + break; + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ---------------------------------------------------------------------- + * nif_if_name2index + * + * Description: + * Perform a Interface Name to Interface Index translation. + * + * Arguments: + * Ifn - Interface name to be translated. + */ + +static +ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM eifn; + char ifn[IF_NAMESIZE+1]; + + if (argc != 1) { + return enif_make_badarg(env); + } + eifn = argv[0]; + + if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) + return make_error2(env, atom_einval); + + return nif_name2index(env, ifn); +} + + + +static +ERL_NIF_TERM nif_name2index(ErlNifEnv* env, + char* ifn) +{ + unsigned int idx = if_nametoindex(ifn); + + if (idx == 0) + return make_error2(env, get_errno()); + else + return make_ok2(env, idx); + +} + + + +/* ---------------------------------------------------------------------- + * nif_if_index2name + * + * Description: + * Perform a Interface Index to Interface Name translation. + * + * Arguments: + * Idx - Interface index to be translated. + */ + +static +ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + unsigned int idx; + + if ((argc != 1) || + !GET_UINT(env, argv[0], &idx)) { + return enif_make_badarg(env); + } + + return nif_index2name(env, idx); +} + + + +static +ERL_NIF_TERM nif_index2name(ErlNifEnv* env, + unsigned int idx) +{ + ERL_NIF_TERM result; + char* ifn = MALLOC(IF_NAMESIZE+1); + + if (ifn == NULL) + return enif_make_badarg(env); // PLACEHOLDER + + if (NULL == if_indextoname(idx, ifn)) { + result = make_ok2(env, MKS(env, ifn)); + } else { + result = make_error2(env, get_errno()); + } + + FREE(ifn); + + return result; +} + + + +/* ---------------------------------------------------------------------- + * nif_if_names + * + * Description: + * Get network interface names and indexes. + * + */ + +static +ERL_NIF_TERM nif_if_names(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 0) { + return enif_make_badarg(env); + } + + return nif_names(env); +} + + + +static +ERL_NIF_TERM nif_names(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + struct if_nameindex* ifs = if_nameindex(); + + if (ifs == NULL) { + result = make_error2(env, get_errno()); + } else { + /* + * We got some interfaces: + * 1) Calculate how many - the only way is to iterate through the list + * until its end (which is indicated by an entry with index = zero + * and if_name = NULL). + * 2) Allocate an ERL_NIF_TERM array of the calculated length. + * 3) Iterate through the array of interfaces and for each create + * a two tuple: {Idx, If} + * + * Or shall we instead build a list in reverse order and then when + * its done, reverse that? Check + */ + unsigned int len = nif_names_length(ifs); + + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i; + + for (i = 0; i < len; i++) { + array[i] = MKT2(env, + MKI(env, ifs[i].if_index), + MKS(env, ifs[i].if_name)); + } + + result = make_ok2(env, MKLA(env, array, len)); + FREE(array); + } else { + result = make_ok2(env, enif_make_list(env, 0)); + } + } + + if (ifs != NULL) + if_freenameindex(ifs); + + return result; +} + + +static +unsigned int nif_names_length(struct if_nameindex* p) +{ + unsigned int len = 0; + + while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { + len++; + } + + return len; +} + + + +/* ---------------------------------------------------------------------- + * U t i l i t y F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* Decode an in_sockaddr (the socket address). + * This is the (erlang) type: in_sockaddr(), which is either + * a in4_sockaddr() (tuple of size 3) or a in6_sockaddr() + * (tuple of size 5). See the net erlang module for details. + * + * We first detect which which of the tuples it is: + * - Size 3: maybe in4_sockaddr + * - Size 5: maybe in6_sockaddr + */ +static +BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM eAddr, + SockAddress* saP, + socklen_t* saLen) +{ + const ERL_NIF_TERM* addrt; + int addrtSz; + + if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) + return FALSE; + + switch (addrtSz) { + case 3: + return decode_in4_sockaddr(env, addrt, saP, saLen); + break; + +#ifdef HAVE_IN6 + case 5: + return decode_in6_sockaddr(env, addrt, saP, saLen); + break; +#endif + + default: + return FALSE; + break; + } + +} + + +/* Decode an in4_sockaddr record. This is a tuple of size 3. + * The size has already been verified, but not its content. + * So, the first element should be the atom 'in4_sockaddr'. + * The second the port number, an integer. And the third, + * the actual ip address, a 4-tuple. + */ +static +BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* addrt, + SockAddress* saP, + socklen_t* saLen) +{ + unsigned int len; + char tag[16]; // Just in case... + int port; + int ipAddrSz; + const ERL_NIF_TERM* ipAddrT; + int a, v; + char addr[4]; + + /* The first element: Verify record tag (atom()): in4_sockaddr */ + + if (!(GET_ATOM_LEN(env, addrt[0], &len) && + (len > 0) && + (len <= (sizeof(tag))))) + return FALSE; + + if (!GET_ATOM(env, addrt[0], tag, sizeof(tag))) + return FALSE; + + if (strncmp(tag, "in4_sockaddr", len) != 0) + return FALSE; + + + /* Get second element: port number (integer) */ + + if (!GET_INT(env, addrt[1], &port)) + return FALSE; + + + /* And finally, get the third element, the ip address (a 4 tuple) */ + + if (!GET_TUPLE(env, addrt[2], &ipAddrSz, &ipAddrT)) + return FALSE; + + if (ipAddrSz != 4) + return FALSE; + + + /* And finally initialize the sockaddr structure (and size) */ + sys_memzero((char*)saP, sizeof(struct sockaddr_in)); + saP->in.sin_family = AF_INET; + saP->in.sin_port = htons(port); + for (a = 0; a < 4; a++) { + if (!GET_INT(env, ipAddrT[a], &v)) + return FALSE; + addr[a] = v; + } + sys_memcpy(&saP->in.sin_addr, &addr, sizeof(addr)); + *saLen = sizeof(struct sockaddr_in); + return TRUE; +} + + + +#if defined(HAVE_IN6) && defined(AF_INET6) +/* Decode an in6_sockaddr record. This is a tuple of size 5. + * The size has already been verified, but not its content. + * So, the first element should be the atom 'in6_sockaddr'. + * The second the port number, an integer. The third, the + * actual ip address, a 8-tuple. The forth, the flowinfo, + * an integer. And finally, the fifth element, the scope_id, + * also an integer. *Not used here*. + */ +static +BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* addrt, + SockAddress* saP, + socklen_t* saLen) +{ + unsigned int len; + char tag[16]; // Just in case... + int port; + int ipAddrSz; + const ERL_NIF_TERM* ipAddrT; + int flowInfo; + int a, v; + char addr[16]; + + + /* The first element: Verify record tag (atom()): in6_sockaddr */ + + if (!(GET_ATOM_LEN(env, addrt[0], &len) && + (len > 0) && + (len <= (sizeof(tag))))) + return FALSE; + + if (!GET_ATOM(env, addrt[0], tag, sizeof(tag))) + return FALSE; + + if (strncmp(tag, "in6_sockaddr", len) != 0) + return FALSE; + + + /* Get second element: port number (integer) */ + + if (!GET_INT(env, addrt[1], &port)) + return FALSE; + + + /* Get the third element, the ip address (a 8 tuple) */ + + if (!GET_TUPLE(env, addrt[2], &ipAddrSz, &ipAddrT)) + return FALSE; + + if (ipAddrSz != 8) + return FALSE; + + + /* And finally, get the forth element, the flowinfo (integer) */ + + if (!GET_INT(env, addrt[3], &flowInfo)) + return FALSE; + + + /* And finally initialize the sockaddr structure (and size) */ + + sys_memzero((char*)saP, sizeof(struct sockaddr_in6)); + saP->in6.sin6_family = AF_INET6; + saP->in6.sin6_port = htons(port); + saP->in6.sin6_flowinfo = flowInfo; + /* 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, addrt[a], &v)) + return FALSE; + addr[a*2 ] = ((v >> 8) & 0xFF); + addr[a*2+1] = (v & 0xFF); + } + sys_memcpy(&saP->in6.sin6_addr, &addr, sizeof(addr)); + *saLen = sizeof(struct sockaddr_in6); + + return TRUE; +} +#endif + + + +/* The erlang format for a set of flags is a list of atoms. + * A special case is when there is no flags, which is + * represented by the atom undefined. + */ +static +BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, + const ERL_NIF_TERM eflags, + int* flags) +{ + BOOLEAN_T result; + + if (IS_ATOM(env, eflags)) { + if (COMPARE(eflags, atom_undefined) == 0) { + *flags = 0; + result = TRUE; + } else { + result = FALSE; + } + } else if (IS_LIST(env, eflags)) { + result = decode_nameinfo_flags_list(env, eflags, flags); + } else { + result = FALSE; + } + + return result; +} + + + +static +BOOLEAN_T decode_nameinfo_flags_list(ErlNifEnv* env, + const ERL_NIF_TERM eflags, + int* flags) +{ + ERL_NIF_TERM elem, tail, list = eflags; + int tmp = 0; + BOOLEAN_T done = FALSE; + + while (!done) { + if (GET_LIST_ELEM(env, list, &elem, &tail)) { + if (COMPARE(elem, atom_namereqd) == 0) { + tmp |= NI_NAMEREQD; + } else if (COMPARE(elem, atom_dgram) == 0) { + tmp |= NI_DGRAM; + } else if (COMPARE(elem, atom_nofqdn) == 0) { + tmp |= NI_NOFQDN; + } else if (COMPARE(elem, atom_numerichost) == 0) { + tmp |= NI_NUMERICHOST; + } else if (COMPARE(elem, atom_numericserv) == 0) { + tmp |= NI_NUMERICSERV; + + /* Starting with glibc 2.3.4: */ + +#if defined(NI_IDN) + } else if (COMPARE(elem, atom_idn) == 0) { + tmp |= NI_IDN; +#endif + +#if defined(NI_IDN_ALLOW_UNASSIGNED) + } else if (COMPARE(elem, atom_idna_allow_unassigned) == 0) { + tmp |= NI_IDN_ALLOW_UNASSIGNED; +#endif + +#if defined(NI_IDN_USE_STD3_ASCII_RULES) + } else if (COMPARE(elem, atom_idna_use_std3_ascii_rules) == 0) { + tmp |= NI_IDN_USE_STD3_ASCII_RULES; +#endif + + } else { + return FALSE; + } + + list = tail; + + } else { + done = TRUE; + } + } + + *flags = tmp; + + return TRUE; +} + + + +/* Decode the address info string (hostname or service name) + * The string is either the atom undefined or an actual string. + */ +static +BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, + const ERL_NIF_TERM eString, + char** stringP) +{ + BOOLEAN_T result; + + if (IS_ATOM(env, eString)) { + if (COMPARE(eString, atom_undefined) == 0) { + *stringP = NULL; + result = TRUE; + } else { + *stringP = NULL; + result = FALSE; + } + } else { + unsigned int len; + char* bufP; + + if (!GET_LIST_LEN(env, eString, &len) && (len != 0)) { + *stringP = NULL; + result = FALSE; + } + + bufP = ALLOC(len); + + if (GET_STR(env, eString, bufP, len)) { + *stringP = bufP; + result = TRUE; + } else { + *stringP = NULL; + result = FALSE; + } + } + + return result; + +} + + + +/* Encode the address info + * The address info is a linked list och address info, which + * will result in the result being a list of zero or more length. + */ +static +ERL_NIF_TERM encode_address_info(ErlNifEnv* env, + struct addrinfo* addrInfo) +{ + ERL_NIF_TERM result; + unsigned int len = address_info_length(addrInfo); + + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i; + + for (i = 0; i < len; i++) { + array[i] = make_address_info(env, &addrInfo[i]); + } + + result = mkake_ok2(env, MKLA(env, array, len)); + } else { + result = MKEL(env); + } + + return result; +} + + + +/* Calculate the length of the adress info linked list + * The list is NULL-terminated, so the only way is to + * iterate through the list until we find next = NULL. + */ +static +unsigned int address_info_length(struct addrinfo* addrInfoP) +{ + unsigned int len; + struct addrinfo* tmp = addrInfoP; + + if (tmp != NULL) { + len = 1; + while (tmp->ai_next != NULL) { + tmp = tmp->ai_next; + len++; + } + } else { + len = 0; + } + + return len; +} + + + +/* Create one (erlang) instance of the address info record + * Should we have address info as a record or as a map? + * + * {address_info, Fam, Type, Proto, Addr} + */ +static +ERL_NIF_TERM make_adress_info(ErlNifEnv* env, + struct addrinfo* addrInfoP) +{ + ERL_NIF_TERM Fam = make_addrinfo_family(env, addrInfoP->ai_family); + ERL_NIF_TERM Type = make_addrinfo_type(env, addrInfoP->ai_socktype); + ERL_NIF_TERM Proto = make_addrinfo_proto(env, addrInfoP->ai_protocol); + ERL_NIF_TERM Addr = make_addrinfo_addr(env, + addrInfoP->ai_addr, + addrInfoP->ai_addrlen); + + return MKT5(env, atom_address_info, Fam, Type, Proto, Addr); + +} + + +/* Convert an "native" family to an erlang family + * Note that this is not currently exhaustive, but only supports + * inet and inet6. Other values will be returned as is, that is + * in the form of an integer. + */ +static +ERL_NIF_TERM make_addrinfo_family(ErlNifEnv* env, + int family) +{ + ERL_NIF_TERM efam; + + switch (family) { + case AF_INET: + efam = atom_inet; + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + efam = atom_inet6; + break; +#endif + + default: + efam = MKI(env, family); + break; + } + + return efam; +} + + + +/* Convert an "native" socket type to an erlang socket type + * Note that this is not currently exhaustive, but only supports + * stream and dgram. Other values will be returned as is, that is + * in the form of an integer. + */ +static +ERL_NIF_TERM make_addrinfo_type(ErlNifEnv* env, + int socktype) +{ + ERL_NIF_TERM etype; + + switch (socktype) { + case SOCK_STREAM: + etype = atom_stream; + break; + + case SOCK_DGRAM: + etype = atom_dgram; + break; + + default: + etype = MKI(env, socktype); + break; + } + + return etype; +} + + + +/* Convert an "native" protocol to an erlang protocol + * Note that this is not currently exhaustive, but only supports + * tcp and udp. Other values will be returned as is, that is + * in the form of an integer. + */ +static +ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env, + int proto) +{ + ERL_NIF_TERM eproto; + + switch (proto) { +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + eproto = atom_ip; + break; + + case IPPROTO_TCP: + eproto = atom_tcp; + break; + + case IPPROTO_UDP: + eproto = atom_udp; + break; + + default: + eproto = MKI(env, proto); + break; + } + + return eproto; +} + + + +/* Convert an "native" address to an erlang address + * Note that this is not currently exhaustive, but only supports + * IPv4 and IPv6 addresses. Values of other families will be + * returned as an undefined. + */ +static +ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, + struct sockaddr* addrP, + socklen_t addrLen) +{ + ERL_NIF_TERM port, addr, eaddr; + SockAddress* p = (SockAddress*) addrP; + + switch (addrP->sa_family) { + case AF_INET: + port = ntohs(p->in.sin_port); + addr = MKT4(env, + MKI(env, p->in.sin_addr[0]), + MKI(env, p->in.sin_addr[1]), + MKI(env, p->in.sin_addr[2]), + MKI(env, p->in.sin_addr[3])); + eaddr = MKT2(env, port, addr); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + port = ntohs(p->in6.sin6_port); + addr = MKT8(env, + MKI(env, p->in6.sin_addr[0]), + MKI(env, p->in6.sin_addr[1]), + MKI(env, p->in6.sin_addr[2]), + MKI(env, p->in6.sin_addr[3]), + MKI(env, p->in6.sin_addr[3]), + MKI(env, p->in6.sin_addr[3]), + MKI(env, p->in6.sin_addr[3]), + MKI(env, p->in6.sin_addr[3])); + eaddr = MKT2(env, port, addr); + break; +#endif + + default: + eaddr = atom_undefined; + break; + } + + return eaddr; +} + + + +/* Create an ok two (2) tuple in the form: {ok, Any}. + * The second element (Any) is already in the form of an + * ERL_NIF_TERM so all we have to do is create the tuple. + */ +static +ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM any) +{ + return MKT2(env, atom_ok, any); +} + + +/* Create an ok three (3) tuple in the form: {ok, Val1, Val2}. + * The second (Val1) and third (Val2) elements are already in + * the form of an ERL_NIF_TERM so all we have to do is create + * the tuple. + */ +/* +static +ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2) +{ + return MKT3(env, atom_ok, val1, val2); +} +*/ + + +/* Create an error two (2) tuple in the form: {error, Reason}. + * The second element (Reason) is already in the form of an + * ERL_NIF_TERM so all we have to do is create the tuple. + */ +static +ERL_NIF_TERM make_error(ErlNifEnv* env, ERL_NIF_TERM reason) +{ + return MKT2(env, atom_error, reason); +} + + +/* Create an error two (2) tuple in the form: {error, Reason}. + * The second element, Reason, is a string to be converted into + * an atom. + */ +static +ERL_NIF_TERM make_error1(ErlNifEnv* env, char* reason) +{ + return make_error(env, MKA(env, reason)); +} + + +/* Create an error two (2) tuple in the form: {error, Reason}. + * The second element, Reason, is the errno value in its + * basic form (integer) which will (eventually) be converted + * into an atom. + */ +static +ERL_NIF_TERM make_error2(ErlNifEnv* env, int err) +{ + return make_error1(env, erl_errno_id(err)); +} + + +/* +static +void xabort(const char* expr, + const char* func, + const char* file, + int line) +{ + fflush(stdout); + fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", + file, line, func, expr); + fflush(stderr); + abort(); +} +*/ + + +/* ---------------------------------------------------------------------- + * C o u n t e r F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* ---------------------------------------------------------------------- + * C a l l b a c k F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* ========================================================================= + * net_dtor - Callback function for resource destructor + * + */ +/* +static +void net_dtor(ErlNifEnv* env, void* obj) +{ +} +*/ + + +/* ========================================================================= + * net_stop - Callback function for resource stop + * + */ +/* +static +void net_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) +{ +} +*/ + + + + +/* ========================================================================= + * net_down - Callback function for resource down (monitored processes) + * + */ +/* +static +void net_down(ErlNifEnv* env, + void* obj, + const ErlNifPid* pid, + const ErlNifMonitor* mon) +{ +} +*/ + + + +/* ---------------------------------------------------------------------- + * L o a d / u n l o a d / u p g r a d e F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +static +ErlNifFunc net_funcs[] = +{ + // Some utility functions + {"nif_is_loaded", 0, nif_is_loaded, 0}, + {"nif_info", 0, nif_info, 0}, + + /* address and name translation in protocol-independent manner */ + {"nif_getnameinfo", 2, nif_getnameinfo, 0}, + + /* Network interface (name and/or index) functions */ + {"nif_if_name2index", 1, nif_if_name2index, 0}, + {"nif_if_index2name", 1, nif_if_index2name, 0}, + {"nif_if_names", 0, nif_if_names, 0} +}; + + + +/* ======================================================================= + * load_info - A map of misc info (e.g global debug) + */ + +static +int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + /* +++ Misc atoms +++ */ + atom_dgram = MKA(env, str_dgram); + atom_error = MKA(env, str_error); + atom_idn = MKA(env, str_idn); + atom_idna_allow_unassigned = MKA(env, str_idna_allow_unassigned); + atom_idna_use_std3_ascii_rules = MKA(env, str_idna_use_std3_ascii_rules); + atom_namereqd = MKA(env, str_namereqd); + atom_name_info = MKA(env, str_name_info); + atom_nofqdn = MKA(env, str_nofqdn); + atom_numerichost = MKA(env, str_numerichost); + atom_numericserv = MKA(env, str_numericserv); + atom_ok = MKA(env, str_ok); + atom_true = MKA(env, str_true); + atom_undefined = MKA(env, str_undefined); + // atom_version = MKA(env, str_version); + + // atom_lowdelay = MKA(env, str_lowdelay); + // atom_throughput = MKA(env, str_throughput); + // atom_reliability = MKA(env, str_reliability); + // atom_mincost = MKA(env, str_mincost); + + /* Error codes */ + // atom_eafnosupport = MKA(env, str_eafnosupport); + atom_eagain = MKA(env, str_eagain); + atom_ebadflags = MKA(env, str_ebadflags); + atom_efail = MKA(env, str_efail); + atom_efamily = MKA(env, str_efamily); + atom_einval = MKA(env, str_einval); + // atom_eisconn = MKA(env, str_eisconn); + atom_emem = MKA(env, str_emem); + atom_enoname = MKA(env, str_enoname); + // atom_enotclosing = MKA(env, str_enotclosing); + // atom_enotconn = MKA(env, str_enotconn); + atom_eoverflow = MKA(env, str_eoverflow); + // atom_exalloc = MKA(env, str_exalloc); + // atom_exbadstate = MKA(env, str_exbadstate); + // atom_exbusy = MKA(env, str_exbusy); + // atom_exnotopen = MKA(env, str_exnotopen); + // atom_exmon = MKA(env, str_exmon); + // atom_exself = MKA(env, str_exself); + // atom_exsend = MKA(env, str_exsend); + + // For storing "global" things... + // socketData.env = enif_alloc_env(); // We should really check + // socketData.version = MKA(env, ERTS_VERSION); + // socketData.buildDate = MKA(env, ERTS_BUILD_DATE); + + net = enif_open_resource_type_x(env, + "net", + &netInit, + ERL_NIF_RT_CREATE, + NULL); + + return !net; +} + +ERL_NIF_INIT(net, net_funcs, on_load, NULL, NULL, NULL) -- cgit v1.2.3 From 5b7ac9423f07171e4cd682a216cc86b1d32c660a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 8 Jun 2018 15:25:51 +0200 Subject: [net-nif] The net-module now actually loads The net (nif) module now actually loads (automatically) when the VM is started (*on linux*). Now we must make sure it *actually* works, and implement the rest of the stuff... --- erts/emulator/nifs/common/net_nif.c | 149 ++++++++++++++++++++++++++---------- 1 file changed, 108 insertions(+), 41 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index b8fa6628a7..edcf944c66 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -24,6 +24,8 @@ * */ +#define STATIC_ERLANG_NIF 1 + /* #include */ /* #include */ /* #include */ @@ -262,6 +264,8 @@ typedef unsigned long long llu_t; #define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) #define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) #define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) +#define MKT5(E,E1,E2,E3,E4,E5) \ + enif_make_tuple5((E), (E1), (E2), (E3), (E4), (E5)) #define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) @@ -364,8 +368,11 @@ static ERL_NIF_TERM nif_if_names(ErlNifEnv* env, static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, const SockAddress* saP, - socklen_t saLen, + SOCKLEN_T saLen, int flags); +static ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, + char* host, + char* serv); static ERL_NIF_TERM nif_name2index(ErlNifEnv* env, char* ifn); static ERL_NIF_TERM nif_index2name(ErlNifEnv* env, @@ -387,16 +394,16 @@ static void net_down(ErlNifEnv* env, static BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, const ERL_NIF_TERM eAddr, SockAddress* saP, - socklen_t* saLen); + SOCKLEN_T* saLen); static BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, const ERL_NIF_TERM* addrt, SockAddress* saP, - socklen_t* saLen); + SOCKLEN_T* saLen); #if defined(HAVE_IN6) && defined(AF_INET6) static BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, const ERL_NIF_TERM* addrt, SockAddress* saP, - socklen_t* saLen); + SOCKLEN_T* saLen); #endif static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, const ERL_NIF_TERM eflags, @@ -404,10 +411,26 @@ static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, static BOOLEAN_T decode_nameinfo_flags_list(ErlNifEnv* env, const ERL_NIF_TERM eflags, int* flags); +static +BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, + const ERL_NIF_TERM eString, + char** stringP); static ERL_NIF_TERM encode_address_info(ErlNifEnv* env, struct addrinfo* addrInfo); static unsigned int address_info_length(struct addrinfo* addrInfoP); +static ERL_NIF_TERM make_address_info(ErlNifEnv* env, + struct addrinfo* addrInfoP); +static ERL_NIF_TERM make_addrinfo_family(ErlNifEnv* env, + int family); +static ERL_NIF_TERM make_addrinfo_type(ErlNifEnv* env, + int socktype); +static ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env, + int proto); +static ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, + struct sockaddr* addrP, + SOCKLEN_T addrLen); + static ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM val); // static ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2); static ERL_NIF_TERM make_error(ErlNifEnv* env, ERL_NIF_TERM reason); @@ -448,18 +471,25 @@ static const struct in6_addr in6addr_loopback = /* *** String constants *** */ +static char str_address_info[] = "address_info"; static char str_dgram[] = "dgram"; static char str_error[] = "error"; static char str_idn[] = "idn"; static char str_idna_allow_unassigned[] = "idna_allow_unassigned"; static char str_idna_use_std3_ascii_rules[] = "idna_use_std3_ascii_rules"; +static char str_inet[] = "inet"; +static char str_inet6[] = "inet6"; +static char str_ip[] = "ip"; static char str_namereqd[] = "namereqd"; static char str_name_info[] = "name_info"; static char str_nofqdn[] = "nofqdn"; static char str_numerichost[] = "numerichost"; static char str_numericserv[] = "numericserv"; static char str_ok[] = "ok"; +static char str_stream[] = "stream"; +static char str_tcp[] = "tcp"; static char str_true[] = "true"; +static char str_udp[] = "udp"; static char str_undefined[] = "undefined"; // static char str_lowdelay[] = "lowdelay"; @@ -469,6 +499,7 @@ static char str_undefined[] = "undefined"; /* (special) error string constants */ // static char str_eafnosupport[] = "eafnosupport"; +static char str_eaddrfamily[] = "eaddrfamily"; static char str_eagain[] = "eagain"; static char str_ebadflags[] = "ebadflags"; static char str_efail[] = "efail"; @@ -476,10 +507,14 @@ static char str_efamily[] = "efamily"; static char str_einval[] = "einval"; // static char str_eisconn[] = "eisconn"; static char str_emem[] = "emem"; +static char str_enodata[] = "enodata"; static char str_enoname[] = "enoname"; // static char str_enotclosing[] = "enotclosing"; // static char str_enotconn[] = "enotconn"; static char str_eoverflow[] = "eoverflow"; +static char str_eservice[] = "eservice"; +static char str_esocktype[] = "esocktype"; +static char str_esystem[] = "esystem"; // static char str_exalloc[] = "exalloc"; // static char str_exbadstate[] = "exbadstate"; // static char str_exbusy[] = "exbusy"; @@ -490,11 +525,15 @@ static char str_eoverflow[] = "eoverflow"; /* *** Atoms *** */ -static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_address_info; static ERL_NIF_TERM atom_dgram; +static ERL_NIF_TERM atom_error; static ERL_NIF_TERM atom_idn; static ERL_NIF_TERM atom_idna_allow_unassigned; static ERL_NIF_TERM atom_idna_use_std3_ascii_rules; +static ERL_NIF_TERM atom_inet; +static ERL_NIF_TERM atom_inet6; +static ERL_NIF_TERM atom_ip; static ERL_NIF_TERM atom_namereqd; static ERL_NIF_TERM atom_name_info; static ERL_NIF_TERM atom_nofqdn; @@ -502,8 +541,11 @@ static ERL_NIF_TERM atom_numerichost; static ERL_NIF_TERM atom_numericserv; static ERL_NIF_TERM atom_ok; // static ERL_NIF_TERM atom_select; +static ERL_NIF_TERM atom_stream; // static ERL_NIF_TERM atom_timeout; +static ERL_NIF_TERM atom_tcp; static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_udp; static ERL_NIF_TERM atom_undefined; // static ERL_NIF_TERM atom_lowdelay; @@ -512,6 +554,7 @@ static ERL_NIF_TERM atom_undefined; // static ERL_NIF_TERM atom_mincost; // static ERL_NIF_TERM atom_eafnosupport; +static ERL_NIF_TERM atom_eaddrfamily; static ERL_NIF_TERM atom_eagain; static ERL_NIF_TERM atom_ebadflags; static ERL_NIF_TERM atom_efail; @@ -519,10 +562,14 @@ static ERL_NIF_TERM atom_efamily; static ERL_NIF_TERM atom_einval; // static ERL_NIF_TERM atom_eisconn; static ERL_NIF_TERM atom_emem; +static ERL_NIF_TERM atom_enodata; static ERL_NIF_TERM atom_enoname; // static ERL_NIF_TERM atom_enotclosing; // static ERL_NIF_TERM atom_enotconn; static ERL_NIF_TERM atom_eoverflow; +static ERL_NIF_TERM atom_eservice; +static ERL_NIF_TERM atom_esocktype; +static ERL_NIF_TERM atom_esystem; // static ERL_NIF_TERM atom_exalloc; // static ERL_NIF_TERM atom_exbadstate; // static ERL_NIF_TERM atom_exbusy; @@ -620,7 +667,7 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, unsigned int eFlags; int flags = 0; // Just in case... SockAddress sa; - socklen_t saLen = 0; // Just in case... + SOCKLEN_T saLen = 0; // Just in case... if ((argc != 2) || !GET_UINT(env, argv[1], &eFlags)) { @@ -645,14 +692,14 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, const SockAddress* saP, - socklen_t saLen, + SOCKLEN_T saLen, int flags) { ERL_NIF_TERM result; char host[HOSTNAME_LEN]; - socklen_t hostLen = sizeof(host); + SOCKLEN_T hostLen = sizeof(host); char serv[SERVICE_LEN]; - socklen_t servLen = sizeof(serv); + SOCKLEN_T servLen = sizeof(serv); int res = getnameinfo((struct sockaddr*) saP, saLen, host, hostLen, @@ -729,7 +776,7 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM result; + ERL_NIF_TERM result, eHostName, eServName; //, eHints; char* hostName; char* servName; // struct addrinfo* hints; @@ -739,7 +786,7 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, } eHostName = argv[0]; eServName = argv[1]; - eHints = argv[2]; + // eHints = argv[2]; if (!decode_addrinfo_string(env, eHostName, &hostName)) return enif_make_badarg(env); @@ -773,10 +820,11 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, static -ERL_NIF_TERM nifgetaddrinfo(ErlNifEnv* env, - char* host, - char* serv) +ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, + char* host, + char* serv) { + ERL_NIF_TERM result; struct addrinfo* addrInfoP; int res; @@ -823,7 +871,7 @@ ERL_NIF_TERM nifgetaddrinfo(ErlNifEnv* env, result = make_error(env, atom_enoname); break; - case EAI_SERCVICE: + case EAI_SERVICE: result = make_error(env, atom_eservice); break; @@ -1041,7 +1089,7 @@ static BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, const ERL_NIF_TERM eAddr, SockAddress* saP, - socklen_t* saLen) + SOCKLEN_T* saLen) { const ERL_NIF_TERM* addrt; int addrtSz; @@ -1078,7 +1126,7 @@ static BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, const ERL_NIF_TERM* addrt, SockAddress* saP, - socklen_t* saLen) + SOCKLEN_T* saLen) { unsigned int len; char tag[16]; // Just in case... @@ -1146,7 +1194,7 @@ static BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, const ERL_NIF_TERM* addrt, SockAddress* saP, - socklen_t* saLen) + SOCKLEN_T* saLen) { unsigned int len; char tag[16]; // Just in case... @@ -1330,7 +1378,7 @@ BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, result = FALSE; } - bufP = ALLOC(len); + bufP = MALLOC(len); if (GET_STR(env, eString, bufP, len)) { *stringP = bufP; @@ -1366,7 +1414,7 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env, array[i] = make_address_info(env, &addrInfo[i]); } - result = mkake_ok2(env, MKLA(env, array, len)); + result = make_ok2(env, MKLA(env, array, len)); } else { result = MKEL(env); } @@ -1407,8 +1455,8 @@ unsigned int address_info_length(struct addrinfo* addrInfoP) * {address_info, Fam, Type, Proto, Addr} */ static -ERL_NIF_TERM make_adress_info(ErlNifEnv* env, - struct addrinfo* addrInfoP) +ERL_NIF_TERM make_address_info(ErlNifEnv* env, + struct addrinfo* addrInfoP) { ERL_NIF_TERM Fam = make_addrinfo_family(env, addrInfoP->ai_family); ERL_NIF_TERM Type = make_addrinfo_type(env, addrInfoP->ai_socktype); @@ -1530,35 +1578,41 @@ ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env, static ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, struct sockaddr* addrP, - socklen_t addrLen) + SOCKLEN_T addrLen) { ERL_NIF_TERM port, addr, eaddr; SockAddress* p = (SockAddress*) addrP; switch (addrP->sa_family) { case AF_INET: - port = ntohs(p->in.sin_port); - addr = MKT4(env, - MKI(env, p->in.sin_addr[0]), - MKI(env, p->in.sin_addr[1]), - MKI(env, p->in.sin_addr[2]), - MKI(env, p->in.sin_addr[3])); - eaddr = MKT2(env, port, addr); + { + unsigned char* a = (unsigned char*) &p->in.sin_addr; + port = ntohs(p->in.sin_port); + addr = MKT4(env, + MKI(env, a[0]), + MKI(env, a[1]), + MKI(env, a[2]), + MKI(env, a[3])); + eaddr = MKT2(env, port, addr); + } break; #if defined(HAVE_IN6) && defined(AF_INET6) case AF_INET6: - port = ntohs(p->in6.sin6_port); - addr = MKT8(env, - MKI(env, p->in6.sin_addr[0]), - MKI(env, p->in6.sin_addr[1]), - MKI(env, p->in6.sin_addr[2]), - MKI(env, p->in6.sin_addr[3]), - MKI(env, p->in6.sin_addr[3]), - MKI(env, p->in6.sin_addr[3]), - MKI(env, p->in6.sin_addr[3]), - MKI(env, p->in6.sin_addr[3])); - eaddr = MKT2(env, port, addr); + { + unsigned char* a = (unsigned char*) &p->in6.sin6_addr; + port = ntohs(p->in6.sin6_port); + addr = MKT8(env, + MKI(env, get_int16(a)), + MKI(env, get_int16(&a[ 2])), + MKI(env, get_int16(&a[ 4])), + MKI(env, get_int16(&a[ 6])), + MKI(env, get_int16(&a[ 8])), + MKI(env, get_int16(&a[10])), + MKI(env, get_int16(&a[12])), + MKI(env, get_int16(&a[14]))); + eaddr = MKT2(env, port, addr); + } break; #endif @@ -1713,6 +1767,7 @@ ErlNifFunc net_funcs[] = /* address and name translation in protocol-independent manner */ {"nif_getnameinfo", 2, nif_getnameinfo, 0}, + {"nif_getaddrinfo", 3, nif_getaddrinfo, 0}, /* Network interface (name and/or index) functions */ {"nif_if_name2index", 1, nif_if_name2index, 0}, @@ -1730,18 +1785,25 @@ static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { /* +++ Misc atoms +++ */ + atom_address_info = MKA(env, str_address_info); atom_dgram = MKA(env, str_dgram); atom_error = MKA(env, str_error); atom_idn = MKA(env, str_idn); atom_idna_allow_unassigned = MKA(env, str_idna_allow_unassigned); atom_idna_use_std3_ascii_rules = MKA(env, str_idna_use_std3_ascii_rules); + atom_inet = MKA(env, str_inet); + atom_inet6 = MKA(env, str_inet6); + atom_ip = MKA(env, str_ip); atom_namereqd = MKA(env, str_namereqd); atom_name_info = MKA(env, str_name_info); atom_nofqdn = MKA(env, str_nofqdn); atom_numerichost = MKA(env, str_numerichost); atom_numericserv = MKA(env, str_numericserv); atom_ok = MKA(env, str_ok); + atom_stream = MKA(env, str_stream); + atom_tcp = MKA(env, str_tcp); atom_true = MKA(env, str_true); + atom_udp = MKA(env, str_udp); atom_undefined = MKA(env, str_undefined); // atom_version = MKA(env, str_version); @@ -1752,6 +1814,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) /* Error codes */ // atom_eafnosupport = MKA(env, str_eafnosupport); + atom_eaddrfamily = MKA(env, str_eaddrfamily); atom_eagain = MKA(env, str_eagain); atom_ebadflags = MKA(env, str_ebadflags); atom_efail = MKA(env, str_efail); @@ -1759,10 +1822,14 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_einval = MKA(env, str_einval); // atom_eisconn = MKA(env, str_eisconn); atom_emem = MKA(env, str_emem); + atom_enodata = MKA(env, str_enodata); atom_enoname = MKA(env, str_enoname); // atom_enotclosing = MKA(env, str_enotclosing); // atom_enotconn = MKA(env, str_enotconn); atom_eoverflow = MKA(env, str_eoverflow); + atom_eservice = MKA(env, str_eservice); + atom_esocktype = MKA(env, str_esocktype); + atom_esystem = MKA(env, str_esystem); // atom_exalloc = MKA(env, str_exalloc); // atom_exbadstate = MKA(env, str_exbadstate); // atom_exbusy = MKA(env, str_exbusy); -- cgit v1.2.3 From cc08971508f3515f9f3b6c406ff0f6a186b24f6b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 11 Jun 2018 12:34:23 +0200 Subject: [net-nif] Added debugging Added a command function, which is currently only used to enable and disable debug, which is also added. --- erts/emulator/nifs/common/net_nif.c | 285 +++++++++++++++++++++++++++++++++++- 1 file changed, 278 insertions(+), 7 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index edcf944c66..134b02d178 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -63,6 +63,7 @@ #include #include #include +#include #ifdef HAVE_UNISTD_H #include @@ -317,6 +318,12 @@ typedef unsigned long long llu_t; # define SOCKLEN_T size_t #endif +#define NDEBUG( ___COND___ , proto ) \ + if ( ___COND___ ) { \ + dbg_printf proto; \ + fflush(stdout); \ + } +#define NDBG( proto ) NDEBUG( data.debug , proto ) /* The general purpose socket address */ typedef union { @@ -330,6 +337,12 @@ typedef union { } SockAddress; +typedef struct { + BOOLEAN_T debug; +} NetData; + + +static NetData data; /* ---------------------------------------------------------------------- @@ -347,6 +360,9 @@ static ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, static ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_command(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, int argc, @@ -366,6 +382,8 @@ static ERL_NIF_TERM nif_if_names(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM ncommand(ErlNifEnv* env, + ERL_NIF_TERM cmd); static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, const SockAddress* saP, SOCKLEN_T saLen, @@ -415,6 +433,9 @@ static BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, const ERL_NIF_TERM eString, char** stringP); +static ERL_NIF_TERM decode_bool(ErlNifEnv* env, + ERL_NIF_TERM eBool, + BOOLEAN_T* bool); static ERL_NIF_TERM encode_address_info(ErlNifEnv* env, struct addrinfo* addrInfo); static unsigned int address_info_length(struct addrinfo* addrInfoP); @@ -437,6 +458,10 @@ 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 void dbg_printf( const char* format, ... ); +static int dbg_realtime(struct timespec* tsP); +static int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts); + /* static void xabort(const char* expr, const char* func, @@ -472,8 +497,10 @@ static const struct in6_addr in6addr_loopback = /* *** String constants *** */ static char str_address_info[] = "address_info"; +static char str_debug[] = "debug"; static char str_dgram[] = "dgram"; static char str_error[] = "error"; +static char str_false[] = "false"; static char str_idn[] = "idn"; static char str_idna_allow_unassigned[] = "idna_allow_unassigned"; static char str_idna_use_std3_ascii_rules[] = "idna_use_std3_ascii_rules"; @@ -526,8 +553,10 @@ static char str_esystem[] = "esystem"; /* *** Atoms *** */ static ERL_NIF_TERM atom_address_info; +static ERL_NIF_TERM atom_debug; static ERL_NIF_TERM atom_dgram; static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_idn; static ERL_NIF_TERM atom_idna_allow_unassigned; static ERL_NIF_TERM atom_idna_use_std3_ascii_rules; @@ -640,12 +669,90 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM info = enif_make_new_map(env); + ERL_NIF_TERM info; + + NDBG( ("info -> entry\r\n") ); + + info = enif_make_new_map(env); + + NDBG( ("info -> done\r\n") ); + return info; } +/* ---------------------------------------------------------------------- + * nif_command + * + * Description: + * This is a general purpose utility function. + * + * Arguments: + * Command - This is a general purpose command, of any type. + * Currently, the only supported command is: + * + * {debug, boolean()} + */ +static +ERL_NIF_TERM nif_command(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM ecmd, result; + + NDBG( ("command -> entry (%d)\r\n", argc) ); + + if (argc != 1) + return enif_make_badarg(env); + + ecmd = argv[0]; + + NDBG( ("command -> ecmd: %T\r\n", ecmd) ); + + result = ncommand(env, ecmd); + + NDBG( ("command -> result: %T\r\n", result) ); + + return result; +} + + + +/* + * The command can, in principle, be anything, though currently we only + * support a debug command. + */ +static +ERL_NIF_TERM ncommand(ErlNifEnv* env, + ERL_NIF_TERM cmd) +{ + const ERL_NIF_TERM* t; + int tsz; + + if (IS_TUPLE(env, cmd)) { + /* Could be the debug tuple */ + if (!GET_TUPLE(env, cmd, &tsz, &t)) + return make_error(env, atom_einval); + + if (tsz != 2) + return make_error(env, atom_einval); + + /* First element should be the atom 'debug' */ + if (COMPARE(t[0], atom_debug) != 0) + return make_error(env, atom_einval); + + return decode_bool(env, t[1], &data.debug); + + } else { + return make_error(env, atom_einval); + } + +} + + + + /* ---------------------------------------------------------------------- * nif_getnameinfo * @@ -663,25 +770,36 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM eSockAddr; + ERL_NIF_TERM result, eSockAddr; unsigned int eFlags; int flags = 0; // Just in case... SockAddress sa; SOCKLEN_T saLen = 0; // Just in case... + NDBG( ("nif_getnameinfo -> entry (%d)\r\n", argc) ); + if ((argc != 2) || !GET_UINT(env, argv[1], &eFlags)) { return enif_make_badarg(env); } eSockAddr = argv[0]; + NDBG( ("nif_getnameinfo -> " + "\r\n SockAddr: %T" + "\r\n Flags: %T" + "\r\n", argv[0], argv[1]) ); + if (!decode_nameinfo_flags(env, eFlags, &flags)) return enif_make_badarg(env); if (decode_in_sockaddr(env, eSockAddr, &sa, &saLen)) return enif_make_badarg(env); - return ngetnameinfo(env, &sa, saLen, flags); + result = ngetnameinfo(env, &sa, saLen, flags); + + NDBG( ("nif_getnameinfo -> done when result: %T\r\n", result) ); + + return result; } @@ -781,6 +899,8 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, char* servName; // struct addrinfo* hints; + NDBG( ("nif_getaddrinfo -> entry (%d)\r\n", argc) ); + if (argc != 3) { return enif_make_badarg(env); } @@ -788,6 +908,12 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, eServName = argv[1]; // eHints = argv[2]; + NDBG( ("nif_getaddrinfo -> " + "\r\n Host: %T" + "\r\n Service: %T" + "\r\n Hints: %T" + "\r\n", argv[0], argv[1], argv[2]) ); + if (!decode_addrinfo_string(env, eHostName, &hostName)) return enif_make_badarg(env); @@ -815,6 +941,8 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, FREE(hints); */ + NDBG( ("nif_getaddrinfo -> done when result: %T\r\n", result) ); + return result; } @@ -907,18 +1035,28 @@ ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM eifn; + ERL_NIF_TERM eifn, result; char ifn[IF_NAMESIZE+1]; + NDBG( ("nif_if_name2index -> entry (%d)\r\n", argc) ); + if (argc != 1) { return enif_make_badarg(env); } eifn = argv[0]; + NDBG( ("nif_name2index -> " + "\r\n Ifn: %T" + "\r\n", argv[0]) ); + if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) return make_error2(env, atom_einval); - return nif_name2index(env, ifn); + result = nif_name2index(env, ifn); + + NDBG( ("nif_if_name2index -> done when result: %T\r\n", result) ); + + return result; } @@ -953,14 +1091,25 @@ ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ERL_NIF_TERM result; unsigned int idx; + NDBG( ("nif_if_index2name -> entry (%d)\r\n", argc) ); + if ((argc != 1) || !GET_UINT(env, argv[0], &idx)) { return enif_make_badarg(env); } - return nif_index2name(env, idx); + NDBG( ("nif_index2name -> " + "\r\n Idx: %T" + "\r\n", argv[0]) ); + + result = nif_index2name(env, idx); + + NDBG( ("nif_if_index2name -> done when result: %T\r\n", result) ); + + return result; } @@ -1001,11 +1150,19 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { + ERL_NIF_TERM result; + + NDBG( ("nif_if_names -> entry (%d)\r\n", argc) ); + if (argc != 0) { return enif_make_badarg(env); } - return nif_names(env); + result = nif_names(env); + + NDBG( ("nif_if_names -> done when result: %T\r\n", result) ); + + return result; } @@ -1016,6 +1173,8 @@ ERL_NIF_TERM nif_names(ErlNifEnv* env) ERL_NIF_TERM result; struct if_nameindex* ifs = if_nameindex(); + NDBG( ("nif_names -> ifs: 0x%lX\r\n", ifs) ); + if (ifs == NULL) { result = make_error2(env, get_errno()); } else { @@ -1033,6 +1192,8 @@ ERL_NIF_TERM nif_names(ErlNifEnv* env) */ unsigned int len = nif_names_length(ifs); + NDBG( ("nif_names -> len: %d\r\n", len) ); + if (len > 0) { ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); unsigned int i; @@ -1395,6 +1556,24 @@ BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, +static +ERL_NIF_TERM decode_bool(ErlNifEnv* env, + ERL_NIF_TERM eBool, + BOOLEAN_T* bool) +{ + if (COMPARE(eBool, atom_true) == 0) { + *bool = TRUE; + return atom_ok; + } else if (COMPARE(eBool, atom_false) == 0) { + *bool = FALSE; + return atom_ok; + } else { + return make_error(env, atom_einval); + } +} + + + /* Encode the address info * The address info is a linked list och address info, which * will result in the result being a list of zero or more length. @@ -1753,6 +1932,88 @@ void net_down(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * D e b u g F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* + * Print a debug format string *with* both a timestamp and the + * the name of the *current* thread. + */ +static +void dbg_printf( const char* format, ... ) +{ + va_list args; + char f[512 + sizeof(format)]; // This has to suffice... + char stamp[30]; + struct timespec ts; + int res; + + /* + * We should really include self in the printout, so we can se which process + * are executing the code. But then I must change the API.... + * ....something for later. + */ + + if (!dbg_realtime(&ts)) { + if (dbg_timespec2str(stamp, sizeof(stamp), &ts) != 0) { + // res = enif_snprintf(f, sizeof(f), "NET [%s] %s", TSNAME(), format); + res = enif_snprintf(f, sizeof(f), "NET [%s]", format); + } else { + // res = enif_snprintf(f, sizeof(f), "NET[%s] [%s] %s", stamp, TSNAME(), format); + res = enif_snprintf(f, sizeof(f), "NET [%s] %s", stamp, format); + } + + if (res > 0) { + va_start (args, format); + erts_vfprintf (stdout, f, args); // TMP: use enif_vfprintf + va_end (args); + fflush(stdout); + } + } + + return; +} + + +static +int dbg_realtime(struct timespec* tsP) +{ + return clock_gettime(CLOCK_REALTIME, tsP); +} + + + + +/* + * Convert a timespec struct into a readable/printable string + */ +static +int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts) +{ + int ret, buflen; + struct tm t; + + tzset(); + if (localtime_r(&(ts->tv_sec), &t) == NULL) + return 1; + + ret = strftime(buf, len, "%F %T", &t); + if (ret == 0) + return 2; + len -= ret - 1; + buflen = strlen(buf); + + ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); + if (ret >= len) + return 3; + + return 0; +} + + + /* ---------------------------------------------------------------------- * L o a d / u n l o a d / u p g r a d e F u n c t i o n s * ---------------------------------------------------------------------- @@ -1764,6 +2025,7 @@ ErlNifFunc net_funcs[] = // Some utility functions {"nif_is_loaded", 0, nif_is_loaded, 0}, {"nif_info", 0, nif_info, 0}, + {"nif_command", 1, nif_command, 0}, // Shall we let this be dirty? /* address and name translation in protocol-independent manner */ {"nif_getnameinfo", 2, nif_getnameinfo, 0}, @@ -1784,10 +2046,17 @@ ErlNifFunc net_funcs[] = static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { + // We should make it possible to use load_info to get default values + data.debug = FALSE; + + // NDBG( ("on_load -> entry\r\n") ); + /* +++ Misc atoms +++ */ atom_address_info = MKA(env, str_address_info); + atom_debug = MKA(env, str_debug); atom_dgram = MKA(env, str_dgram); atom_error = MKA(env, str_error); + atom_false = MKA(env, str_false); atom_idn = MKA(env, str_idn); atom_idna_allow_unassigned = MKA(env, str_idna_allow_unassigned); atom_idna_use_std3_ascii_rules = MKA(env, str_idna_use_std3_ascii_rules); @@ -1849,6 +2118,8 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) ERL_NIF_RT_CREATE, NULL); + // NDBG( ("on_load -> done\r\n") ); + return !net; } -- cgit v1.2.3 From f2a28200a826af65596bb554b014d2c93b6314a7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 11 Jun 2018 14:40:58 +0200 Subject: [net-nif] Removed the old net module The old net module (in kernel) (deprecated) was removed and its function(s) has been moved into the new module. Also a minor updated to the info function. OTP-14831 --- erts/emulator/nifs/common/net_nif.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 134b02d178..328fb52ac3 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -669,13 +669,15 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM info; + ERL_NIF_TERM info, tmp; NDBG( ("info -> entry\r\n") ); - info = enif_make_new_map(env); + tmp = enif_make_new_map(env); + if (!enif_make_map_put(env, tmp, atom_debug, BOOL2ATOM(data.debug), &info)) + info = tmp; - NDBG( ("info -> done\r\n") ); + NDBG( ("info -> done: %T\r\n", info) ); return info; } -- cgit v1.2.3 From 8c6d495f54207d019e645e5ff726418677f92ab9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 11 Jun 2018 18:14:42 +0200 Subject: [net-nif] Implemented gethostname --- erts/emulator/nifs/common/net_nif.c | 93 ++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 328fb52ac3..d623c6f246 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -215,6 +215,13 @@ typedef unsigned int BOOLEAN_T; typedef unsigned long long llu_t; +#ifdef __WIN32__ +#define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__)) +#else +#define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__)) +#endif // __WIN32__ + + /* Socket stuff */ // #define INVALID_SOCKET -1 @@ -244,6 +251,12 @@ typedef unsigned long long llu_t; #define SERVICE_LEN 256 +/* MAXHOSTNAMELEN could be 64 or 255 depending + * on the platform. Instead, use INET_MAXHOSTNAMELEN + * which is always 255 across all platforms + */ +#define NET_MAXHOSTNAMELEN 255 + /* =================================================================== * * * * Various enif macros * @@ -364,10 +377,13 @@ static ERL_NIF_TERM nif_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); -static ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, +static ERL_NIF_TERM nif_gethostname(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -384,6 +400,7 @@ static ERL_NIF_TERM nif_if_names(ErlNifEnv* env, static ERL_NIF_TERM ncommand(ErlNifEnv* env, ERL_NIF_TERM cmd); +static ERL_NIF_TERM ngethostname(ErlNifEnv* env); static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, const SockAddress* saP, SOCKLEN_T saLen, @@ -531,9 +548,11 @@ static char str_eagain[] = "eagain"; static char str_ebadflags[] = "ebadflags"; static char str_efail[] = "efail"; static char str_efamily[] = "efamily"; +static char str_efault[] = "efault"; static char str_einval[] = "einval"; // static char str_eisconn[] = "eisconn"; static char str_emem[] = "emem"; +static char str_enametoolong[] = "enametoolong"; static char str_enodata[] = "enodata"; static char str_enoname[] = "enoname"; // static char str_enotclosing[] = "enotclosing"; @@ -588,9 +607,11 @@ static ERL_NIF_TERM atom_eagain; static ERL_NIF_TERM atom_ebadflags; static ERL_NIF_TERM atom_efail; static ERL_NIF_TERM atom_efamily; +static ERL_NIF_TERM atom_efault; static ERL_NIF_TERM atom_einval; // static ERL_NIF_TERM atom_eisconn; static ERL_NIF_TERM atom_emem; +static ERL_NIF_TERM atom_enametoolong; static ERL_NIF_TERM atom_enodata; static ERL_NIF_TERM atom_enoname; // static ERL_NIF_TERM atom_enotclosing; @@ -754,6 +775,71 @@ ERL_NIF_TERM ncommand(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * nif_gethostname + * + * Description: + * Access the hostname of the current processor. + * + */ +static +ERL_NIF_TERM nif_gethostname(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM result; + + NDBG( ("nif_gethostname -> entry (%d)\r\n", argc) ); + + if (argc != 0) + return enif_make_badarg(env); + + result = ngethostname(env); + + NDBG( ("nif_gethostname -> done when result: %T\r\n", result) ); + + return result; +} + + +static +ERL_NIF_TERM ngethostname(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + char buf[NET_MAXHOSTNAMELEN + 1]; + int res; + + res = net_gethostname(buf, sizeof(buf)); + + NDBG( ("ngethostname -> gethostname res: %d\r\n", res) ); + + switch (res) { + case 0: + result = make_ok2(env, MKS(env, buf)); + break; + + case EFAULT: + result = make_error(env, atom_efault); + break; + + case EINVAL: + result = make_error(env, atom_einval); + break; + + case ENAMETOOLONG: + result = make_error(env, atom_enametoolong); + break; + + default: + result = make_error(env, MKI(env, res)); + break; + } + + return result; +} + + + /* ---------------------------------------------------------------------- * nif_getnameinfo @@ -2029,6 +2115,9 @@ ErlNifFunc net_funcs[] = {"nif_info", 0, nif_info, 0}, {"nif_command", 1, nif_command, 0}, // Shall we let this be dirty? + /* get/set hostname */ + {"nif_gethostname", 0, nif_gethostname, 0}, + /* address and name translation in protocol-independent manner */ {"nif_getnameinfo", 2, nif_getnameinfo, 0}, {"nif_getaddrinfo", 3, nif_getaddrinfo, 0}, @@ -2090,9 +2179,11 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_ebadflags = MKA(env, str_ebadflags); atom_efail = MKA(env, str_efail); atom_efamily = MKA(env, str_efamily); + atom_efault = MKA(env, str_efault); atom_einval = MKA(env, str_einval); // atom_eisconn = MKA(env, str_eisconn); atom_emem = MKA(env, str_emem); + atom_enametoolong = MKA(env, str_enametoolong); atom_enodata = MKA(env, str_enodata); atom_enoname = MKA(env, str_enoname); // atom_enotclosing = MKA(env, str_enotclosing); -- cgit v1.2.3 From d892681a4554b08442db219c3ac161027e9a4c4f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 12 Jun 2018 15:06:03 +0200 Subject: [net-nif] Fixed things... Fixed functions getaddrinfo, if_index2name, if_name2index and if_names. OTP-14831 --- erts/emulator/nifs/common/net_nif.c | 162 +++++++++++++++++++++++++++++------- 1 file changed, 130 insertions(+), 32 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index d623c6f246..900828c7b2 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -514,6 +514,7 @@ static const struct in6_addr in6addr_loopback = /* *** String constants *** */ static char str_address_info[] = "address_info"; +static char str_dccp[] = "dccp"; static char str_debug[] = "debug"; static char str_dgram[] = "dgram"; static char str_error[] = "error"; @@ -530,6 +531,9 @@ static char str_nofqdn[] = "nofqdn"; static char str_numerichost[] = "numerichost"; static char str_numericserv[] = "numericserv"; static char str_ok[] = "ok"; +static char str_raw[] = "raw"; +static char str_rdm[] = "rdm"; +static char str_seqpacket[] = "seqpacket"; static char str_stream[] = "stream"; static char str_tcp[] = "tcp"; static char str_true[] = "true"; @@ -557,6 +561,7 @@ static char str_enodata[] = "enodata"; static char str_enoname[] = "enoname"; // static char str_enotclosing[] = "enotclosing"; // static char str_enotconn[] = "enotconn"; +static char str_enxio[] = "enxio"; static char str_eoverflow[] = "eoverflow"; static char str_eservice[] = "eservice"; static char str_esocktype[] = "esocktype"; @@ -572,6 +577,7 @@ static char str_esystem[] = "esystem"; /* *** Atoms *** */ static ERL_NIF_TERM atom_address_info; +static ERL_NIF_TERM atom_dccp; static ERL_NIF_TERM atom_debug; static ERL_NIF_TERM atom_dgram; static ERL_NIF_TERM atom_error; @@ -588,8 +594,11 @@ static ERL_NIF_TERM atom_nofqdn; static ERL_NIF_TERM atom_numerichost; static ERL_NIF_TERM atom_numericserv; static ERL_NIF_TERM atom_ok; +static ERL_NIF_TERM atom_raw; +static ERL_NIF_TERM atom_rdm; // static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_stream; +static ERL_NIF_TERM atom_seqpacket; // static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_tcp; static ERL_NIF_TERM atom_true; @@ -616,6 +625,7 @@ static ERL_NIF_TERM atom_enodata; static ERL_NIF_TERM atom_enoname; // static ERL_NIF_TERM atom_enotclosing; // static ERL_NIF_TERM atom_enotconn; +static ERL_NIF_TERM atom_enxio; static ERL_NIF_TERM atom_eoverflow; static ERL_NIF_TERM atom_eservice; static ERL_NIF_TERM atom_esocktype; @@ -1029,7 +1039,8 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, FREE(hints); */ - NDBG( ("nif_getaddrinfo -> done when result: %T\r\n", result) ); + NDBG( ("nif_getaddrinfo -> done when result: " + "\r\n %T\r\n", result) ); return result; } @@ -1044,8 +1055,17 @@ ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, struct addrinfo* addrInfoP; int res; + NDBG( ("ngetaddrinfo -> entry with" + "\r\n host: %s" + "\r\n serv: %s" + "\r\n", + ((host == NULL) ? "NULL" : host), + ((serv == NULL) ? "NULL" : serv)) ); + res = getaddrinfo(host, serv, NULL, &addrInfoP); + NDBG( ("ngetaddrinfo -> res: %d\r\n", res) ); + switch (res) { case 0: { @@ -1133,7 +1153,7 @@ ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, } eifn = argv[0]; - NDBG( ("nif_name2index -> " + NDBG( ("nif_if_name2index -> " "\r\n Ifn: %T" "\r\n", argv[0]) ); @@ -1153,12 +1173,18 @@ static ERL_NIF_TERM nif_name2index(ErlNifEnv* env, char* ifn) { - unsigned int idx = if_nametoindex(ifn); + unsigned int idx; + + NDBG( ("nif_name2index -> entry with ifn: %s\r\n", ifn) ); + + idx = if_nametoindex(ifn); - if (idx == 0) + NDBG( ("nif_name2index -> idx: %d\r\n", idx) ); + + if (idx == 0) return make_error2(env, get_errno()); else - return make_ok2(env, idx); + return make_ok2(env, MKI(env, idx)); } @@ -1212,10 +1238,10 @@ ERL_NIF_TERM nif_index2name(ErlNifEnv* env, if (ifn == NULL) return enif_make_badarg(env); // PLACEHOLDER - if (NULL == if_indextoname(idx, ifn)) { + if (NULL != if_indextoname(idx, ifn)) { result = make_ok2(env, MKS(env, ifn)); } else { - result = make_error2(env, get_errno()); + result = make_error(env, atom_enxio); } FREE(ifn); @@ -1310,9 +1336,19 @@ static unsigned int nif_names_length(struct if_nameindex* p) { unsigned int len = 0; + BOOLEAN_T done = FALSE; + + while (!done) { + + NDBG( ("nif_names_length -> %d: " + "\r\n if_index: %d" + "\r\n if_name: 0x%lX" + "\r\n", len, p[len].if_index, p[len].if_name) ); - while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { - len++; + if ((p[len].if_index == 0) && (p[len].if_name == NULL)) + done = TRUE; + else + len++; } return len; @@ -1627,9 +1663,13 @@ BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, result = FALSE; } - bufP = MALLOC(len); + NDBG( ("decode_addrinfo_string -> len: %d\r\n", len) ); + + bufP = MALLOC(len + 1); // We shall NULL-terminate - if (GET_STR(env, eString, bufP, len)) { + if (GET_STR(env, eString, bufP, len+1)) { + NDBG( ("decode_addrinfo_string -> buf: %s\r\n", bufP) ); + // bufP[len] = '\0'; *stringP = bufP; result = TRUE; } else { @@ -1673,19 +1713,32 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env, ERL_NIF_TERM result; unsigned int len = address_info_length(addrInfo); - if (len > 0) { - ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); - unsigned int i; + NDBG( ("encode_address_info -> len: %d\r\n", len) ); + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i = 0; + struct addrinfo* p = addrInfo; + + while (i < len) { + array[i] = make_address_info(env, p); + p = p->ai_next; + i++; + } + /* for (i = 0; i < len; i++) { array[i] = make_address_info(env, &addrInfo[i]); } + */ - result = make_ok2(env, MKLA(env, array, len)); + result = MKLA(env, array, len); } else { result = MKEL(env); } + NDBG( ("encode_address_info -> result: " + "\r\n %T\r\n", result) ); + return result; } @@ -1698,17 +1751,19 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env, static unsigned int address_info_length(struct addrinfo* addrInfoP) { - unsigned int len; - struct addrinfo* tmp = addrInfoP; + unsigned int len = 1; + struct addrinfo* tmp; + BOOLEAN_T done = FALSE; - if (tmp != NULL) { - len = 1; - while (tmp->ai_next != NULL) { - tmp = tmp->ai_next; + tmp = addrInfoP; + + while (!done) { + if (tmp->ai_next != NULL) { len++; + tmp = tmp->ai_next; + } else { + done = TRUE; } - } else { - len = 0; } return len; @@ -1725,14 +1780,27 @@ static ERL_NIF_TERM make_address_info(ErlNifEnv* env, struct addrinfo* addrInfoP) { - ERL_NIF_TERM Fam = make_addrinfo_family(env, addrInfoP->ai_family); - ERL_NIF_TERM Type = make_addrinfo_type(env, addrInfoP->ai_socktype); - ERL_NIF_TERM Proto = make_addrinfo_proto(env, addrInfoP->ai_protocol); - ERL_NIF_TERM Addr = make_addrinfo_addr(env, - addrInfoP->ai_addr, - addrInfoP->ai_addrlen); + ERL_NIF_TERM result, fam, type, proto, addr; + + fam = make_addrinfo_family(env, addrInfoP->ai_family); + // NDBG( ("make_address_info -> fam: %T\r\n", fam) ); + type = make_addrinfo_type(env, addrInfoP->ai_socktype); + // NDBG( ("make_address_info -> type: %T\r\n", type) ); + proto = make_addrinfo_proto(env, addrInfoP->ai_protocol); + // NDBG( ("make_address_info -> proto: %T\r\n", proto) ); + addr = make_addrinfo_addr(env, + addrInfoP->ai_addr, + addrInfoP->ai_addrlen); + // NDBG( ("make_address_info -> addr: %T\r\n", addr) ); + + result = MKT5(env, atom_address_info, fam, type, proto, addr); - return MKT5(env, atom_address_info, Fam, Type, Proto, Addr); + /* + NDBG( ("make_address_info -> result: " + "\r\n %T\r\n", result) ); + */ + + return result; } @@ -1789,6 +1857,22 @@ ERL_NIF_TERM make_addrinfo_type(ErlNifEnv* env, etype = atom_dgram; break; + case SOCK_RAW: + etype = atom_raw; + break; + + case SOCK_RDM: + etype = atom_rdm; + break; + + case SOCK_SEQPACKET: + etype = atom_seqpacket; + break; + + case SOCK_DCCP: + etype = atom_dccp; + break; + default: etype = MKI(env, socktype); break; @@ -1850,11 +1934,16 @@ ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, ERL_NIF_TERM port, addr, eaddr; SockAddress* p = (SockAddress*) addrP; + NDBG( ("make_addrinfo_addr -> entry with" + "\r\n family: %d" + "\r\n addrLen: %d" + "\r\n", addrP->sa_family, addrLen) ); + switch (addrP->sa_family) { case AF_INET: { unsigned char* a = (unsigned char*) &p->in.sin_addr; - port = ntohs(p->in.sin_port); + port = MKI(env, ntohs(p->in.sin_port)); addr = MKT4(env, MKI(env, a[0]), MKI(env, a[1]), @@ -1868,7 +1957,7 @@ ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, case AF_INET6: { unsigned char* a = (unsigned char*) &p->in6.sin6_addr; - port = ntohs(p->in6.sin6_port); + port = MKI(env, ntohs(p->in6.sin6_port)); addr = MKT8(env, MKI(env, get_int16(a)), MKI(env, get_int16(&a[ 2])), @@ -1888,6 +1977,9 @@ ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, break; } + NDBG( ("make_addrinfo_addr -> eaddr: " + "\r\n %T\r\n", eaddr) ); + return eaddr; } @@ -1948,6 +2040,7 @@ ERL_NIF_TERM make_error1(ErlNifEnv* env, char* reason) static ERL_NIF_TERM make_error2(ErlNifEnv* env, int err) { + NDBG( ("make_error2 -> err: %d\r\n", err) ); return make_error1(env, erl_errno_id(err)); } @@ -2144,6 +2237,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) /* +++ Misc atoms +++ */ atom_address_info = MKA(env, str_address_info); + atom_dccp = MKA(env, str_dccp); atom_debug = MKA(env, str_debug); atom_dgram = MKA(env, str_dgram); atom_error = MKA(env, str_error); @@ -2160,6 +2254,9 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_numerichost = MKA(env, str_numerichost); atom_numericserv = MKA(env, str_numericserv); atom_ok = MKA(env, str_ok); + atom_raw = MKA(env, str_raw); + atom_rdm = MKA(env, str_rdm); + atom_seqpacket = MKA(env, str_seqpacket); atom_stream = MKA(env, str_stream); atom_tcp = MKA(env, str_tcp); atom_true = MKA(env, str_true); @@ -2188,6 +2285,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_enoname = MKA(env, str_enoname); // atom_enotclosing = MKA(env, str_enotclosing); // atom_enotconn = MKA(env, str_enotconn); + atom_enxio = MKA(env, str_enxio); atom_eoverflow = MKA(env, str_eoverflow); atom_eservice = MKA(env, str_eservice); atom_esocktype = MKA(env, str_esocktype); -- cgit v1.2.3 From ef34944c970f842a7406f59c827243c8be77fdc2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 12 Jun 2018 15:47:29 +0200 Subject: [net-nif] Fixed getnameinfo And now fixed the getnameinfo function. OTP-14831 --- erts/emulator/nifs/common/net_nif.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 900828c7b2..e0129aec69 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -659,6 +659,7 @@ static ErlNifResourceTypeInit netInit = { * * The "proper" net functions: * ------------------------------ + * nif_gethostname/0 * nif_getnameinfo/2 * nif_getaddrinfo/3 * nif_if_name2index/1 @@ -860,7 +861,6 @@ ERL_NIF_TERM ngethostname(ErlNifEnv* env) * Arguments: * SockAddr - Socket Address (address and port) * Flags - The flags argument modifies the behavior of getnameinfo(). - * Not used! */ static @@ -869,33 +869,33 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM result, eSockAddr; - unsigned int eFlags; + ERL_NIF_TERM eFlags; int flags = 0; // Just in case... SockAddress sa; SOCKLEN_T saLen = 0; // Just in case... NDBG( ("nif_getnameinfo -> entry (%d)\r\n", argc) ); - if ((argc != 2) || - !GET_UINT(env, argv[1], &eFlags)) { + if (argc != 2) return enif_make_badarg(env); - } eSockAddr = argv[0]; + eFlags = argv[1]; NDBG( ("nif_getnameinfo -> " "\r\n SockAddr: %T" "\r\n Flags: %T" - "\r\n", argv[0], argv[1]) ); + "\r\n", eSockAddr, eFlags) ); if (!decode_nameinfo_flags(env, eFlags, &flags)) return enif_make_badarg(env); - if (decode_in_sockaddr(env, eSockAddr, &sa, &saLen)) + if (!decode_in_sockaddr(env, eSockAddr, &sa, &saLen)) return enif_make_badarg(env); result = ngetnameinfo(env, &sa, saLen, flags); - NDBG( ("nif_getnameinfo -> done when result: %T\r\n", result) ); + NDBG( ("nif_getnameinfo -> done when result: " + "\r\n %T\r\n", result) ); return result; } @@ -922,6 +922,8 @@ ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, serv, servLen, flags); + NDBG( ("ngetnameinfo -> res: %d\r\n", res) ); + switch (res) { case 0: { @@ -1569,6 +1571,7 @@ BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, result = FALSE; } } else if (IS_LIST(env, eflags)) { + NDBG( ("decode_nameinfo_flags -> is atom\r\n") ); result = decode_nameinfo_flags_list(env, eflags, flags); } else { result = FALSE; -- cgit v1.2.3 From 1b31432a2c60364dc3e7b2a18fa8494475344271 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 15 Jun 2018 18:51:48 +0200 Subject: [socket+net-nif] Introduced a couple of common files Started to move the common stuff, such as common utility functions, debug and encode / decode of basic types. OTP-14831 --- erts/emulator/nifs/common/net_nif.c | 511 ++++++++++---- erts/emulator/nifs/common/socket_dbg.c | 116 ++++ erts/emulator/nifs/common/socket_dbg.h | 41 ++ erts/emulator/nifs/common/socket_int.h | 193 ++++++ erts/emulator/nifs/common/socket_nif.c | 1100 +++++++++++++++++++------------ erts/emulator/nifs/common/socket_util.c | 659 ++++++++++++++++++ erts/emulator/nifs/common/socket_util.h | 108 +++ 7 files changed, 2192 insertions(+), 536 deletions(-) create mode 100644 erts/emulator/nifs/common/socket_dbg.c create mode 100644 erts/emulator/nifs/common/socket_dbg.h create mode 100644 erts/emulator/nifs/common/socket_int.h create mode 100644 erts/emulator/nifs/common/socket_util.c create mode 100644 erts/emulator/nifs/common/socket_util.h (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index e0129aec69..90263d11c2 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -174,6 +174,9 @@ #include +#include "socket_dbg.h" +#include "socket_int.h" + /* All platforms fail on malloc errors. */ #define FATAL_MALLOC @@ -263,66 +266,67 @@ typedef unsigned long long llu_t; * * * =================================================================== */ -#define MALLOC(SZ) enif_alloc((SZ)) -#define FREE(P) enif_free((P)) - -#define MKA(E,S) enif_make_atom((E), (S)) -#define MKBIN(E,B) enif_make_binary((E), (B)) -#define MKI(E,I) enif_make_int((E), (I)) -#define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) -#define MKEL(E) enif_make_list((E), 0) -#define MKREF(E) enif_make_ref((E)) -#define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) -#define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) -#define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) -#define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) -#define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) -#define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) -#define MKT5(E,E1,E2,E3,E4,E5) \ - enif_make_tuple5((E), (E1), (E2), (E3), (E4), (E5)) -#define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ - enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) - -#define MCREATE(N) enif_mutex_create((N)) -#define MDESTROY(M) enif_mutex_destroy((M)) -#define MLOCK(M) enif_mutex_lock((M)) -#define MUNLOCK(M) enif_mutex_unlock((M)) - -#define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) -#define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) - -#define SELECT(E,FD,M,O,P,R) \ - enif_select((E), (FD), (M), (O), (P), (R)) -#define SELECT_READ(E, DP, P, R) \ - SELECT((E), (DP)->sock, (ERL_NIF_SELECT_READ), (DP), (P), (R)) -#define SELECT_WRITE(E, DP, P, R) \ - SELECT((E), (DP)->sock, (ERL_NIF_SELECT_WRITE), (DP), (P), (R)) -#define SELECT_STOP(E, DP) \ - enif_select((E), (DP)->sock, (ERL_NIF_SELECT_STOP), (DP), NULL, atom_undefined) - -#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)) -#define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) -#define IS_LIST(E, TE) enif_is_list((E), (TE)) - -#define COMPARE(L, R) enif_compare((L), (R)) - -#define GET_ATOM_LEN(E, TE, LP) \ - enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) -#define GET_ATOM(E, TE, BP, MAX) \ - enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) -#define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) -#define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) -#define GET_LIST_ELEM(E, L, HP, TP) enif_get_list_cell((E), (L), (HP), (TP)) -#define GET_LIST_LEN(E, L, LP) enif_get_list_length((E), (L), (LP)) -#define GET_STR(E, L, B, SZ) \ - enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) -#define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) -#define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) - -#define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) -#define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) +/* #define MALLOC(SZ) enif_alloc((SZ)) */ +/* #define FREE(P) enif_free((P)) */ + +/* #define MKA(E,S) enif_make_atom((E), (S)) */ +/* #define MKBIN(E,B) enif_make_binary((E), (B)) */ +/* #define MKI(E,I) enif_make_int((E), (I)) */ +/* #define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) */ +/* #define MKEL(E) enif_make_list((E), 0) */ +/* #define MKREF(E) enif_make_ref((E)) */ +/* #define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) */ +/* #define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) */ +/* #define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) */ +/* #define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) */ +/* #define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) */ +/* #define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) */ +/* #define MKT5(E,E1,E2,E3,E4,E5) \ */ +/* enif_make_tuple5((E), (E1), (E2), (E3), (E4), (E5)) */ +/* #define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ */ +/* enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) */ +/* #define MKTA(E, A, AL) enif_make_tuple_from_array((E), (A), (AL)) */ + +/* #define MCREATE(N) enif_mutex_create((N)) */ +/* #define MDESTROY(M) enif_mutex_destroy((M)) */ +/* #define MLOCK(M) enif_mutex_lock((M)) */ +/* #define MUNLOCK(M) enif_mutex_unlock((M)) */ + +/* #define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) */ +/* #define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) */ + +/* #define SELECT(E,FD,M,O,P,R) \ */ +/* enif_select((E), (FD), (M), (O), (P), (R)) */ +/* #define SELECT_READ(E, DP, P, R) \ */ +/* SELECT((E), (DP)->sock, (ERL_NIF_SELECT_READ), (DP), (P), (R)) */ +/* #define SELECT_WRITE(E, DP, P, R) \ */ +/* SELECT((E), (DP)->sock, (ERL_NIF_SELECT_WRITE), (DP), (P), (R)) */ +/* #define SELECT_STOP(E, DP) \ */ +/* enif_select((E), (DP)->sock, (ERL_NIF_SELECT_STOP), (DP), NULL, atom_undefined) */ + +/* #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)) */ +/* #define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) */ +/* #define IS_LIST(E, TE) enif_is_list((E), (TE)) */ + +/* #define COMPARE(L, R) enif_compare((L), (R)) */ + +/* #define GET_ATOM_LEN(E, TE, LP) \ */ +/* enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) */ +/* #define GET_ATOM(E, TE, BP, MAX) \ */ +/* enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) */ +/* #define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) */ +/* #define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) */ +/* #define GET_LIST_ELEM(E, L, HP, TP) enif_get_list_cell((E), (L), (HP), (TP)) */ +/* #define GET_LIST_LEN(E, L, LP) enif_get_list_length((E), (L), (LP)) */ +/* #define GET_STR(E, L, B, SZ) \ */ +/* enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) */ +/* #define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) */ +/* #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) */ + +/* #define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) */ +/* #define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) */ #ifdef HAVE_SOCKLEN_T @@ -342,12 +346,16 @@ typedef unsigned long long llu_t; typedef union { struct sockaddr sa; - struct sockaddr_in in; + struct sockaddr_in in4; #ifdef HAVE_IN6 struct sockaddr_in6 in6; #endif +#ifdef HAVE_SYS_UN_H + struct sockaddr_un un; +#endif + } SockAddress; typedef struct { @@ -440,6 +448,22 @@ static BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, SockAddress* saP, SOCKLEN_T* saLen); #endif +static ERL_NIF_TERM encode_in_sockaddr(ErlNifEnv* env, + struct sockaddr* addrP, + SOCKLEN_T addrLen); +static ERL_NIF_TERM encode_in4_sockaddr(ErlNifEnv* env, + struct sockaddr_in* addrP, + SOCKLEN_T addrLen); +#if defined(HAVE_IN6) && defined(AF_INET6) +static ERL_NIF_TERM encode_in6_sockaddr(ErlNifEnv* env, + struct sockaddr_in6* addrP, + SOCKLEN_T addrLen); +#endif +#ifdef HAVE_SYS_UN_H +static ERL_NIF_TERM encode_un_sockaddr(ErlNifEnv* env, + struct sockaddr_un* addrP, + SOCKLEN_T addrLen); +#endif static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, const ERL_NIF_TERM eflags, int* flags); @@ -453,28 +477,50 @@ BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, static ERL_NIF_TERM decode_bool(ErlNifEnv* env, ERL_NIF_TERM eBool, BOOLEAN_T* bool); +static ERL_NIF_TERM encode_address_infos(ErlNifEnv* env, + struct addrinfo* addrInfo); static ERL_NIF_TERM encode_address_info(ErlNifEnv* env, - struct addrinfo* addrInfo); + struct addrinfo* addrInfoP); static unsigned int address_info_length(struct addrinfo* addrInfoP); -static ERL_NIF_TERM make_address_info(ErlNifEnv* env, - struct addrinfo* addrInfoP); -static ERL_NIF_TERM make_addrinfo_family(ErlNifEnv* env, +static ERL_NIF_TERM encode_address_info_family(ErlNifEnv* env, int family); -static ERL_NIF_TERM make_addrinfo_type(ErlNifEnv* env, +static ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env, int socktype); -static ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env, +static ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, int proto); -static ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, - struct sockaddr* addrP, - SOCKLEN_T addrLen); +/* static ERL_NIF_TERM make_address_info(ErlNifEnv* env, */ +/* struct addrinfo* addrInfoP); */ +/* static ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, */ +/* struct sockaddr* addrP, */ +/* SOCKLEN_T addrLen); */ + +static ERL_NIF_TERM make_in4_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr); +#if defined(HAVE_IN6) && defined(AF_INET6) +static ERL_NIF_TERM make_in6_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM flowInfo, + ERL_NIF_TERM scopeId); +#endif +static ERL_NIF_TERM make_address_info(ErlNifEnv* env, + ERL_NIF_TERM fam, + ERL_NIF_TERM sockType, + ERL_NIF_TERM proto, + ERL_NIF_TERM addr); static ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM val); // static ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2); 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); +#if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) +static size_t my_strnlen(const char *s, size_t maxlen); +#endif + static void dbg_printf( const char* format, ... ); static int dbg_realtime(struct timespec* tsP); static int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts); @@ -522,9 +568,12 @@ static char str_false[] = "false"; static char str_idn[] = "idn"; static char str_idna_allow_unassigned[] = "idna_allow_unassigned"; static char str_idna_use_std3_ascii_rules[] = "idna_use_std3_ascii_rules"; +static char str_in4_sockaddr[] = "in4_sockaddr"; +static char str_in6_sockaddr[] = "in6_sockaddr"; static char str_inet[] = "inet"; static char str_inet6[] = "inet6"; static char str_ip[] = "ip"; +static char str_ipv6[] = "ipv6"; static char str_namereqd[] = "namereqd"; static char str_name_info[] = "name_info"; static char str_nofqdn[] = "nofqdn"; @@ -585,9 +634,12 @@ static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_idn; static ERL_NIF_TERM atom_idna_allow_unassigned; static ERL_NIF_TERM atom_idna_use_std3_ascii_rules; +static ERL_NIF_TERM atom_in4_sockaddr; +static ERL_NIF_TERM atom_in6_sockaddr; static ERL_NIF_TERM atom_inet; static ERL_NIF_TERM atom_inet6; static ERL_NIF_TERM atom_ip; +static ERL_NIF_TERM atom_ipv6; static ERL_NIF_TERM atom_namereqd; static ERL_NIF_TERM atom_name_info; static ERL_NIF_TERM atom_nofqdn; @@ -1009,9 +1061,9 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, // eHints = argv[2]; NDBG( ("nif_getaddrinfo -> " - "\r\n Host: %T" - "\r\n Service: %T" - "\r\n Hints: %T" + "\r\n ehost: %T" + "\r\n eservice: %T" + "\r\n ehints: %T" "\r\n", argv[0], argv[1], argv[2]) ); if (!decode_addrinfo_string(env, eHostName, &hostName)) @@ -1071,7 +1123,7 @@ ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, switch (res) { case 0: { - ERL_NIF_TERM addrInfo = encode_address_info(env, addrInfoP); + ERL_NIF_TERM addrInfo = encode_address_infos(env, addrInfoP); freeaddrinfo(addrInfoP); result = make_ok2(env, addrInfo); } @@ -1454,14 +1506,14 @@ BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, /* And finally initialize the sockaddr structure (and size) */ sys_memzero((char*)saP, sizeof(struct sockaddr_in)); - saP->in.sin_family = AF_INET; - saP->in.sin_port = htons(port); + saP->in4.sin_family = AF_INET; + saP->in4.sin_port = htons(port); for (a = 0; a < 4; a++) { if (!GET_INT(env, ipAddrT[a], &v)) return FALSE; addr[a] = v; } - sys_memcpy(&saP->in.sin_addr, &addr, sizeof(addr)); + sys_memcpy(&saP->in4.sin_addr, &addr, sizeof(addr)); *saLen = sizeof(struct sockaddr_in); return TRUE; } @@ -1552,6 +1604,168 @@ BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, +static +ERL_NIF_TERM encode_in_sockaddr(ErlNifEnv* env, + struct sockaddr* addrP, + SOCKLEN_T addrLen) +{ + SockAddress* sockAddrP = (SockAddress*) addrP; + ERL_NIF_TERM sockAddr; + + switch (sockAddrP->sa.sa_family) { + case AF_INET: + sockAddr = encode_in4_sockaddr(env, &sockAddrP->in4, addrLen); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + sockAddr = encode_in6_sockaddr(env, &sockAddrP->in6, addrLen); + break; +#endif + +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + sockAddr = encode_un_sockaddr(env, &sockAddrP->un, addrLen); + break; +#endif + + default: + sockAddr = atom_undefined; + break; + } + + return sockAddr; +} + + + +/* Encode an IPv4 socket address; in4_sockaddr */ +static +ERL_NIF_TERM encode_in4_sockaddr(ErlNifEnv* env, + struct sockaddr_in* addrP, + SOCKLEN_T addrLen) +{ + ERL_NIF_TERM sockAddr; + short port; + ERL_NIF_TERM ePort, eAddr; + + + if (addrLen >= sizeof(struct sockaddr_in)) { + unsigned int i; + ERL_NIF_TERM at[4]; + unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); + unsigned char* a = (unsigned char*) &addrP->sin_addr; + + /* The port */ + port = ntohs(addrP->sin_port); + ePort = MKI(env, port); + + /* The address */ + for (i = 0; i < atLen; i++) { + at[i] = MKI(env, a[i]); + } + eAddr = MKTA(env, at, atLen); + // eAddr = MKT4(env, at[0], at[1], at[2], at[3]); + + /* And finally construct the in4_sockaddr record */ + sockAddr = make_in4_sockaddr(env, ePort, eAddr); + + } else { + sockAddr = atom_undefined; + } + + return sockAddr; +} + + + +/* Encode an IPv6 socket address; in6_sockaddr */ +#if defined(HAVE_IN6) && defined(AF_INET6) +static +ERL_NIF_TERM encode_in6_sockaddr(ErlNifEnv* env, + struct sockaddr_in6* addrP, + SOCKLEN_T addrLen) +{ + ERL_NIF_TERM sockAddr; + short port; + ERL_NIF_TERM ePort, eAddr, eFlowInfo, eScopeId; + + + if (addrLen >= sizeof(struct sockaddr_in6)) { + unsigned int i; + ERL_NIF_TERM at[8]; + unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); + unsigned char* a = (unsigned char*) &addrP->sin6_addr; + + /* The port */ + port = ntohs(addrP->sin6_port); + ePort = MKI(env, port); + + /* The address */ + for (i = 0; i < atLen; i++) { + at[i] = MKI(env, get_int16(a + i*2)); + } + eAddr = MKTA(env, at, atLen); + + /* The flowInfo */ + eFlowInfo = MKI(env, addrP->sin6_flowinfo); + + /* The scopeId */ + eScopeId = MKI(env, addrP->sin6_scope_id); + + /* And finally construct the in6_sockaddr record */ + sockAddr = make_in6_sockaddr(env, ePort, eAddr, eFlowInfo, eScopeId); + + } else { + sockAddr = atom_undefined; + } + + return sockAddr; +} +#endif + + + +/* Encode an Unix Domain socket address: string() */ +#ifdef HAVE_SYS_UN_H +static +ERL_NIF_TERM encode_un_sockaddr(ErlNifEnv* env, + struct sockaddr_un* addrP, + SOCKLEN_T addrLen) +{ + ERL_NIF_TERM sockAddr; + size_t n, m; + + if (addrLen >= offsetof(struct sockaddr_un, sun_path)) { + n = addrLen - offsetof(struct sockaddr_un, sun_path); + if (255 < n) { + sockAddr = atom_undefined; + } else { + m = my_strnlen(addrP->sun_path, n); +#ifdef __linux__ + /* Assume that the address is a zero terminated string, + * except when the first byte is \0 i.e the string length is 0, + * then use the reported length instead. + * This fix handles Linux's nonportable + * abstract socket address extension. + */ + if (m == 0) { + m = n; + } +#endif + + sockAddr = MKSL(env, addrP->sun_path, m); + } + } else { + sockAddr = atom_undefined; + } + + return sockAddr; +} +#endif + + + /* The erlang format for a set of flags is a list of atoms. * A special case is when there is no flags, which is * represented by the atom undefined. @@ -1710,13 +1924,13 @@ ERL_NIF_TERM decode_bool(ErlNifEnv* env, * will result in the result being a list of zero or more length. */ static -ERL_NIF_TERM encode_address_info(ErlNifEnv* env, - struct addrinfo* addrInfo) +ERL_NIF_TERM encode_address_infos(ErlNifEnv* env, + struct addrinfo* addrInfo) { ERL_NIF_TERM result; unsigned int len = address_info_length(addrInfo); - NDBG( ("encode_address_info -> len: %d\r\n", len) ); + NDBG( ("encode_address_infos -> len: %d\r\n", len) ); if (len > 0) { ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); @@ -1724,22 +1938,17 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env, struct addrinfo* p = addrInfo; while (i < len) { - array[i] = make_address_info(env, p); + array[i] = encode_address_info(env, p); p = p->ai_next; i++; } - /* - for (i = 0; i < len; i++) { - array[i] = make_address_info(env, &addrInfo[i]); - } - */ result = MKLA(env, array, len); } else { result = MKEL(env); } - NDBG( ("encode_address_info -> result: " + NDBG( ("encode_address_infos -> result: " "\r\n %T\r\n", result) ); return result; @@ -1780,28 +1989,17 @@ unsigned int address_info_length(struct addrinfo* addrInfoP) * {address_info, Fam, Type, Proto, Addr} */ static -ERL_NIF_TERM make_address_info(ErlNifEnv* env, - struct addrinfo* addrInfoP) +ERL_NIF_TERM encode_address_info(ErlNifEnv* env, + struct addrinfo* addrInfoP) { ERL_NIF_TERM result, fam, type, proto, addr; - fam = make_addrinfo_family(env, addrInfoP->ai_family); - // NDBG( ("make_address_info -> fam: %T\r\n", fam) ); - type = make_addrinfo_type(env, addrInfoP->ai_socktype); - // NDBG( ("make_address_info -> type: %T\r\n", type) ); - proto = make_addrinfo_proto(env, addrInfoP->ai_protocol); - // NDBG( ("make_address_info -> proto: %T\r\n", proto) ); - addr = make_addrinfo_addr(env, - addrInfoP->ai_addr, - addrInfoP->ai_addrlen); - // NDBG( ("make_address_info -> addr: %T\r\n", addr) ); + fam = encode_address_info_family(env, addrInfoP->ai_family); + type = encode_address_info_type(env, addrInfoP->ai_socktype); + proto = encode_address_info_proto(env, addrInfoP->ai_protocol); + addr = encode_in_sockaddr(env, addrInfoP->ai_addr, addrInfoP->ai_addrlen); - result = MKT5(env, atom_address_info, fam, type, proto, addr); - - /* - NDBG( ("make_address_info -> result: " - "\r\n %T\r\n", result) ); - */ + result = make_address_info(env, fam, type, proto, addr); return result; @@ -1814,23 +2012,29 @@ ERL_NIF_TERM make_address_info(ErlNifEnv* env, * in the form of an integer. */ static -ERL_NIF_TERM make_addrinfo_family(ErlNifEnv* env, - int family) +ERL_NIF_TERM encode_address_info_family(ErlNifEnv* env, + int family) { ERL_NIF_TERM efam; switch (family) { case AF_INET: - efam = atom_inet; + efam = esock_atom_inet; break; #if defined(HAVE_IN6) && defined(AF_INET6) case AF_INET6: - efam = atom_inet6; + efam = esock_atom_inet6; break; #endif - default: + #ifdef HAVE_SYS_UN_H + case AF_UNIX: + efam = esock_atom_local; + break; +#endif + + default: efam = MKI(env, family); break; } @@ -1846,8 +2050,8 @@ ERL_NIF_TERM make_addrinfo_family(ErlNifEnv* env, * in the form of an integer. */ static -ERL_NIF_TERM make_addrinfo_type(ErlNifEnv* env, - int socktype) +ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env, + int socktype) { ERL_NIF_TERM etype; @@ -1872,10 +2076,6 @@ ERL_NIF_TERM make_addrinfo_type(ErlNifEnv* env, etype = atom_seqpacket; break; - case SOCK_DCCP: - etype = atom_dccp; - break; - default: etype = MKI(env, socktype); break; @@ -1892,8 +2092,8 @@ ERL_NIF_TERM make_addrinfo_type(ErlNifEnv* env, * in the form of an integer. */ static -ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env, - int proto) +ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, + int proto) { ERL_NIF_TERM eproto; @@ -1906,6 +2106,12 @@ ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env, eproto = atom_ip; break; +#if defined(SOL_IPV6) + case SOL_IPV6: + eproto = atom_ipv6; + break; +#endif + case IPPROTO_TCP: eproto = atom_tcp; break; @@ -1914,6 +2120,12 @@ ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env, eproto = atom_udp; break; +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + eproto = atom_sctp; + break; +#endif + default: eproto = MKI(env, proto); break; @@ -1929,15 +2141,16 @@ ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env, * IPv4 and IPv6 addresses. Values of other families will be * returned as an undefined. */ +/* static -ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, - struct sockaddr* addrP, - SOCKLEN_T addrLen) +ERL_NIF_TERM encode_address_info_addr(ErlNifEnv* env, + struct sockaddr* addrP, + SOCKLEN_T addrLen) { ERL_NIF_TERM port, addr, eaddr; SockAddress* p = (SockAddress*) addrP; - NDBG( ("make_addrinfo_addr -> entry with" + NDBG( ("encode_address_info_addr -> entry with" "\r\n family: %d" "\r\n addrLen: %d" "\r\n", addrP->sa_family, addrLen) ); @@ -1985,6 +2198,57 @@ ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, return eaddr; } +*/ + + + +#if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) +/* strnlen doesn't exist everywhere */ +static +size_t my_strnlen(const char *s, size_t maxlen) +{ + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; +} +#endif + + + +/* Construct the IPv4 socket address record: in4_sockaddr */ +static +ERL_NIF_TERM make_in4_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr) +{ + return MKT3(env, atom_in4_sockaddr, port, addr); +} + + + +/* Construct the IPv6 socket address record: in6_sockaddr */ +static +ERL_NIF_TERM make_in6_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM flowInfo, + ERL_NIF_TERM scopeId) +{ + return MKT5(env, atom_in6_sockaddr, port, addr, flowInfo, scopeId); +} + + + +static +ERL_NIF_TERM make_address_info(ErlNifEnv* env, + ERL_NIF_TERM fam, + ERL_NIF_TERM sockType, + ERL_NIF_TERM proto, + ERL_NIF_TERM addr) +{ + return MKT5(env, atom_address_info, fam, sockType, proto, addr); +} @@ -2248,9 +2512,12 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_idn = MKA(env, str_idn); atom_idna_allow_unassigned = MKA(env, str_idna_allow_unassigned); atom_idna_use_std3_ascii_rules = MKA(env, str_idna_use_std3_ascii_rules); + atom_in4_sockaddr = MKA(env, str_in4_sockaddr); + atom_in6_sockaddr = MKA(env, str_in6_sockaddr); atom_inet = MKA(env, str_inet); atom_inet6 = MKA(env, str_inet6); atom_ip = MKA(env, str_ip); + atom_ipv6 = MKA(env, str_ipv6); atom_namereqd = MKA(env, str_namereqd); atom_name_info = MKA(env, str_name_info); atom_nofqdn = MKA(env, str_nofqdn); diff --git a/erts/emulator/nifs/common/socket_dbg.c b/erts/emulator/nifs/common/socket_dbg.c new file mode 100644 index 0000000000..dd11fbca9b --- /dev/null +++ b/erts/emulator/nifs/common/socket_dbg.c @@ -0,0 +1,116 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. 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. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Debug functions for the socket and net NIF(s). + * ---------------------------------------------------------------------- + * + */ + +#include +#include +#include +#include +#include + +#include +#include "socket_dbg.h" + +#define TSELF() enif_thread_self() +#define TNAME(__T__) enif_thread_name( __T__ ) +#define TSNAME() TNAME(TSELF()) + +static int realtime(struct timespec* tsP); +static int timespec2str(char *buf, unsigned int len, struct timespec *ts); + + +/* + * Print a debug format string *with* both a timestamp and the + * the name of the *current* thread. + */ +extern +void esock_dbg_printf( const char* prefix, const char* format, ... ) +{ + va_list args; + char f[512 + sizeof(format)]; // This has to suffice... + char stamp[30]; + struct timespec ts; + int res; + + /* + * We should really include self in the printout, so we can se which process + * are executing the code. But then I must change the API.... + * ....something for later. + */ + + if (!realtime(&ts)) { + if (timespec2str(stamp, sizeof(stamp), &ts) != 0) { + res = enif_snprintf(f, sizeof(f), "%s [%s] %s", prefix, TSNAME(), format); + // res = enif_snprintf(f, sizeof(f), "%s [%s]", prefix, format); + } else { + res = enif_snprintf(f, sizeof(f), "%s [%s] [%s] %s", prefix, stamp, TSNAME(), format); + // res = enif_snprintf(f, sizeof(f), "%s [%s] %s", prefix, stamp, format); + } + + if (res > 0) { + va_start (args, format); + enif_vfprintf (stdout, f, args); + va_end (args); + fflush(stdout); + } + } + + return; +} + + +static +int realtime(struct timespec* tsP) +{ + return clock_gettime(CLOCK_REALTIME, tsP); +} + + + + +/* + * Convert a timespec struct into a readable/printable string + */ +static +int timespec2str(char *buf, unsigned int len, struct timespec *ts) +{ + int ret, buflen; + struct tm t; + + tzset(); + if (localtime_r(&(ts->tv_sec), &t) == NULL) + return 1; + + ret = strftime(buf, len, "%F %T", &t); + if (ret == 0) + return 2; + len -= ret - 1; + buflen = strlen(buf); + + ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); + if (ret >= len) + return 3; + + return 0; +} diff --git a/erts/emulator/nifs/common/socket_dbg.h b/erts/emulator/nifs/common/socket_dbg.h new file mode 100644 index 0000000000..36e2be1679 --- /dev/null +++ b/erts/emulator/nifs/common/socket_dbg.h @@ -0,0 +1,41 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. 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. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Defines and macros for the debug util of the socket and + * net NIF(s). + * ---------------------------------------------------------------------- + * + */ + +#ifndef SOCKET_DBG_H__ +#define SOCKET_DBG_H__ + + +#define ESOCK_DBG_PRINTF( ___COND___ , proto ) \ + if ( ___COND___ ) { \ + esock_dbg_printf proto; \ + fflush(stdout); \ + } + + +extern +void esock_dbg_printf( const char* prefix, const char* format, ... ); + +#endif // SOCKET_DBG_H__ diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h new file mode 100644 index 0000000000..83794f0b95 --- /dev/null +++ b/erts/emulator/nifs/common/socket_int.h @@ -0,0 +1,193 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. 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. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Utility "stuff" for socket and net. + * ---------------------------------------------------------------------- + * + */ + +#ifndef SOCKET_INT_H__ +#define SOCKET_INT_H__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __WIN32__ + +/* All this just to replace sys/socket.h, netinet/in.h and sys/un.h??? */ +#define INCL_WINSOCK_API_TYPEDEFS 1 +#ifndef WINDOWS_H_INCLUDES_WINSOCK2_H +#include +#endif +#include +#include /* NEED VC 6.0 or higher */ +/* Visual studio 2008+: NTDDI_VERSION needs to be set for iphlpapi.h + * to define the right structures. It needs to be set to WINXP (or LONGHORN) + * for IPV6 to work and it's set lower by default, so we need to change it. + */ +#ifdef HAVE_SDKDDKVER_H +# include +# ifdef NTDDI_VERSION +# undef NTDDI_VERSION +# endif +# define NTDDI_VERSION NTDDI_WINXP +#endif +#include + +#else /* !__WIN32__ */ + +#include +#include +#ifdef HAVE_SYS_UN_H +#include +#endif + +#endif + +#include + +/* The general purpose sockaddr */ +typedef union { + /* General sockaddr */ + struct sockaddr in; + + /* IPv4 sockaddr */ + struct sockaddr_in in4; + + /* IPv6 sockaddr */ +#if defined(HAVE_IN6) && defined(AF_INET6) + struct sockaddr_in6 in6; +#endif + + /* Unix Domain Socket sockaddr */ +#if defined(HAVE_SYS_UN_H) + struct sockaddr_un un; +#endif + +} SocketAddress; + + +typedef unsigned int BOOLEAN_T; +#define TRUE 1 +#define FALSE 0 + + +/* Misc error strings */ +#define ESOCK_STR_EINVAL "einval" +#define ESOCK_STR_EAFNOSUPPORT "eafnosupport" + + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * Misc atoms + */ +extern ERL_NIF_TERM esock_atom_addr; +extern ERL_NIF_TERM esock_atom_any; +extern ERL_NIF_TERM esock_atom_dgram; +extern ERL_NIF_TERM esock_atom_error; +extern ERL_NIF_TERM esock_atom_family; +extern ERL_NIF_TERM esock_atom_flowinfo; +extern ERL_NIF_TERM esock_atom_inet; +extern ERL_NIF_TERM esock_atom_inet6; +extern ERL_NIF_TERM esock_atom_local; +extern ERL_NIF_TERM esock_atom_loopback; +extern ERL_NIF_TERM esock_atom_ok; +extern ERL_NIF_TERM esock_atom_path; +extern ERL_NIF_TERM esock_atom_port; +extern ERL_NIF_TERM esock_atom_raw; +extern ERL_NIF_TERM esock_atom_scope_id; +extern ERL_NIF_TERM esock_atom_seqpacket; +extern ERL_NIF_TERM esock_atom_stream; +extern ERL_NIF_TERM esock_atom_undefined; + + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * Error value (=reason) atoms + */ +extern ERL_NIF_TERM esock_atom_eagain; +extern ERL_NIF_TERM esock_atom_eafnosupport; +extern ERL_NIF_TERM esock_atom_einval; + + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * Various wrapper macros for enif functions + */ +#define MALLOC(SZ) enif_alloc((SZ)) +#define FREE(P) enif_free((P)) + +#define MKA(E,S) enif_make_atom((E), (S)) +#define MKBIN(E,B) enif_make_binary((E), (B)) +#define MKI(E,I) enif_make_int((E), (I)) +#define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) +#define MKEL(E) enif_make_list((E), 0) +#define MKMA(E,KA,VA,L,M) enif_make_map_from_arrays((E), (KA), (VA), (L), (M)) +#define MKREF(E) enif_make_ref((E)) +#define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) +#define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) +#define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) +#define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) +#define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) +#define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) +#define MKT5(E,E1,E2,E3,E4,E5) \ + enif_make_tuple5((E), (E1), (E2), (E3), (E4), (E5)) +#define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ + enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) +#define MKTA(E, A, AL) enif_make_tuple_from_array((E), (A), (AL)) + +#define MCREATE(N) enif_mutex_create((N)) +#define MDESTROY(M) enif_mutex_destroy((M)) +#define MLOCK(M) enif_mutex_lock((M)) +#define MUNLOCK(M) enif_mutex_unlock((M)) + +#define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) +#define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) + +#define SELECT(E,FD,M,O,P,R) \ + 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_LIST(E, TE) enif_is_list((E), (TE)) +#define IS_MAP(E, TE) enif_is_map((E), (TE)) +#define IS_NUM(E, TE) enif_is_number((E), (TE)) +#define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) + +#define GET_ATOM_LEN(E, TE, LP) \ + enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) +#define GET_ATOM(E, TE, BP, MAX) \ + enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) +#define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) +#define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) +#define GET_LIST_ELEM(E, L, HP, TP) enif_get_list_cell((E), (L), (HP), (TP)) +#define GET_LIST_LEN(E, L, LP) enif_get_list_length((E), (L), (LP)) +#define GET_STR(E, L, B, SZ) \ + enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) +#define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) +#define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) +#define GET_MAP_VAL(E, M, K, V) enif_get_map_value((E), (M), (K), (V)) + +#define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) +#define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) + + +#endif // SOCKET_INT_H__ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index badaa8d988..335d773f89 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -173,6 +173,9 @@ #include +#include "socket_dbg.h" +#include "socket_int.h" +#include "socket_util.h" /* All platforms fail on malloc errors. */ #define FATAL_MALLOC @@ -399,66 +402,61 @@ typedef union { * * * =================================================================== */ -#define MALLOC(SZ) enif_alloc((SZ)) -#define FREE(P) enif_free((P)) - -#define MKA(E,S) enif_make_atom((E), (S)) -#define MKBIN(E,B) enif_make_binary((E), (B)) -#define MKI(E,I) enif_make_int((E), (I)) -#define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) -#define MKMA(E,KA,VA,L,M) enif_make_map_from_arrays((E), (KA), (VA), (L), (M)) -#define MKREF(E) enif_make_ref((E)) -#define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) -#define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) -#define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) -#define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) -#define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) -#define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) -#define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ - enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) - -#define MCREATE(N) enif_mutex_create((N)) -#define MDESTROY(M) enif_mutex_destroy((M)) -#define MLOCK(M) enif_mutex_lock((M)) -#define MUNLOCK(M) enif_mutex_unlock((M)) - -#define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) -#define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) - -#define SELECT(E,FD,M,O,P,R) \ - 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_MAP(E, TE) enif_is_map((E), (TE)) -#define IS_NUM(E, TE) enif_is_number((E), (TE)) -#define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) - -#define GET_ATOM_LEN(E, TE, LP) \ - enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) -#define GET_ATOM(E, TE, BP, MAX) \ - enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) -#define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) -#define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) -#define GET_STR(E, L, B, SZ) \ - enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) -#define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) -#define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) - -#define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) -#define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) - - -#define SDEBUG( ___COND___ , proto ) \ - if ( ___COND___ ) { \ - dbg_printf proto; \ - fflush(stdout); \ - } -#define SGDBG( proto ) SDEBUG( data.dbg , proto ) -#define SSDBG( __D__ , proto ) SDEBUG( (__D__)->dbg , proto ) +/* #define MALLOC(SZ) enif_alloc((SZ)) */ +/* #define FREE(P) enif_free((P)) */ + +/* #define MKA(E,S) enif_make_atom((E), (S)) */ +/* #define MKBIN(E,B) enif_make_binary((E), (B)) */ +/* #define MKI(E,I) enif_make_int((E), (I)) */ +/* #define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) */ +/* #define MKMA(E,KA,VA,L,M) enif_make_map_from_arrays((E), (KA), (VA), (L), (M)) */ +/* #define MKREF(E) enif_make_ref((E)) */ +/* #define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) */ +/* #define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) */ +/* #define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) */ +/* #define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) */ +/* #define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) */ +/* #define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) */ +/* #define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ */ +/* enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) */ + +/* #define MCREATE(N) enif_mutex_create((N)) */ +/* #define MDESTROY(M) enif_mutex_destroy((M)) */ +/* #define MLOCK(M) enif_mutex_lock((M)) */ +/* #define MUNLOCK(M) enif_mutex_unlock((M)) */ + +/* #define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) */ +/* #define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) */ + +/* #define SELECT(E,FD,M,O,P,R) \ */ +/* 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_MAP(E, TE) enif_is_map((E), (TE)) */ +/* #define IS_NUM(E, TE) enif_is_number((E), (TE)) */ +/* #define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) */ + +/* #define GET_ATOM_LEN(E, TE, LP) \ */ +/* enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) */ +/* #define GET_ATOM(E, TE, BP, MAX) \ */ +/* enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) */ +/* #define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) */ +/* #define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) */ +/* #define GET_STR(E, L, B, SZ) \ */ +/* enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) */ +/* #define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) */ +/* #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) */ + +/* #define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) */ +/* #define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) */ + + +#define SGDBG( proto ) ESOCK_DBG_PRINTF( data.dbg , proto ) +#define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto ) /* =================================================================== * @@ -548,24 +546,28 @@ static unsigned long one_value = 1; #define SOCKOPTLEN_T SOCKLEN_T #endif -/* The general purpose sockaddr */ +/* The general purpose sockaddr * / typedef union { - struct sockaddr sa; - struct sockaddr_in sai; + struct sockaddr in; + struct sockaddr_in in4; #ifdef HAVE_IN6 - struct sockaddr_in6 sai6; + struct sockaddr_in6 in6; #endif #ifdef HAVE_SYS_UN_H - struct sockaddr_un sal; + struct sockaddr_un un; #endif + } SocketAddress; +*/ +/* We can use the IPv4 def for this since the beginning + * is the same for INET and INET6 */ #define which_address_port(sap) \ - ((((sap)->sai.sin_family == AF_INET) || \ - ((sap)->sai.sin_family == AF_INET6)) ? \ - ((sap)->sai.sin_port) : -1) + ((((sap)->in4.sin_family == AF_INET) || \ + ((sap)->in4.sin_family == AF_INET6)) ? \ + ((sap)->in4.sin_port) : -1) typedef struct { @@ -706,8 +708,6 @@ typedef struct { * ---------------------------------------------------------------------- */ -/* THIS IS JUST TEMPORARY */ -extern char* erl_errno_id(int error); static ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, @@ -1313,12 +1313,6 @@ static void socket_down(ErlNifEnv* env, const ErlNifPid* pid, const ErlNifMonitor* mon); -static ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM val); -static ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2); -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); @@ -1341,10 +1335,6 @@ static void xabort(const char* expr, const char* file, int line); -static void dbg_printf( const char* format, ... ); -static int dbg_realtime(struct timespec* tsP); -static int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts); - static BOOLEAN_T extract_item_on_load(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, @@ -1384,20 +1374,18 @@ static const struct in6_addr in6addr_loopback = /* *** String constants *** */ -static char str_any[] = "any"; +// static char str_any[] = "any"; static char str_close[] = "close"; static char str_closed[] = "closed"; static char str_closing[] = "closing"; static char str_debug[] = "debug"; -static char str_error[] = "error"; static char str_false[] = "false"; static char str_global_counters[] = "global_counters"; static char str_in4_sockaddr[] = "in4_sockaddr"; static char str_in6_sockaddr[] = "in6_sockaddr"; static char str_iow[] = "iow"; -static char str_loopback[] = "loopback"; +// 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_num_dlocal[] = "num_domain_local"; static char str_num_dinet[] = "num_domain_inet"; @@ -1412,7 +1400,6 @@ static char str_num_tseqpkgs[] = "num_type_seqpacket"; static char str_num_tstreams[] = "num_type_stream"; 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"; @@ -1434,19 +1421,37 @@ static char str_exself[] = "exself"; // failed self static char str_exsend[] = "exsend"; // failed send +/* *** "Global" Atoms *** */ +ERL_NIF_TERM esock_atom_addr; +ERL_NIF_TERM esock_atom_any; +ERL_NIF_TERM esock_atom_dgram; +ERL_NIF_TERM esock_atom_error; +ERL_NIF_TERM esock_atom_family; +ERL_NIF_TERM esock_atom_flowinfo; +ERL_NIF_TERM esock_atom_inet; +ERL_NIF_TERM esock_atom_inet6; +ERL_NIF_TERM esock_atom_local; +ERL_NIF_TERM esock_atom_loopback; +ERL_NIF_TERM esock_atom_ok; +ERL_NIF_TERM esock_atom_path; +ERL_NIF_TERM esock_atom_port; +ERL_NIF_TERM esock_atom_raw; +ERL_NIF_TERM esock_atom_scope_id; +ERL_NIF_TERM esock_atom_seqpacket; +ERL_NIF_TERM esock_atom_stream; +ERL_NIF_TERM esock_atom_undefined; + + /* *** 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_debug; -static ERL_NIF_TERM atom_error; static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_global_counters; static ERL_NIF_TERM atom_in4_sockaddr; static ERL_NIF_TERM atom_in6_sockaddr; static ERL_NIF_TERM atom_iow; -static ERL_NIF_TERM atom_loopback; static ERL_NIF_TERM atom_nif_abort; static ERL_NIF_TERM atom_num_dinet; static ERL_NIF_TERM atom_num_dinet6; @@ -1459,11 +1464,9 @@ static ERL_NIF_TERM atom_num_sockets; static ERL_NIF_TERM atom_num_tdgrams; static ERL_NIF_TERM atom_num_tseqpkgs; static ERL_NIF_TERM atom_num_tstreams; -static ERL_NIF_TERM atom_ok; static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; -static ERL_NIF_TERM atom_undefined; static ERL_NIF_TERM atom_lowdelay; static ERL_NIF_TERM atom_throughput; @@ -1629,7 +1632,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, ERL_NIF_TERM emap; ERL_NIF_TERM result; - SGDBG( ("nif_open -> entry with %d args\r\n", argc) ); + SGDBG( ("SOCKET", "nif_open -> entry with %d args\r\n", argc) ); /* Extract arguments and perform preliminary validation */ @@ -1642,7 +1645,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, } emap = argv[3]; - SGDBG( ("nif_open -> " + SGDBG( ("SOCKET", "nif_open -> " "\r\n edomain: %T" "\r\n etype: %T" "\r\n eproto: %T" @@ -1668,7 +1671,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, result = nopen(env, domain, type, proto, netns); - SGDBG( ("nif_open -> done with result: " + SGDBG( ("SOCKET", "nif_open -> done with result: " "\r\n %T" "\r\n", result) ); @@ -1698,7 +1701,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, int current_ns; #endif - SGDBG( ("nopen -> entry with" + SGDBG( ("SOCKET", "nopen -> entry with" "\r\n domain: %d" "\r\n type: %d" "\r\n protocol: %d" @@ -1708,16 +1711,16 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, #ifdef HAVE_SETNS if ((netns != NULL) && !change_network_namespace(netns, ¤t_ns, &save_errno)) - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); #endif if ((sock = sock_open(domain, type, protocol)) == INVALID_SOCKET) - return make_error2(env, sock_errno()); + return esock_make_error_errno(env, sock_errno()); #ifdef HAVE_SETNS if ((netns != NULL) && !restore_network_namespace(current_ns, sock, &save_errno)) - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); if (netns != NULL) FREE(netns); @@ -1727,7 +1730,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, if ((event = sock_create_event(sock)) == INVALID_EVENT) { save_errno = sock_errno(); while ((sock_close(sock) == INVALID_SOCKET) && (sock_errno() == EINTR)); - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); } @@ -1756,12 +1759,12 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, * of environment... */ if (enif_self(env, &descP->ctrlPid) == NULL) - return make_error(env, atom_exself); + return esock_make_error(env, atom_exself); if (MONP(env, descP, &descP->ctrlPid, &descP->ctrlMon) > 0) - return make_error(env, atom_exmon); + return esock_make_error(env, atom_exmon); #ifdef __WIN32__ @@ -1779,11 +1782,11 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, SELECT(env, event, (ERL_NIF_SELECT_READ), - descP, NULL, atom_undefined); + descP, NULL, esock_atom_undefined); #endif - return make_ok2(env, res); + return esock_make_ok2(env, res); } @@ -1802,7 +1805,7 @@ BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err) int current_ns = 0; int new_ns = 0; - SGDBG( ("change_network_namespace -> entry with" + SGDBG( ("SOCKET", "change_network_namespace -> entry with" "\r\n new ns: %s", netns) ); if (netns != NULL) { @@ -1853,7 +1856,7 @@ BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err) { int save_errno; - SGDBG( ("restore_network_namespace -> entry with" + SGDBG( ("SOCKET", "restore_network_namespace -> entry with" "\r\n ns: %d", ns) ); if (ns != INVALID_SOCKET) { @@ -1920,7 +1923,7 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, } SSDBG( descP, - ("nif_bind -> " + ("SOCKET", "nif_bind -> " "\r\n Socket: %T" "\r\n Addr: %T" "\r\n", argv[0], argv[1]) ); @@ -1943,7 +1946,7 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, */ /* WHY NOT !IS_OPEN(...) */ if (descP->state != SOCKET_STATE_OPEN) - return make_error(env, atom_exbadstate); + return esock_make_error(env, atom_exbadstate); return nbind(env, descP, argv[1]); } @@ -1960,34 +1963,34 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, int port; SSDBG( descP, - ("nbind -> entry with" + ("SOCKET", "nbind -> entry with" "\r\n addr: %T" "\r\n", addr) ); if ((xerr = decode_laddress(env, descP->domain, addr, &local, &addrLen)) != NULL) - return make_error1(env, xerr); + return esock_make_error_str(env, xerr); - SSDBG( descP, ("nbind -> try bind\r\n") ); + SSDBG( descP, ("SOCKET", "nbind -> try bind\r\n") ); if (IS_SOCKET_ERROR(sock_bind(descP->sock, (struct sockaddr*) &local, addrLen))) { - return make_error2(env, sock_errno()); + return esock_make_error_errno(env, sock_errno()); } port = which_address_port(&local); if (port == 0) { SOCKLEN_T len = sizeof(local); sys_memzero((char *) &local, len); - sock_name(descP->sock, &local.sa, &len); + sock_name(descP->sock, &local.in, &len); port = which_address_port(&local); } else if (port == -1) { port = 0; } - SSDBG( descP, ("nbind -> done with port = %d\r\n", port) ); + SSDBG( descP, ("SOCKET", "nbind -> done with port = %d\r\n", port) ); - return make_ok2(env, MKI(env, port)); + return esock_make_ok2(env, MKI(env, port)); } @@ -2050,15 +2053,15 @@ char* decode_laddress_binary(ErlNifEnv* env, #else 1 #endif - ) > sizeof(localP->sal.sun_path)) + ) > sizeof(localP->un.sun_path)) return str_einval; sys_memzero((char*)localP, sizeof(struct sockaddr_un)); - localP->sal.sun_family = domain; - sys_memcpy(localP->sal.sun_path, bin.data, bin.size); + localP->un.sun_family = domain; + sys_memcpy(localP->un.sun_path, bin.data, bin.size); addrLen = offsetof(struct sockaddr_un, sun_path) + bin.size; #ifndef NO_SA_LEN - localP->u.sal.sun_len = addrLen; + localP->un.sun_len = addrLen; #endif *addrLenP = addrLen; return NULL; @@ -2103,7 +2106,7 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, if ((xres = decode_in_sockaddr(env, eSockAddr, &descP->remote, &descP->addrLen)) != NULL) { - return make_error1(env, xres); + return esock_make_error_str(env, xres); } return nconnect(env, descP); @@ -2119,13 +2122,13 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, /* Verify that we are where in the proper state */ if (!IS_OPEN(descP)) - return make_error(env, atom_exbadstate); + return esock_make_error(env, atom_exbadstate); if (IS_CONNECTED(descP)) - return make_error(env, atom_eisconn); + return esock_make_error(env, atom_eisconn); if (IS_CONNECTING(descP)) - return make_error(env, atom_einval); + return esock_make_error(env, atom_einval); code = sock_connect(descP->sock, (struct sockaddr*) &descP->remote, @@ -2140,15 +2143,15 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, descP->sock, (ERL_NIF_SELECT_WRITE), descP, NULL, ref); - return make_ok2(env, ref); + return esock_make_ok2(env, ref); } else if (code == 0) { /* ok we are connected */ descP->state = SOCKET_STATE_CONNECTED; /* Do we need to do somthing for "active" mode? * Is there even such a thing *here*? */ - return atom_ok; + return esock_atom_ok; } else { - return make_error2(env, sock_errno()); + return esock_make_error_errno(env, sock_errno()); } } @@ -2192,16 +2195,16 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, int error; if (descP->state != SOCKET_STATE_CONNECTING) - return make_error(env, atom_enotconn); + return esock_make_error(env, atom_enotconn); if (!verify_is_connected(descP, &error)) { descP->state = SOCKET_STATE_OPEN; /* restore state */ - return make_error2(env, error); + return esock_make_error_errno(env, error); } descP->state = SOCKET_STATE_CONNECTED; - return atom_ok; + return esock_atom_ok; } @@ -2294,17 +2297,17 @@ ERL_NIF_TERM nlisten(ErlNifEnv* env, int backlog) { if (descP->state == SOCKET_STATE_CLOSED) - return make_error(env, atom_exbadstate); + return esock_make_error(env, atom_exbadstate); if (!IS_OPEN(descP)) - return make_error(env, atom_exbadstate); + return esock_make_error(env, atom_exbadstate); if (IS_SOCKET_ERROR(sock_listen(descP->sock, backlog))) - return make_error2(env, sock_errno()); + return esock_make_error_errno(env, sock_errno()); descP->state = SOCKET_STATE_LISTENING; - return atom_ok; + return esock_atom_ok; } @@ -2361,7 +2364,7 @@ ERL_NIF_TERM naccept(ErlNifEnv* env, break; default: - res = make_error(env, atom_einval); + res = esock_make_error(env, atom_einval); break; } @@ -2385,7 +2388,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, ErlNifPid caller; if (enif_self(env, &caller) == NULL) - return make_error(env, atom_exself); + return esock_make_error(env, atom_exself); n = sizeof(remote); sys_memzero((char *) &remote, n); @@ -2400,7 +2403,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, if (MONP(env, descP, &descP->currentAcceptor.pid, &descP->currentAcceptor.mon) > 0) - return make_error(env, atom_exmon); + return esock_make_error(env, atom_exmon); descP->currentAcceptor.ref = ref; @@ -2434,10 +2437,10 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, descP->state = SOCKET_STATE_ACCEPTING; - return make_error(env, atom_eagain); + return esock_make_error(env, atom_eagain); } else { - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); } } else { @@ -2452,7 +2455,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, save_errno = sock_errno(); while ((sock_close(accSock) == INVALID_SOCKET) && (sock_errno() == EINTR)); - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); } if ((accDescP = alloc_descriptor(accSock, accEvent)) == NULL) { @@ -2472,7 +2475,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, &accDescP->ctrlPid, &accDescP->ctrlMon) > 0) { sock_close(accSock); - return make_error(env, atom_exmon); + return esock_make_error(env, atom_exmon); } accDescP->remote = remote; @@ -2483,12 +2486,12 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), - descP, NULL, atom_undefined); + descP, NULL, esock_atom_undefined); #endif accDescP->state = SOCKET_STATE_CONNECTED; - return make_ok2(env, accRef); + return esock_make_ok2(env, accRef); } } @@ -2510,7 +2513,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, int save_errno; if (enif_self(env, &caller) == NULL) - return make_error(env, atom_exself); + return esock_make_error(env, atom_exself); if (compare_pids(env, &descP->currentAcceptor.pid, &caller) != 0) { /* This will have to do until we implement the queue. @@ -2518,7 +2521,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, * and instead return with eagain (the caller will then wait * for the select message). */ - return make_error(env, atom_exbusy); + return esock_make_error(env, atom_exbusy); } n = sizeof(descP->remote); @@ -2537,9 +2540,9 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, (ERL_NIF_SELECT_READ), descP, NULL, ref); - return make_error(env, atom_eagain); + return esock_make_error(env, atom_eagain); } else { - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); } } else { SocketDescriptor* accDescP; @@ -2553,7 +2556,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, save_errno = sock_errno(); while ((sock_close(accSock) == INVALID_SOCKET) && (sock_errno() == EINTR)); - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); } if ((accDescP = alloc_descriptor(accSock, accEvent)) == NULL) { @@ -2573,7 +2576,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, &accDescP->ctrlPid, &accDescP->ctrlMon) > 0) { sock_close(accSock); - return make_error(env, atom_exmon); + return esock_make_error(env, atom_exmon); } accDescP->remote = remote; @@ -2584,7 +2587,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), - descP, NULL, atom_undefined); + descP, NULL, esock_atom_undefined); #endif accDescP->state = SOCKET_STATE_CONNECTED; @@ -2596,7 +2599,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, */ descP->state = SOCKET_STATE_LISTENING; - return make_ok2(env, accRef); + return esock_make_ok2(env, accRef); } } @@ -2638,7 +2641,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, sendRef = argv[1]; if (!IS_CONNECTED(descP)) - return make_error(env, atom_enotconn); + return esock_make_error(env, atom_enotconn); if (!esendflags2sendflags(eflags, &flags)) return enif_make_badarg(env); @@ -2739,15 +2742,15 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, /* THIS TEST IS NOT CORRECT!!! */ if (!IS_OPEN(descP)) - return make_error(env, atom_einval); + return esock_make_error(env, atom_einval); if (!esendflags2sendflags(eflags, &flags)) - return make_error(env, atom_einval); + return esock_make_error(env, atom_einval); if ((xres = decode_in_sockaddr(env, eSockAddr, &remoteAddr, &remoteAddrLen)) != NULL) - return make_error1(env, xres); + return esock_make_error_str(env, xres); return nsendto(env, descP, sendRef, &data, flags, &remoteAddr, remoteAddrLen); } @@ -2775,7 +2778,7 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, if (toAddrP != NULL) { written = sock_sendto(descP->sock, dataP->data, dataP->size, flags, - &toAddrP->sa, toAddrLen); + &toAddrP->in, toAddrLen); } else { written = sock_sendto(descP->sock, dataP->data, dataP->size, flags, @@ -2893,7 +2896,7 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, recvRef = argv[1]; if (!IS_CONNECTED(descP)) - return make_error(env, atom_enotconn); + return esock_make_error(env, atom_enotconn); if (!erecvflags2recvflags(eflags, &flags)) return enif_make_badarg(env); @@ -2945,7 +2948,7 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, * size (what has been configured). */ if (!ALLOC_BIN((len ? len : descP->rBufSz), &buf)) - return make_error(env, atom_exalloc); + return esock_make_error(env, atom_exalloc); /* We ignore the wrap for the moment. * Maybe we should issue a wrap-message to controlling process... @@ -2999,7 +3002,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, recvRef = argv[1]; /* if (IS_OPEN(descP)) */ - /* return make_error(env, atom_enotconn); */ + /* return esock_make_error(env, atom_enotconn); */ if (!erecvflags2recvflags(eflags, &flags)) return enif_make_badarg(env); @@ -3055,7 +3058,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, * size (what has been configured). */ if (!ALLOC_BIN((bufSz ? bufSz : descP->rBufSz), &buf)) - return make_error(env, atom_exalloc); + return esock_make_error(env, atom_exalloc); /* We ignore the wrap for the moment. * Maybe we should issue a wrap-message to controlling process... @@ -3066,7 +3069,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, sys_memzero((char*) &fromAddr, addrLen); read = sock_recvfrom(descP->sock, buf.data, buf.size, flags, - &fromAddr.sa, &addrLen); + &fromAddr.in, &addrLen); return recvfrom_check_result(env, descP, read, @@ -3129,7 +3132,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, if (enif_self(env, &descP->closerPid) == NULL) { MUNLOCK(descP->closeMtx); - return make_error(env, atom_exself); + return esock_make_error(env, atom_exself); } /* Monitor the caller, since we should complete this operation even if @@ -3140,7 +3143,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, &descP->closerPid, &descP->closerMon) > 0) { MUNLOCK(descP->closeMtx); - return make_error(env, atom_exmon); + return esock_make_error(env, atom_exmon); } descP->closeLocal = TRUE; @@ -3156,11 +3159,11 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, descP, NULL, descP->closeRef); if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { /* Prep done - inform the caller it can finalize (close) directly */ - reply = atom_ok; + reply = esock_atom_ok; } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { /* The stop callback function has been *scheduled* which means that we * have to wait for it to complete. */ - reply = make_ok2(env, descP->closeRef); + reply = esock_make_ok2(env, descP->closeRef); } else { /* * @@ -3172,10 +3175,10 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, * */ reason = MKT2(env, atom_select, MKI(env, selectRes)); - reply = make_error(env, reason); + reply = esock_make_error(env, reason); } } else { - reply = make_error(env, reason); + reply = esock_make_error(env, reason); } return reply; @@ -3222,10 +3225,10 @@ ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, ERL_NIF_TERM reply; if (descP->state == SOCKET_STATE_CLOSED) - return atom_ok; + return esock_atom_ok; if (descP->state != SOCKET_STATE_CLOSING) - return make_error(env, atom_enotclosing); + return esock_make_error(env, atom_enotclosing); /* This nif is executed in a dirty scheduler just so that * it can "hang" (whith minumum effect on the VM) while the @@ -3242,12 +3245,12 @@ ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, /* Not all data in the buffers where sent, * make sure the caller gets this. */ - reply = make_error(env, atom_timeout); + reply = esock_make_error(env, atom_timeout); } else { - reply = make_error2(env, save_errno); + reply = esock_make_error_errno(env, save_errno); } } else { - reply = atom_ok; + reply = esock_atom_ok; } sock_close_event(descP->event); @@ -3315,9 +3318,9 @@ ERL_NIF_TERM nshutdown(ErlNifEnv* env, descP->isWritable = FALSE; break; } - reply = atom_ok; + reply = esock_atom_ok; } else { - reply = make_error2(env, sock_errno()); + reply = esock_make_error_errno(env, sock_errno()); } return reply; @@ -3366,10 +3369,10 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, eVal = argv[4]; if (!decode_bool(env, eIsEncoded, &isEncoded)) - return make_error(env, atom_einval); + return esock_make_error(env, atom_einval); if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) - return make_error(env, atom_einval); + return esock_make_error(env, atom_einval); return nsetopt(env, descP, isEncoded, isOTP, level, eOpt, eVal); } @@ -3422,7 +3425,7 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, break; default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -3440,9 +3443,9 @@ ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env, ERL_NIF_TERM result; if (decode_bool(env, eVal, &descP->dbg)) { - result = atom_ok; + result = esock_atom_ok; } else { - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); } return result; @@ -3459,9 +3462,9 @@ ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, ERL_NIF_TERM result; if (decode_bool(env, eVal, &descP->iow)) { - result = atom_ok; + result = esock_atom_ok; } else { - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); } return result; @@ -3486,11 +3489,11 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, opt, val.data, val.size); if (res != 0) - result = make_error2(env, res); + result = esock_make_error_errno(env, res); else - result = atom_ok; + result = esock_atom_ok; } else { - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); } return result; @@ -3543,7 +3546,7 @@ ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -3612,7 +3615,7 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -3667,11 +3670,11 @@ ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, int res = socket_setopt(descP->sock, SOL_SOCKET, SO_LINGER, (void*) &val, optLen); if (res != 0) - result = make_error2(env, res); + result = esock_make_error_errno(env, res); else - result = atom_ok; + result = esock_atom_ok; } else { - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); } return result; @@ -3760,7 +3763,7 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -3826,12 +3829,12 @@ ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, IP_TOS, &val, sizeof(val)); if (res != 0) - result = make_error2(env, res); + result = esock_make_error_errno(env, res); else - result = atom_ok; + result = esock_atom_ok; } else { - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); } return result; @@ -3878,7 +3881,7 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -3931,7 +3934,7 @@ ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -3999,7 +4002,7 @@ ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -4047,7 +4050,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -4105,12 +4108,12 @@ ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, opt, &val, optLen); if (res != 0) - result = make_error2(env, res); + result = esock_make_error_errno(env, res); else - result = atom_ok; + result = esock_atom_ok; } else { - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); } FREE(val); @@ -4136,12 +4139,12 @@ ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, opt, &ival, sizeof(ival)); if (res != 0) - result = make_error2(env, res); + result = esock_make_error_errno(env, res); else - result = atom_ok; + result = esock_atom_ok; } else { - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); } return result; @@ -4164,12 +4167,12 @@ ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, opt, &val, sizeof(val)); if (res != 0) - result = make_error2(env, res); + result = esock_make_error_errno(env, res); else - result = atom_ok; + result = esock_atom_ok; } else { - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); } return result; @@ -4376,10 +4379,10 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, eIsEncoded = argv[1]; if (!decode_bool(env, eIsEncoded, &isEncoded)) - return make_error(env, atom_einval); + return esock_make_error(env, atom_einval); if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) - return make_error(env, atom_einval); + return esock_make_error(env, atom_einval); return ngetopt(env, descP, isEncoded, isOTP, level, eOpt); } @@ -4431,7 +4434,7 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, break; default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -4449,7 +4452,7 @@ ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, encode_bool(descP->dbg, &eVal); - return make_ok2(env, eVal); + return esock_make_ok2(env, eVal); } @@ -4463,7 +4466,7 @@ ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, encode_bool(descP->iow, &eVal); - return make_ok2(env, eVal); + return esock_make_ok2(env, eVal); } @@ -4501,11 +4504,11 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, result = ngetopt_bool_opt(env, descP, level, opt); break; default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } } else { - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); } return result; @@ -4525,20 +4528,20 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, if (valueSz == 0) { res = sock_getopt(descP->sock, level, opt, NULL, NULL); if (res != 0) - result = make_error2(env, res); + result = esock_make_error_errno(env, res); else - result = atom_ok; + result = esock_atom_ok; } else { ErlNifBinary val; if (ALLOC_BIN(valueSz, &val)) { res = sock_getopt(descP->sock, level, opt, val.data, &valueSz); if (res != 0) { - result = make_error2(env, res); + result = esock_make_error_errno(env, res); } else { if (valueSz < val.size) { if (REALLOC_BIN(&val, valueSz)) { - result = make_ok2(env, MKBIN(env, &val)); + result = esock_make_ok2(env, MKBIN(env, &val)); } else { result = enif_make_badarg(env); } @@ -4598,7 +4601,7 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -4665,7 +4668,7 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -4719,13 +4722,13 @@ ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, &val, &valSz); if (res != 0) { - result = make_error2(env, res); + result = esock_make_error_errno(env, res); } else { ERL_NIF_TERM lOnOff = MKI(env, val.l_onoff); ERL_NIF_TERM lSecs = MKI(env, val.l_linger); ERL_NIF_TERM linger = MKT2(env, lOnOff, lSecs); - result = make_ok2(env, linger); + result = esock_make_ok2(env, linger); } return result; @@ -4808,7 +4811,7 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -4872,7 +4875,7 @@ ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, res = sock_getopt(descP->sock, level, IP_TOS, &val, &valSz); if (res != 0) { - result = make_error2(env, res); + result = esock_make_error_errno(env, res); } else { result = encode_ip_tos(env, val); } @@ -4919,7 +4922,7 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -4970,7 +4973,7 @@ ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -5034,7 +5037,7 @@ ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -5079,7 +5082,7 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, #endif default: - result = make_error(env, atom_einval); + result = esock_make_error(env, atom_einval); break; } @@ -5132,11 +5135,11 @@ ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { - result = make_error2(env, res); + result = esock_make_error_errno(env, res); } else { ERL_NIF_TERM sval = MKSL(env, val, valSz); - result = make_ok2(env, sval); + result = esock_make_ok2(env, sval); } FREE(val); @@ -5161,11 +5164,11 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { - result = make_error2(env, res); + result = esock_make_error_errno(env, res); } else { ERL_NIF_TERM bval = ((val) ? atom_true : atom_false); - result = make_ok2(env, bval); + result = esock_make_ok2(env, bval); } return result; @@ -5188,9 +5191,9 @@ ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { - result = make_error2(env, res); + result = esock_make_error_errno(env, res); } else { - result = make_ok2(env, MKI(env, val)); + result = esock_make_ok2(env, MKI(env, val)); } return result; @@ -5215,7 +5218,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, cnt_inc(&descP->writePkgCnt, 1); cnt_inc(&descP->writeByteCnt, written); - return atom_ok; + return esock_atom_ok; } else if (written < 0) { @@ -5226,7 +5229,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, cnt_inc(&descP->writeFails, 1); - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); } else { @@ -5251,7 +5254,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), descP, NULL, sendRef); - return make_ok2(env, enif_make_int(env, written)); + return esock_make_ok2(env, enif_make_int(env, written)); } @@ -5295,7 +5298,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, data = MKBIN(env, bufP); - return make_ok3(env, atom_false, data); + return esock_make_ok3(env, atom_false, data); } else { @@ -5308,7 +5311,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, data = MKBIN(env, bufP); - return make_ok3(env, atom_true, data); + return esock_make_ok3(env, atom_true, data); } @@ -5345,13 +5348,13 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, (ERL_NIF_SELECT_STOP), descP, NULL, recvRef); - return make_error(env, atom_closed); + return esock_make_error(env, atom_closed); } else if ((save_errno == ERRNO_BLOCK) || (save_errno == EAGAIN)) { - return make_error(env, atom_eagain); + return esock_make_error(env, atom_eagain); } else { - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); } } else { @@ -5369,14 +5372,14 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, data = MKBIN(env, bufP); data = MKSBIN(env, data, 0, read); - return make_ok3(env, atom_true, data); + return esock_make_ok3(env, atom_true, data); } else { /* +++ We got only a part of what was expected +++ * +++ => receive more later. +++ */ - return make_ok3(env, atom_false, MKBIN(env, bufP)); + return esock_make_ok3(env, atom_false, MKBIN(env, bufP)); } } } @@ -5430,13 +5433,13 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, (ERL_NIF_SELECT_STOP), descP, NULL, recvRef); - return make_error(env, atom_closed); + return esock_make_error(env, atom_closed); } else if ((save_errno == ERRNO_BLOCK) || (save_errno == EAGAIN)) { - return make_error(env, atom_eagain); + return esock_make_error(env, atom_eagain); } else { - return make_error2(env, save_errno); + return esock_make_error_errno(env, save_errno); } } else { @@ -5463,7 +5466,7 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, data = MKSBIN(env, data, 0, read); } - return make_ok2(env, MKT3(env, fromDomainT, fromSourceT, data)); + return esock_make_ok2(env, MKT3(env, fromDomainT, fromSourceT, data)); } } @@ -5663,9 +5666,9 @@ char* decode_in4_sockaddr_atomaddr(ErlNifEnv* env, { struct in_addr addr; - if (COMPARE(atom_loopback, eAddr) == 0) { + if (COMPARE(esock_atom_loopback, eAddr) == 0) { addr.s_addr = sock_htonl(INADDR_LOOPBACK); - } else if (COMPARE(atom_any, eAddr) == 0) { + } else if (COMPARE(esock_atom_any, eAddr) == 0) { addr.s_addr = sock_htonl(INADDR_ANY); } else { return str_einval; @@ -5673,11 +5676,11 @@ char* decode_in4_sockaddr_atomaddr(ErlNifEnv* env, sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); #ifndef NO_SA_LEN - sockAddrP->sai.sin_len = sizeof(struct sockaddr_in6); + sockAddrP->sai.sin_len = sizeof(struct sockaddr_in); #endif - sockAddrP->sai.sin_family = AF_INET; - sockAddrP->sai.sin_port = sock_htons(port); - sockAddrP->sai.sin_addr.s_addr = addr.s_addr; + sockAddrP->in4.sin_family = AF_INET; + sockAddrP->in4.sin_port = sock_htons(port); + sockAddrP->in4.sin_addr.s_addr = addr.s_addr; *addrLenP = sizeof(struct sockaddr_in); return NULL; @@ -5708,16 +5711,16 @@ char* decode_in4_sockaddr_addr(ErlNifEnv* env, sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in)); #ifndef NO_SA_LEN - sockAddrP->sai.sin_len = sizeof(struct sockaddr_in); + sockAddrP->in4.sin_len = sizeof(struct sockaddr_in); #endif - sockAddrP->sai.sin_family = AF_INET; - sockAddrP->sai.sin_port = sock_htons(port); + sockAddrP->in4.sin_family = AF_INET; + sockAddrP->in4.sin_port = sock_htons(port); for (a = 0; a < 4; a++) { if (!GET_INT(env, ip4AddrT[a], &v)) return str_einval; addr[a] = v; } - sys_memcpy(&sockAddrP->sai.sin_addr, &addr, sizeof(addr)); + sys_memcpy(&sockAddrP->in4.sin_addr, &addr, sizeof(addr)); *addrLenP = sizeof(struct sockaddr_in); return NULL; @@ -5789,9 +5792,9 @@ char* decode_in6_sockaddr_atomaddr(ErlNifEnv* env, { const struct in6_addr* addr; - if (COMPARE(atom_loopback, eAddr) == 0) { + if (COMPARE(esock_atom_loopback, eAddr) == 0) { addr = &in6addr_loopback; - } else if (COMPARE(atom_any, eAddr) == 0) { + } else if (COMPARE(esock_atom_any, eAddr) == 0) { addr = &in6addr_any; } else { return str_einval; @@ -5799,13 +5802,13 @@ char* decode_in6_sockaddr_atomaddr(ErlNifEnv* env, sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN - sockAddrP->sai6.sin6_len = sizeof(struct sockaddr_in6); + sockAddrP->in6.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; + sockAddrP->in6.sin6_family = AF_INET6; + sockAddrP->in6.sin6_port = sock_htons(port); + sockAddrP->in6.sin6_flowinfo = flowInfo; + sockAddrP->in6.sin6_scope_id = scopeId; + sockAddrP->in6.sin6_addr = *addr; *addrLenP = sizeof(struct sockaddr_in6); return NULL; @@ -5839,12 +5842,12 @@ char* decode_in6_sockaddr_addr(ErlNifEnv* env, sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN - sockAddrP->sai6.sin6_len = sizeof(struct sockaddr_in6); + sockAddrP->in6.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->in6.sin6_family = AF_INET6; + sockAddrP->in6.sin6_port = sock_htons(port); + sockAddrP->in6.sin6_flowinfo = flowInfo; + sockAddrP->in6.sin6_scope_id = scopeId; /* The address tuple is of size 8 * and each element is a two byte integer */ @@ -5854,7 +5857,7 @@ char* decode_in6_sockaddr_addr(ErlNifEnv* env, addr[a*2 ] = ((v >> 8) & 0xFF); addr[a*2+1] = (v & 0xFF); } - sys_memcpy(&sockAddrP->sai6.sin6_addr, &addr, sizeof(addr)); + sys_memcpy(&sockAddrP->in6.sin6_addr, &addr, sizeof(addr)); *addrLenP = sizeof(struct sockaddr_in6); return NULL; @@ -5960,7 +5963,7 @@ void encode_address(ErlNifEnv* env, { short port; - switch (addrP->sa.sa_family) { + switch (addrP->in.sa_family) { /* +++ inet (IPv4) +++ */ @@ -5969,9 +5972,9 @@ void encode_address(ErlNifEnv* env, ERL_NIF_TERM addrT, portT; unsigned int i; ERL_NIF_TERM at4[4]; - char* a4 = (char*) &addrP->sai.sin_addr; + char* a4 = (char*) &addrP->in4.sin_addr; - port = sock_ntohs(addrP->sai.sin_port); + port = sock_ntohs(addrP->in4.sin_port); for (i = 0; i < 4; i++) { at4[i] = MKI(env, a4[i]); } @@ -5981,8 +5984,8 @@ void encode_address(ErlNifEnv* env, portT = MKI(env, port); *sourceT = MKT2(env, addrT, portT); } else { - *domainT = atom_undefined; - *sourceT = atom_undefined; + *domainT = esock_atom_undefined; + *sourceT = esock_atom_undefined; } break; @@ -5995,9 +5998,9 @@ void encode_address(ErlNifEnv* env, ERL_NIF_TERM addrT, portT; unsigned int i; ERL_NIF_TERM at6[8]; - char* a16 = (char*) &addrP->sai6.sin6_addr; + char* a16 = (char*) &addrP->in6.sin6_addr; - port = sock_ntohs(addrP->sai6.sin6_port); + port = sock_ntohs(addrP->in6.sin6_port); /* The address tuple is of size 8 * and each element is a two byte integer */ @@ -6013,8 +6016,8 @@ void encode_address(ErlNifEnv* env, portT = MKI(env, port); *sourceT = MKT2(env, addrT, portT); } else { - *domainT = atom_undefined; - *sourceT = atom_undefined; + *domainT = esock_atom_undefined; + *sourceT = esock_atom_undefined; } break; #endif @@ -6028,13 +6031,13 @@ void encode_address(ErlNifEnv* env, *domainT = MKA(env, "local"); if (addrLen < offsetof(struct sockaddr_un, sun_path)) { - *sourceT = atom_undefined; + *sourceT = esock_atom_undefined; } else { n = addrLen - offsetof(struct sockaddr_un, sun_path); if (255 < n) { - *sourceT = atom_undefined; + *sourceT = esock_atom_undefined; } else { - m = my_strnlen(addrP->sal.sun_path, n); + m = my_strnlen(addrP->un.sun_path, n); #ifdef __linux__ /* Assume that the address is a zero terminated string, * except when the first byte is \0 i.e the string length is 0, @@ -6047,7 +6050,7 @@ void encode_address(ErlNifEnv* env, } #endif - *sourceT = MKSL(env, addrP->sal.sun_path, m); + *sourceT = MKSL(env, addrP->un.sun_path, m); } } } @@ -6055,8 +6058,8 @@ void encode_address(ErlNifEnv* env, #endif default: - *domainT = atom_undefined; - *sourceT = atom_undefined; + *domainT = esock_atom_undefined; + *sourceT = esock_atom_undefined; break; } /* switch (addrP->sa.sa_family) */ @@ -6369,23 +6372,23 @@ ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val) switch (val) { case IPTOS_LOWDELAY: - result = make_ok2(env, atom_lowdelay); + result = esock_make_ok2(env, atom_lowdelay); break; case IPTOS_THROUGHPUT: - result = make_ok2(env, atom_throughput); + result = esock_make_ok2(env, atom_throughput); break; case IPTOS_RELIABILITY: - result = make_ok2(env, atom_reliability); + result = esock_make_ok2(env, atom_reliability); break; case IPTOS_MINCOST: - result = make_ok2(env, atom_mincost); + result = esock_make_ok2(env, atom_mincost); break; default: - result = make_ok2(env, MKI(env, val)); + result = esock_make_ok2(env, MKI(env, val)); break; } @@ -6468,6 +6471,11 @@ int compare_pids(ErlNifEnv* env, } +/* ---------------------------------------------------------------------- + * D e c o d e / E n c o d e F u n c t i o n s + * ---------------------------------------------------------------------- + */ + /* edomain2domain - convert internal (erlang) domain to (proper) domain * * Note that only a subset is supported. @@ -6587,7 +6595,7 @@ BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns) /* The currently only supported extra option is: netns */ key = enif_make_atom(env, "netns"); - if (!enif_get_map_value(env, map, key, &value)) { + if (!GET_MAP_VAL(env, map, key, &value)) { *netns = NULL; // Just in case... return FALSE; } @@ -6737,77 +6745,409 @@ BOOLEAN_T ehow2how(unsigned int ehow, int* how) } return TRUE; - } +} -#if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) -/* strnlen doesn't exist everywhere */ + +/* +++ decode_sockaddr +++ + * + * Decode a socket address - sockaddr. In erlang its represented by + * a map, which has a specific set of attributes, depending on one + * mandatory attribute; family. So depending on the value of the family + * attribute: + * + * local - sockaddr_un: path + * inet - sockaddr_in4: port, addr + * inet6 - sockaddr_in6: port, addr, flowinfo, scope_id + */ +/* static -size_t my_strnlen(const char *s, size_t maxlen) +char* decode_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLen) { - size_t i = 0; - while (i < maxlen && s[i] != '\0') - i++; - return i; -} + ERL_NIF_TERM efam; + int fam; + char* res; + + if (!IS_MAP(env, eSockAddr)) + return str_einval; + + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_family, &efam)) + return str_einval; + + if (!decode_domain(env, efam, &fam)) + return str_einval; + + switch (fam) { + case AF_INET: + res = decode_sockaddr_in4(env, eSockAddr, &sockAddrP->in6, addrLen); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + res = decode_sockaddr_in6(env, eSockAddr, &sockAddrP->in6, addrLen); + break; #endif +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + res = decode_sockaddr_un(env, eSockAddr, &sockAddrP->un, addrLen); + break; +#endif + + default: + result = str_eafnosupport; + break; -/* Create an ok two (2) tuple in the form: {ok, Any}. - * The second element (Any) is already in the form of an - * ERL_NIF_TERM so all we have to do is create the tuple. + } + + return res; +} +*/ + + + +/* +++ decode_sockaddr_in4 +++ + * + * Decode a IPv4 socket address - sockaddr_in4. In erlang its represented by + * a map, which has a specific set of attributes: + * + * port :: port_numbber() + * addr :: ip4_address() + * + * The erlang module ensures that both of these has values exist, so there + * is no need for any elaborate error handling. */ +/* static -ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM any) +char* decode_sockaddr_in4(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_in* sockAddrP, + unsigned int* addrLen) { - return MKT2(env, atom_ok, any); + ERL_NIF_TERM eport, eaddr; + short port; + + / * Basic init * / + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); + +#ifndef NO_SA_LEN + sockAddrP->sin_len = sizeof(struct sockaddr_in); +#endif + + sockAddrP->sin_family = AF_INET; + + / * Extract (e) port number from map * / + if (!GET_MAP_VAL(env, eSockAddr, atom_port, &eport)) + return str_einval; + + / * Decode port number * / + if (!GET_INT(env, eport, &port)) + return str_einval; + sockAddrP->sin_port = sock_htons(port); + + / * Extract (e) address from map * / + if (!GET_MAP_VAL(env, eSockAddr, atom_addr, &eaddr)) + return str_einval; + + / * Decode address * / + if (!decode_ip4_address(env, eaddr, sockAddrP, addrLen)) + return str_einval; + + return NULL; } +*/ -/* Create an ok three (3) tuple in the form: {ok, Val1, Val2}. - * The second (Val1) and third (Val2) elements are already in - * the form of an ERL_NIF_TERM so all we have to do is create - * the tuple. + +/* +++ decode_sockaddr_in6 +++ + * + * Decode a IPv6 socket address - sockaddr_in6. In erlang its represented by + * a map, which has a specific set of attributes: + * + * port :: port_numbber() (integer) + * addr :: ip6_address() (tuple) + * flowinfo :: in6_flow_info() (integer) + * scope_id :: in6_scope_id() (integer) + * + * The erlang module ensures that all of these has values exist, so there + * is no need for any elaborate error handling. */ +/* +#if defined(HAVE_IN6) && defined(AF_INET6) static -ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2) +char* decode_sockaddr_in6(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_in6* sockAddrP, + unsigned int* addrLen) { - return MKT3(env, atom_ok, val1, val2); + ERL_NIF_TERM eport, eaddr; + short port; + + / * Basic init * / + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + sockAddrP->sin6_len = sizeof(struct sockaddr_in); +#endif + + sockAddrP->sin6_family = AF_INET6; + + / * *** Extract (e) port number from map *** * / + if (!GET_MAP_VAL(env, eSockAddr, atom_port, &eport)) + return str_einval; + + / * Decode port number * / + if (!GET_INT(env, eport, &port)) + return str_einval; + + sockAddrP->sin6_port = sock_htons(port); + + / * *** Extract (e) flowinfo from map *** * / + if (!GET_MAP_VAL(env, eSockAddr, atom_flowinfo, &eflowInfo)) + return str_einval; + + / * 4: Get the flowinfo * / + if (!GET_UINT(env, eflowInfo, &flowInfo)) + return str_einval; + + sockAddrP->sin6_flowinfo = flowInfo; + + / * *** Extract (e) scope_id from map *** * / + if (!GET_MAP_VAL(env, eSockAddr, atom_scope_id, &escopeId)) + return str_einval; + + / * *** Get the scope_id *** * / + if (!GET_UINT(env, escopeId, &scopeId)) + return str_einval; + + sockAddrP->sin6_scope_id = scopeId; + + / * *** Extract (e) address from map *** * / + if (!GET_MAP_VAL(env, eSockAddr, atom_addr, &eaddr)) + return str_einval; + + / * Decode address * / + if (!decode_ip6_address(env, eaddr, sockAddrP, addrLen)) + return str_einval; + + return NULL; } +#endif +*/ -/* Create an error two (2) tuple in the form: {error, Reason}. - * The second element (Reason) is already in the form of an - * ERL_NIF_TERM so all we have to do is create the tuple. + + +/* +++ decode_sockaddr_un +++ + * + * Decode a Unix Domain socket address - sockaddr_un. In erlang its represented by + * a map, which has a specific set of attributes: + * + * path :: binary() + * + * The erlang module ensures that this value exist, so there + * is no need for any elaborate error handling. */ +/* +#ifdef HAVE_SYS_UN_H static -ERL_NIF_TERM make_error(ErlNifEnv* env, ERL_NIF_TERM reason) +char* decode_sockaddr_un(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_un* sockAddrP, + unsigned int* addrLen) { - return MKT2(env, atom_error, reason); + ErlNifBinary bin; + ERL_NIF_TERM epath; + unsigned int len; + + / * *** Extract (e) path (a binary) from map *** * / + if (!GET_MAP_VAL(env, eSockAddr, atom_port, &epath)) + return str_einval; + + / * Get the path * / + if (!GET_BIN(env, epath, &bin)) + return str_einval; + + if ((bin.size + +#ifdef __linux__ + / * Make sure the address gets zero terminated + * except when the first byte is \0 because then it is + * sort of zero terminated although the zero termination + * comes before the address... + * This fix handles Linux's nonportable + * abstract socket address extension. + * / + (bin.data[0] == '\0' ? 0 : 1) +#else + 1 +#endif + ) > sizeof(sockaAddrP->sun_path)) + return str_einval; + + + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_un)); + sockAddrP->sun_family = AF_UNIX; + + sys_memcpy(sockAddrP->sun_path, bin.data, bin.size); + len = offsetof(struct sockaddr_un, sun_path) + bin.size; + +#ifndef NO_SA_LEN + sockAddrP->sun_len = len; +#endif + *addrLen = len: + + return NULL; } +#endif +*/ -/* Create an error two (2) tuple in the form: {error, Reason}. - * The second element, Reason, is a string to be converted into - * an atom. + +/* +++ decode_ip4_address +++ + * + * Decode a IPv4 address. This can be three things: + * + * + Then atom 'any' + * + Then atom 'loopback' + * + An ip4_address() (4 tuple) + * + * Note that this *only* decodes the "address" part of a + * (IPv4) socket address. There are several other things (port). */ +/* static -ERL_NIF_TERM make_error1(ErlNifEnv* env, char* reason) +char* decode_ip4_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct sockaddr_in* sockAddrP, + unsigned int* addrLen) { - return make_error(env, MKA(env, reason)); + if (IS_ATOM(env, eAddr)) { + / * This is either 'any' or 'loopback' * / + struct in_addr addr; + + if (COMPARE(esock_atom_loopback, eAddr) == 0) { + addr.s_addr = sock_htonl(INADDR_LOOPBACK); + } else if (COMPARE(esock_atom_any, eAddr) == 0) { + addr.s_addr = sock_htonl(INADDR_ANY); + } else { + return str_einval; + } + + sockAddrP->sin_addr.s_addr = addr.s_addr; + *addrLen = sizeof(struct sockaddr_in); + + } else { + / * This is a 4-tuple * / + + const ERL_NIF_TERM* addrt; + int addrtSz; + int a, v; + char addr[4]; + + if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) + return str_einval; + + if (addrtSz != 4) + return str_einval; + + for (a = 0; a < 4; a++) { + if (!GET_INT(env, addrt[a], &v)) + return str_einval; + addr[a] = v; + } + + sys_memcpy(&sockAddrP->sin_addr, &addr, sizeof(addr)); + *addrLenP = sizeof(struct sockaddr_in); + + } + + return NULL; } +*/ + -/* Create an error two (2) tuple in the form: {error, Reason}. - * The second element, Reason, is the errno value in its - * basic form (integer) which will (eventually) be converted - * into an atom. +/* +++ decode_ip6_address +++ + * + * Decode a IPv6 address. This can be three things: + * + * + Then atom 'any' + * + Then atom 'loopback' + * + An ip6_address() (8 tuple) + * + * Note that this *only* decodes the "address" part of a + * (IPv6) socket address. There are several other things + * (port, flowinfo and scope_id) that are handled elsewhere). */ +/* +#if defined(HAVE_IN6) && defined(AF_INET6) +static +char* decode_ip6_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct sockaddr_in6* sockAddrP, + unsigned int* addrLen) +{ + if (IS_ATOM(env, eAddr)) { + / * This is either 'any' or 'loopback' * / + const struct in6_addr* addr; + + if (COMPARE(esock_atom_loopback, eAddr) == 0) { + addr = &in6addr_loopback; + } else if (COMPARE(esock_atom_any, eAddr) == 0) { + addr = &in6addr_any; + } else { + return str_einval; + } + + sockAddrP->sin6_addr = *addr; + *addrLen = sizeof(struct sockaddr_in6); + + } else { + / * This is a 8-tuple * / + + const ERL_NIF_TERM* addrt; + int addrtSz; + int a, v; + char addr[16]; + + if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) + return str_einval; + + if (addrtSz != 8) + return str_einval; + + for (a = 0; a < 8; a++) { + if (!GET_INT(env, addrt[a], &v)) + return str_einval; + addr[a*2 ] = ((v >> 8) & 0xFF); + addr[a*2+1] = (v & 0xFF); + } + + sys_memcpy(&sockAddrP->sin6_addr, &addr, sizeof(addr)); + *addrLen = sizeof(struct sockaddr_in6); + + } + + return NULL; +} +#endif +*/ + + + +#if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) +/* strnlen doesn't exist everywhere */ static -ERL_NIF_TERM make_error2(ErlNifEnv* env, int err) +size_t my_strnlen(const char *s, size_t maxlen) { - return make_error1(env, erl_errno_id(err)); + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; } +#endif /* Send an error closed message to the specified process: @@ -6893,6 +7233,7 @@ void xabort(const char* expr, } + /* ---------------------------------------------------------------------- * C o u n t e r F u n c t i o n s * ---------------------------------------------------------------------- @@ -7137,89 +7478,6 @@ void socket_down(ErlNifEnv* env, -/* ---------------------------------------------------------------------- - * D e b u g F u n c t i o n s - * ---------------------------------------------------------------------- - */ - -/* - * Print a debug format string *with* both a timestamp and the - * the name of the *current* thread. - */ -static -void dbg_printf( const char* format, ... ) -{ - va_list args; - char f[512 + sizeof(format)]; // This has to suffice... - char stamp[30]; - struct timespec ts; - int res; - - /* - * We should really include self in the printout, so we can se which process - * are executing the code. But then I must change the API.... - * ....something for later. - */ - - if (!dbg_realtime(&ts)) { - if (dbg_timespec2str(stamp, sizeof(stamp), &ts) != 0) { - // res = enif_snprintf(f, sizeof(f), "SOCKET [%s] %s", TSNAME(), format); - res = enif_snprintf(f, sizeof(f), "SOCKET [%s]", format); - } else { - // res = enif_snprintf(f, sizeof(f), "SOCKET [%s] [%s] %s", stamp, TSNAME(), format); - res = enif_snprintf(f, sizeof(f), "SOCKET [%s] %s", stamp, format); - } - - if (res > 0) { - va_start (args, format); - erts_vfprintf (stdout, f, args); // TMP: use enif_vfprintf - va_end (args); - fflush(stdout); - } - } - - return; -} - - -static -int dbg_realtime(struct timespec* tsP) -{ - return clock_gettime(CLOCK_REALTIME, tsP); -} - - - - -/* - * Convert a timespec struct into a readable/printable string - */ -static -int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts) -{ - int ret, buflen; - struct tm t; - - tzset(); - if (localtime_r(&(ts->tv_sec), &t) == NULL) - return 1; - - ret = strftime(buf, len, "%F %T", &t); - if (ret == 0) - return 2; - len -= ret - 1; - buflen = strlen(buf); - - ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); - if (ret >= len) - return 3; - - return 0; -} - - - - /* ---------------------------------------------------------------------- * L o a d / u n l o a d / u p g r a d e F u n c t i o n s * ---------------------------------------------------------------------- @@ -7271,7 +7529,7 @@ BOOLEAN_T extract_item_on_load(ErlNifEnv* env, if (!enif_is_map(env, map)) return FALSE; - if (!enif_get_map_value(env, map, key, val)) + if (!GET_MAP_VAL(env, map, key, val)) return FALSE; return TRUE; @@ -7373,18 +7631,15 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) data.numProtoSCTP = 0; /* +++ Misc atoms +++ */ - atom_any = MKA(env, str_any); atom_close = MKA(env, str_close); atom_closed = MKA(env, str_closed); atom_closing = MKA(env, str_closing); atom_debug = MKA(env, str_debug); - atom_error = MKA(env, str_error); atom_false = MKA(env, str_false); atom_global_counters = MKA(env, str_global_counters); atom_in4_sockaddr = MKA(env, str_in4_sockaddr); atom_in6_sockaddr = MKA(env, str_in6_sockaddr); atom_iow = MKA(env, str_iow); - atom_loopback = MKA(env, str_loopback); atom_nif_abort = MKA(env, str_nif_abort); atom_num_dinet = MKA(env, str_num_dinet); atom_num_dinet6 = MKA(env, str_num_dinet6); @@ -7397,11 +7652,28 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_num_tdgrams = MKA(env, str_num_tdgrams); atom_num_tseqpkgs = MKA(env, str_num_tseqpkgs); atom_num_tstreams = MKA(env, str_num_tstreams); - atom_ok = MKA(env, str_ok); atom_select = MKA(env, str_select); atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); - atom_undefined = MKA(env, str_undefined); + + esock_atom_addr = MKA(env, "addr"); + esock_atom_any = MKA(env, "any"); + esock_atom_dgram = MKA(env, "dgram"); + esock_atom_error = MKA(env, "error"); + esock_atom_family = MKA(env, "family"); + esock_atom_flowinfo = MKA(env, "flowinfo"); + esock_atom_inet = MKA(env, "inet"); + esock_atom_inet6 = MKA(env, "inet6"); + esock_atom_local = MKA(env, "local"); + esock_atom_loopback = MKA(env, "loopback"); + esock_atom_ok = MKA(env, "ok"); + esock_atom_path = MKA(env, "path"); + esock_atom_port = MKA(env, "port"); + esock_atom_raw = MKA(env, "raw"); + esock_atom_scope_id = MKA(env, "scope_id"); + esock_atom_seqpacket = MKA(env, "seqpacket"); + esock_atom_stream = MKA(env, "stream"); + esock_atom_undefined = MKA(env, "undefined"); atom_lowdelay = MKA(env, str_lowdelay); atom_throughput = MKA(env, str_throughput); diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c new file mode 100644 index 0000000000..818e259ae8 --- /dev/null +++ b/erts/emulator/nifs/common/socket_util.c @@ -0,0 +1,659 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. 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. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Utility functions for the socket and net NIF(s). + * ---------------------------------------------------------------------- + * + */ + +#include +#include "socket_int.h" +#include "socket_util.h" +#include "sys.h" + + +/* THIS IS JUST TEMPORARY */ +extern char* erl_errno_id(int error); + + +/* +++ esock_decode_sockaddr +++ + * + * Decode a socket address - sockaddr. In erlang its represented as + * a map, which has a specific set of attributes, depending on one + * mandatory attribute; family. So depending on the value of the family + * attribute: + * + * local - sockaddr_un: path + * inet - sockaddr_in4: port, addr + * inet6 - sockaddr_in6: port, addr, flowinfo, scope_id + */ + +extern +char* esock_decode_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLen) +{ + ERL_NIF_TERM efam; + int fam; + char* xres; + + if (!IS_MAP(env, eSockAddr)) + return ESOCK_STR_EINVAL; + + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_family, &efam)) + return ESOCK_STR_EINVAL; + + if ((xres = esock_decode_domain(env, efam, &fam)) != NULL) + return xres; + + switch (fam) { + case AF_INET: + xres = esock_decode_sockaddr_in4(env, eSockAddr, + &sockAddrP->in4, addrLen); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + xres = esock_decode_sockaddr_in6(env, eSockAddr, + &sockAddrP->in6, addrLen); + break; +#endif + +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + xres = esock_decode_sockaddr_un(env, eSockAddr, + &sockAddrP->un, addrLen); + break; +#endif + + default: + xres = ESOCK_STR_EAFNOSUPPORT; + break; + + } + + return xres; +} + + + +/* +++ esock_decode_sockaddr_in4 +++ + * + * Decode a IPv4 socket address - sockaddr_in4. In erlang its represented as + * a map, which has a specific set of attributes: + * + * port :: port_numbber() + * addr :: ip4_address() + * + * The erlang module ensures that both of these has values exist, so there + * is no need for any elaborate error handling. + */ + +extern +char* esock_decode_sockaddr_in4(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_in* sockAddrP, + unsigned int* addrLen) +{ + ERL_NIF_TERM eport, eaddr; + int port; + + /* Basic init */ + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); + +#ifndef NO_SA_LEN + sockAddrP->sin_len = sizeof(struct sockaddr_in); +#endif + + sockAddrP->sin_family = AF_INET; + + /* Extract (e) port number from map */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_port, &eport)) + return ESOCK_STR_EINVAL; + + /* Decode port number */ + if (!GET_INT(env, eport, &port)) + return ESOCK_STR_EINVAL; + + sockAddrP->sin_port = htons(port); + + /* Extract (e) address from map */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_addr, &eaddr)) + return ESOCK_STR_EINVAL; + + /* Decode address */ + if (!esock_decode_ip4_address(env, eaddr, sockAddrP, addrLen)) + return ESOCK_STR_EINVAL; + + return NULL; +} + + +/* +++ decode_sockaddr_in6 +++ + * + * Decode a IPv6 socket address - sockaddr_in6. In erlang its represented as + * a map, which has a specific set of attributes: + * + * port :: port_numbber() (integer) + * addr :: ip6_address() (tuple) + * flowinfo :: in6_flow_info() (integer) + * scope_id :: in6_scope_id() (integer) + * + * The erlang module ensures that all of these has values exist, so there + * is no need for any elaborate error handling here. + */ + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_decode_sockaddr_in6(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_in6* sockAddrP, + unsigned int* addrLen) +{ + ERL_NIF_TERM eport, eaddr, eflowInfo, escopeId; + int port; + unsigned int flowInfo, scopeId; + + /* Basic init */ + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + sockAddrP->sin6_len = sizeof(struct sockaddr_in); +#endif + + sockAddrP->sin6_family = AF_INET6; + + /* *** Extract (e) port number from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_port, &eport)) + return ESOCK_STR_EINVAL; + + /* Decode port number */ + if (!GET_INT(env, eport, &port)) + return ESOCK_STR_EINVAL; + + sockAddrP->sin6_port = htons(port); + + /* *** Extract (e) flowinfo from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_flowinfo, &eflowInfo)) + return ESOCK_STR_EINVAL; + + /* 4: Get the flowinfo */ + if (!GET_UINT(env, eflowInfo, &flowInfo)) + return ESOCK_STR_EINVAL; + + sockAddrP->sin6_flowinfo = flowInfo; + + /* *** Extract (e) scope_id from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_scope_id, &escopeId)) + return ESOCK_STR_EINVAL; + + /* *** Get the scope_id *** */ + if (!GET_UINT(env, escopeId, &scopeId)) + return ESOCK_STR_EINVAL; + + sockAddrP->sin6_scope_id = scopeId; + + /* *** Extract (e) address from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_addr, &eaddr)) + return ESOCK_STR_EINVAL; + + /* Decode address */ + if (!esock_decode_ip6_address(env, eaddr, sockAddrP, addrLen)) + return ESOCK_STR_EINVAL; + + return NULL; +} +#endif + + + +/* +++ esock_decode_sockaddr_un +++ + * + * Decode a Unix Domain socket address - sockaddr_un. In erlang its + * represented as a map, which has a specific set of attributes: + * + * path :: binary() + * + * The erlang module ensures that this value exist, so there + * is no need for any elaborate error handling here. + */ + +#ifdef HAVE_SYS_UN_H +extern +char* esock_decode_sockaddr_un(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_un* sockAddrP, + unsigned int* addrLen) +{ + ErlNifBinary bin; + ERL_NIF_TERM epath; + unsigned int len; + + /* *** Extract (e) path (a binary) from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_path, &epath)) + return ESOCK_STR_EINVAL; + + /* Get the path */ + if (!GET_BIN(env, epath, &bin)) + return ESOCK_STR_EINVAL; + + if ((bin.size + +#ifdef __linux__ + /* Make sure the address gets zero terminated + * except when the first byte is \0 because then it is + * sort of zero terminated although the zero termination + * comes before the address... + * This fix handles Linux's nonportable + * abstract socket address extension. + */ + (bin.data[0] == '\0' ? 0 : 1) +#else + 1 +#endif + ) > sizeof(sockAddrP->sun_path)) + return ESOCK_STR_EINVAL; + + + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_un)); + sockAddrP->sun_family = AF_UNIX; + + sys_memcpy(sockAddrP->sun_path, bin.data, bin.size); + len = offsetof(struct sockaddr_un, sun_path) + bin.size; + +#ifndef NO_SA_LEN + sockAddrP->sun_len = len; +#endif + *addrLen = len; + + return NULL; +} +#endif + + + +/* +++ decode_ip4_address +++ + * + * Decode a IPv4 address. This can be three things: + * + * + Then atom 'any' + * + Then atom 'loopback' + * + An ip4_address() (4 tuple) + * + * Note that this *only* decodes the "address" part of a + * (IPv4) socket address. There are several other things (port). + */ + +extern +char* esock_decode_ip4_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct sockaddr_in* sockAddrP, + unsigned int* addrLen) +{ + if (IS_ATOM(env, eAddr)) { + /* This is either 'any' or 'loopback' */ + struct in_addr addr; + + if (COMPARE(esock_atom_loopback, eAddr) == 0) { + addr.s_addr = htonl(INADDR_LOOPBACK); + } else if (COMPARE(esock_atom_any, eAddr) == 0) { + addr.s_addr = htonl(INADDR_ANY); + } else { + return ESOCK_STR_EINVAL; + } + + sockAddrP->sin_addr.s_addr = addr.s_addr; + *addrLen = sizeof(struct sockaddr_in); + + } else { + /* This is a 4-tuple */ + + const ERL_NIF_TERM* addrt; + int addrtSz; + int a, v; + char addr[4]; + + if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) + return ESOCK_STR_EINVAL; + + if (addrtSz != 4) + return ESOCK_STR_EINVAL; + + for (a = 0; a < 4; a++) { + if (!GET_INT(env, addrt[a], &v)) + return ESOCK_STR_EINVAL; + addr[a] = v; + } + + sys_memcpy(&sockAddrP->sin_addr, &addr, sizeof(addr)); + *addrLen = sizeof(struct sockaddr_in); + + } + + return NULL; +} + + + +/* +++ decode_ip6_address +++ + * + * Decode a IPv6 address. This can be three things: + * + * + Then atom 'any' + * + Then atom 'loopback' + * + An ip6_address() (8 tuple) + * + * Note that this *only* decodes the "address" part of a + * (IPv6) socket address. There are several other things + * (port, flowinfo and scope_id) that are handled elsewhere). + */ + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_decode_ip6_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct sockaddr_in6* sockAddrP, + unsigned int* addrLen) +{ + if (IS_ATOM(env, eAddr)) { + /* This is either 'any' or 'loopback' */ + const struct in6_addr* addr; + + if (COMPARE(esock_atom_loopback, eAddr) == 0) { + addr = &in6addr_loopback; + } else if (COMPARE(esock_atom_any, eAddr) == 0) { + addr = &in6addr_any; + } else { + return ESOCK_STR_EINVAL; + } + + sockAddrP->sin6_addr = *addr; + *addrLen = sizeof(struct sockaddr_in6); + + } else { + /* This is a 8-tuple */ + + const ERL_NIF_TERM* addrt; + int addrtSz; + int a, v; + char addr[16]; + + if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) + return ESOCK_STR_EINVAL; + + if (addrtSz != 8) + return ESOCK_STR_EINVAL; + + for (a = 0; a < 8; a++) { + if (!GET_INT(env, addrt[a], &v)) + return ESOCK_STR_EINVAL; + addr[a*2 ] = ((v >> 8) & 0xFF); + addr[a*2+1] = (v & 0xFF); + } + + sys_memcpy(&sockAddrP->sin6_addr, &addr, sizeof(addr)); + *addrLen = sizeof(struct sockaddr_in6); + + } + + return NULL; +} +#endif + + +/* +++ esock_decode_domain +++ + * + * Decode the Erlang form of the 'domain' type, that is: + * + * inet => AF_INET + * inet6 => AF_INET6 + * local => AF_UNIX + * + */ +extern +char* esock_decode_domain(ErlNifEnv* env, + ERL_NIF_TERM eDomain, + int* domain) +{ + char* xres = NULL; + + if (COMPARE(esock_atom_inet, eDomain) == 0) { + *domain = AF_INET; + +#if defined(HAVE_IN6) && defined(AF_INET6) + } else if (COMPARE(esock_atom_inet6, eDomain) == 0) { + *domain = AF_INET6; +#endif + +#ifdef HAVE_SYS_UN_H + } else if (COMPARE(esock_atom_local, eDomain) == 0) { + *domain = AF_UNIX; +#endif + + } else { + *domain = -1; + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + +/* +++ esock_encode_domain +++ + * + * Encode the native domain to the Erlang form, that is: + * + * AF_INET => inet + * AF_INET6 => inet6 + * AF_UNIX => local + * + */ +extern +char* esock_encode_domain(ErlNifEnv* env, + int domain, + ERL_NIF_TERM* eDomain) +{ + char* xres = NULL; + + switch (domain) { + case AF_INET: + *eDomain = esock_atom_inet; + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + *eDomain = esock_atom_inet6; + break; +#endif + +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + *eDomain = esock_atom_local; + break; +#endif + + default: + *eDomain = esock_atom_undefined; // Just in case + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + +/* +++ esock_decode_type +++ + * + * Decode the Erlang form of the 'type' type, that is: + * + * stream => SOCK_STREAM + * dgram => SOCK_DGRAM + * raw => SOCK_RAW + * seqpacket => SOCK_SEQPACKET + * + */ +extern +char* esock_decode_type(ErlNifEnv* env, + ERL_NIF_TERM eType, + int* type) +{ + char* xres = NULL; + + if (COMPARE(esock_atom_stream, eType) == 0) { + *type = SOCK_STREAM; + } else if (COMPARE(esock_atom_dgram, eType) == 0) { + *type = SOCK_DGRAM; + } else if (COMPARE(esock_atom_raw, eType) == 0) { + *type = SOCK_RAW; + +#if defined(HAVE_SCTP) + } else if (COMPARE(esock_atom_seqpacket, eType) == 0) { + *type = SOCK_SEQPACKET; +#endif + + } else { + *type = -1; + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + +/* +++ esock_decode_domain +++ + * + * Encode the native type to the Erlang form, that is: + * + * SOCK_STREAM => stream + * SOCK_DGRAM => dgram + * SOCK_RAW => raw + * SOCK_SEQPACKET => seqpacket + * + */ +extern +char* esock_encode_type(ErlNifEnv* env, + int type, + ERL_NIF_TERM* eType) +{ + char* xres = NULL; + + switch (type) { + case SOCK_STREAM: + *eType = esock_atom_stream; + break; + + case SOCK_DGRAM: + *eType = esock_atom_dgram; + break; + + case SOCK_RAW: + *eType = esock_atom_raw; + break; + +#if defined(HAVE_SCTP) + case SOCK_SEQPACKET: + *eType = esock_atom_seqpacket; + break; +#endif + + default: + *eType = esock_atom_undefined; // Just in case + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + +/* Create an ok two (2) tuple in the form: + * + * {ok, Any} + * + * The second element (Any) is already in the form of an + * ERL_NIF_TERM so all we have to do is create the tuple. + */ +extern +ERL_NIF_TERM esock_make_ok2(ErlNifEnv* env, ERL_NIF_TERM any) +{ + return MKT2(env, esock_atom_ok, any); +} + + +/* Create an ok three (3) tuple in the form: + * + * {ok, Val1, Val2} + * + * The second (Val1) and third (Val2) elements are already in + * the form of an ERL_NIF_TERM so all we have to do is create + * the tuple. + */ +extern +ERL_NIF_TERM esock_make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2) +{ + return MKT3(env, esock_atom_ok, val1, val2); +} + + + +/* Create an error two (2) tuple in the form: + * + * {error, Reason} + * + * The second element (Reason) is already in the form of an + * ERL_NIF_TERM so all we have to do is create the tuple. + */ +extern +ERL_NIF_TERM esock_make_error(ErlNifEnv* env, ERL_NIF_TERM reason) +{ + return MKT2(env, esock_atom_error, reason); +} + + + +/* Create an error two (2) tuple in the form: {error, Reason}. + * + * {error, Reason} + * + * The second element, Reason, is the reason string that has + * converted into an atom. + */ +extern +ERL_NIF_TERM esock_make_error_str(ErlNifEnv* env, char* reason) +{ + return esock_make_error(env, MKA(env, reason)); +} + + +/* Create an error two (2) tuple in the form: + * + * {error, Reason} + * + * The second element, Reason, is the errno value in its + * basic form (integer) which has been converted into an atom. + */ +extern +ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err) +{ + return esock_make_error_str(env, erl_errno_id(err)); +} + + diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h new file mode 100644 index 0000000000..babfebc17b --- /dev/null +++ b/erts/emulator/nifs/common/socket_util.h @@ -0,0 +1,108 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. 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. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Utility "stuff" for socket and net. + * ---------------------------------------------------------------------- + * + */ + +#ifndef SOCKET_UTIL_H__ +#define SOCKET_UTIL_H__ + +#include +#include "socket_int.h" + +extern +char* esock_decode_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLen); + +extern +char* esock_decode_sockaddr_in4(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_in* sockAddrP, + unsigned int* addrLen); + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_decode_sockaddr_in6(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_in6* sockAddrP, + unsigned int* addrLen); +#endif + +#ifdef HAVE_SYS_UN_H +extern +char* esock_decode_sockaddr_un(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_un* sockAddrP, + unsigned int* addrLen); +#endif + +extern +char* esock_decode_ip4_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct sockaddr_in* sockAddrP, + unsigned int* addrLen); + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_decode_ip6_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct sockaddr_in6* sockAddrP, + unsigned int* addrLen); +#endif + +extern +char* esock_decode_domain(ErlNifEnv* env, + ERL_NIF_TERM eDomain, + int* domain); + +extern +char* esock_encode_domain(ErlNifEnv* env, + int domain, + ERL_NIF_TERM* eDomain); + +extern +char* esock_decode_type(ErlNifEnv* env, + ERL_NIF_TERM eType, + int* type); + +extern +char* esock_encode_type(ErlNifEnv* env, + int type, + ERL_NIF_TERM* eType); + + +extern +ERL_NIF_TERM esock_make_ok2(ErlNifEnv* env, ERL_NIF_TERM any); +extern +ERL_NIF_TERM esock_make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2); + +extern +ERL_NIF_TERM esock_make_error(ErlNifEnv* env, ERL_NIF_TERM reason); +extern +ERL_NIF_TERM esock_make_error_str(ErlNifEnv* env, char* reason); +extern +ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err); + + +#endif // SOCKET_UTIL_H__ -- cgit v1.2.3 From 978a8a855c57bdfb20d3bcd8a6055968f3be3887 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 18 Jun 2018 15:29:53 +0200 Subject: [socket+net-nif] Backup --- erts/emulator/nifs/common/net_nif.c | 1203 ++++--------------------------- erts/emulator/nifs/common/socket_dbg.h | 10 + erts/emulator/nifs/common/socket_int.h | 20 +- erts/emulator/nifs/common/socket_nif.c | 416 +++++------ erts/emulator/nifs/common/socket_util.c | 474 +++++++++++- erts/emulator/nifs/common/socket_util.h | 47 ++ 6 files changed, 858 insertions(+), 1312 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 90263d11c2..9a96eff654 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -26,21 +26,6 @@ #define STATIC_ERLANG_NIF 1 -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ - #ifdef HAVE_CONFIG_H #include "config.h" @@ -176,48 +161,13 @@ #include "socket_dbg.h" #include "socket_int.h" +#include "socket_util.h" /* All platforms fail on malloc errors. */ #define FATAL_MALLOC - -/* *** Boolean *type* stuff... *** */ -typedef unsigned int BOOLEAN_T; -#define TRUE 1 -#define FALSE 0 -#define BOOL2STR(__B__) ((__B__) ? "true" : "false") -#define BOOL2ATOM(__B__) ((__B__) ? atom_true : atom_false) - -/* Two byte integer decoding */ -#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ - (((unsigned char*) (s))[1])) - -#define SASSERT(e) \ - ((void) ((e) ? 1 : (xabort(#e, __func__, __FILE__, __LINE__), 0))) - - -/* Debug stuff... */ -#define SOCKET_NIF_DEBUG_DEFAULT TRUE - -/* Various defaults... */ -#define SOCKET_DEBUG_DEFAULT TRUE -#define SOCKET_IOW_DEFAULT FALSE - -/* Counters and stuff (Don't know where to sent this stuff anyway) */ -#define SOCKET_NIF_IOW_DEFAULT FALSE - - -/* Used in debug printouts */ -#ifdef __WIN32__ -#define LLU "%I64u" -#else -#define LLU "%llu" -#endif -typedef unsigned long long llu_t; - - #ifdef __WIN32__ #define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__)) #else @@ -226,15 +176,6 @@ typedef unsigned long long llu_t; -/* Socket stuff */ -// #define INVALID_SOCKET -1 -// #define INVALID_EVENT -1 -// #define SOCKET_ERROR -1 - -// #define SOCKET int -// #define HANDLE long int - - /* *** Misc macros and defines *** */ #ifdef __WIN32__ @@ -243,12 +184,6 @@ typedef unsigned long long llu_t; #define get_errno() errno #endif -// #if defined(TCP_CA_NAME_MAX) -// #define SOCKET_OPT_TCP_CONGESTION_NAME_MAX TCP_CA_NAME_MAX -// #else -/* This is really excessive, but just in case... */ -// #define SOCKET_OPT_TCP_CONGESTION_NAME_MAX 256 -// #endif #define HOSTNAME_LEN 256 #define SERVICE_LEN 256 @@ -260,74 +195,13 @@ typedef unsigned long long llu_t; */ #define NET_MAXHOSTNAMELEN 255 + /* =================================================================== * * * * Various enif macros * * * * =================================================================== */ -/* #define MALLOC(SZ) enif_alloc((SZ)) */ -/* #define FREE(P) enif_free((P)) */ - -/* #define MKA(E,S) enif_make_atom((E), (S)) */ -/* #define MKBIN(E,B) enif_make_binary((E), (B)) */ -/* #define MKI(E,I) enif_make_int((E), (I)) */ -/* #define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) */ -/* #define MKEL(E) enif_make_list((E), 0) */ -/* #define MKREF(E) enif_make_ref((E)) */ -/* #define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) */ -/* #define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) */ -/* #define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) */ -/* #define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) */ -/* #define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) */ -/* #define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) */ -/* #define MKT5(E,E1,E2,E3,E4,E5) \ */ -/* enif_make_tuple5((E), (E1), (E2), (E3), (E4), (E5)) */ -/* #define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ */ -/* enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) */ -/* #define MKTA(E, A, AL) enif_make_tuple_from_array((E), (A), (AL)) */ - -/* #define MCREATE(N) enif_mutex_create((N)) */ -/* #define MDESTROY(M) enif_mutex_destroy((M)) */ -/* #define MLOCK(M) enif_mutex_lock((M)) */ -/* #define MUNLOCK(M) enif_mutex_unlock((M)) */ - -/* #define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) */ -/* #define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) */ - -/* #define SELECT(E,FD,M,O,P,R) \ */ -/* enif_select((E), (FD), (M), (O), (P), (R)) */ -/* #define SELECT_READ(E, DP, P, R) \ */ -/* SELECT((E), (DP)->sock, (ERL_NIF_SELECT_READ), (DP), (P), (R)) */ -/* #define SELECT_WRITE(E, DP, P, R) \ */ -/* SELECT((E), (DP)->sock, (ERL_NIF_SELECT_WRITE), (DP), (P), (R)) */ -/* #define SELECT_STOP(E, DP) \ */ -/* enif_select((E), (DP)->sock, (ERL_NIF_SELECT_STOP), (DP), NULL, atom_undefined) */ - -/* #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)) */ -/* #define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) */ -/* #define IS_LIST(E, TE) enif_is_list((E), (TE)) */ - -/* #define COMPARE(L, R) enif_compare((L), (R)) */ - -/* #define GET_ATOM_LEN(E, TE, LP) \ */ -/* enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) */ -/* #define GET_ATOM(E, TE, BP, MAX) \ */ -/* enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) */ -/* #define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) */ -/* #define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) */ -/* #define GET_LIST_ELEM(E, L, HP, TP) enif_get_list_cell((E), (L), (HP), (TP)) */ -/* #define GET_LIST_LEN(E, L, LP) enif_get_list_length((E), (L), (LP)) */ -/* #define GET_STR(E, L, B, SZ) \ */ -/* enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) */ -/* #define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) */ -/* #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) */ - -/* #define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) */ -/* #define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) */ - #ifdef HAVE_SOCKLEN_T # define SOCKLEN_T socklen_t @@ -335,37 +209,26 @@ typedef unsigned long long llu_t; # define SOCKLEN_T size_t #endif -#define NDEBUG( ___COND___ , proto ) \ - if ( ___COND___ ) { \ - dbg_printf proto; \ - fflush(stdout); \ - } -#define NDBG( proto ) NDEBUG( data.debug , proto ) - -/* The general purpose socket address */ -typedef union { - struct sockaddr sa; - - struct sockaddr_in in4; - -#ifdef HAVE_IN6 - struct sockaddr_in6 in6; -#endif - -#ifdef HAVE_SYS_UN_H - struct sockaddr_un un; -#endif +#define NDBG( proto ) ESOCK_DBG_PRINTF( data.debug , proto ) -} SockAddress; typedef struct { BOOLEAN_T debug; } NetData; + +/* =================================================================== * + * * + * Static data * + * * + * =================================================================== */ + + static NetData data; + /* ---------------------------------------------------------------------- * F o r w a r d s * ---------------------------------------------------------------------- @@ -409,10 +272,10 @@ static ERL_NIF_TERM nif_if_names(ErlNifEnv* env, static ERL_NIF_TERM ncommand(ErlNifEnv* env, ERL_NIF_TERM cmd); static ERL_NIF_TERM ngethostname(ErlNifEnv* env); -static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, - const SockAddress* saP, - SOCKLEN_T saLen, - int flags); +static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, + const SocketAddress* saP, + SOCKLEN_T saLen, + int flags); static ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, char* host, char* serv); @@ -434,36 +297,7 @@ static void net_down(ErlNifEnv* env, const ErlNifPid* pid, const ErlNifMonitor* mon); */ -static BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, - const ERL_NIF_TERM eAddr, - SockAddress* saP, - SOCKLEN_T* saLen); -static BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, - const ERL_NIF_TERM* addrt, - SockAddress* saP, - SOCKLEN_T* saLen); -#if defined(HAVE_IN6) && defined(AF_INET6) -static BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, - const ERL_NIF_TERM* addrt, - SockAddress* saP, - SOCKLEN_T* saLen); -#endif -static ERL_NIF_TERM encode_in_sockaddr(ErlNifEnv* env, - struct sockaddr* addrP, - SOCKLEN_T addrLen); -static ERL_NIF_TERM encode_in4_sockaddr(ErlNifEnv* env, - struct sockaddr_in* addrP, - SOCKLEN_T addrLen); -#if defined(HAVE_IN6) && defined(AF_INET6) -static ERL_NIF_TERM encode_in6_sockaddr(ErlNifEnv* env, - struct sockaddr_in6* addrP, - SOCKLEN_T addrLen); -#endif -#ifdef HAVE_SYS_UN_H -static ERL_NIF_TERM encode_un_sockaddr(ErlNifEnv* env, - struct sockaddr_un* addrP, - SOCKLEN_T addrLen); -#endif + static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, const ERL_NIF_TERM eflags, int* flags); @@ -490,47 +324,11 @@ static ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env, static ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, int proto); -/* static ERL_NIF_TERM make_address_info(ErlNifEnv* env, */ -/* struct addrinfo* addrInfoP); */ -/* static ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, */ -/* struct sockaddr* addrP, */ -/* SOCKLEN_T addrLen); */ - -static ERL_NIF_TERM make_in4_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM port, - ERL_NIF_TERM addr); -#if defined(HAVE_IN6) && defined(AF_INET6) -static ERL_NIF_TERM make_in6_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM port, - ERL_NIF_TERM addr, - ERL_NIF_TERM flowInfo, - ERL_NIF_TERM scopeId); -#endif static ERL_NIF_TERM make_address_info(ErlNifEnv* env, ERL_NIF_TERM fam, ERL_NIF_TERM sockType, ERL_NIF_TERM proto, ERL_NIF_TERM addr); -static ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM val); -// static ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2); -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); - -#if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) -static size_t my_strnlen(const char *s, size_t maxlen); -#endif - -static void dbg_printf( const char* format, ... ); -static int dbg_realtime(struct timespec* tsP); -static int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts); - -/* -static void xabort(const char* expr, - const char* func, - const char* file, - int line); -*/ static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); @@ -560,134 +358,63 @@ static const struct in6_addr in6addr_loopback = /* *** String constants *** */ static char str_address_info[] = "address_info"; -static char str_dccp[] = "dccp"; static char str_debug[] = "debug"; -static char str_dgram[] = "dgram"; -static char str_error[] = "error"; -static char str_false[] = "false"; static char str_idn[] = "idn"; static char str_idna_allow_unassigned[] = "idna_allow_unassigned"; static char str_idna_use_std3_ascii_rules[] = "idna_use_std3_ascii_rules"; -static char str_in4_sockaddr[] = "in4_sockaddr"; -static char str_in6_sockaddr[] = "in6_sockaddr"; -static char str_inet[] = "inet"; -static char str_inet6[] = "inet6"; -static char str_ip[] = "ip"; -static char str_ipv6[] = "ipv6"; static char str_namereqd[] = "namereqd"; static char str_name_info[] = "name_info"; static char str_nofqdn[] = "nofqdn"; static char str_numerichost[] = "numerichost"; static char str_numericserv[] = "numericserv"; -static char str_ok[] = "ok"; -static char str_raw[] = "raw"; -static char str_rdm[] = "rdm"; -static char str_seqpacket[] = "seqpacket"; -static char str_stream[] = "stream"; -static char str_tcp[] = "tcp"; -static char str_true[] = "true"; -static char str_udp[] = "udp"; -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_eafnosupport[] = "eafnosupport"; static char str_eaddrfamily[] = "eaddrfamily"; -static char str_eagain[] = "eagain"; static char str_ebadflags[] = "ebadflags"; static char str_efail[] = "efail"; static char str_efamily[] = "efamily"; static char str_efault[] = "efault"; -static char str_einval[] = "einval"; -// static char str_eisconn[] = "eisconn"; static char str_emem[] = "emem"; static char str_enametoolong[] = "enametoolong"; static char str_enodata[] = "enodata"; static char str_enoname[] = "enoname"; -// static char str_enotclosing[] = "enotclosing"; -// static char str_enotconn[] = "enotconn"; static char str_enxio[] = "enxio"; static char str_eoverflow[] = "eoverflow"; static char str_eservice[] = "eservice"; static char str_esocktype[] = "esocktype"; static char str_esystem[] = "esystem"; -// static char str_exalloc[] = "exalloc"; -// static char str_exbadstate[] = "exbadstate"; -// static char str_exbusy[] = "exbusy"; -// static char str_exmon[] = "exmonitor"; // failed monitor -// static char str_exself[] = "exself"; // failed self -// static char str_exsend[] = "exsend"; // failed send /* *** Atoms *** */ static ERL_NIF_TERM atom_address_info; -static ERL_NIF_TERM atom_dccp; static ERL_NIF_TERM atom_debug; -static ERL_NIF_TERM atom_dgram; -static ERL_NIF_TERM atom_error; -static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_idn; static ERL_NIF_TERM atom_idna_allow_unassigned; static ERL_NIF_TERM atom_idna_use_std3_ascii_rules; -static ERL_NIF_TERM atom_in4_sockaddr; -static ERL_NIF_TERM atom_in6_sockaddr; -static ERL_NIF_TERM atom_inet; -static ERL_NIF_TERM atom_inet6; -static ERL_NIF_TERM atom_ip; -static ERL_NIF_TERM atom_ipv6; static ERL_NIF_TERM atom_namereqd; static ERL_NIF_TERM atom_name_info; static ERL_NIF_TERM atom_nofqdn; static ERL_NIF_TERM atom_numerichost; static ERL_NIF_TERM atom_numericserv; -static ERL_NIF_TERM atom_ok; -static ERL_NIF_TERM atom_raw; -static ERL_NIF_TERM atom_rdm; -// static ERL_NIF_TERM atom_select; -static ERL_NIF_TERM atom_stream; -static ERL_NIF_TERM atom_seqpacket; -// static ERL_NIF_TERM atom_timeout; -static ERL_NIF_TERM atom_tcp; -static ERL_NIF_TERM atom_true; -static ERL_NIF_TERM atom_udp; -static ERL_NIF_TERM atom_undefined; - -// static ERL_NIF_TERM atom_lowdelay; -// static ERL_NIF_TERM atom_throughput; -// static ERL_NIF_TERM atom_reliability; -// static ERL_NIF_TERM atom_mincost; - -// static ERL_NIF_TERM atom_eafnosupport; + + static ERL_NIF_TERM atom_eaddrfamily; -static ERL_NIF_TERM atom_eagain; +// static ERL_NIF_TERM atom_eagain; static ERL_NIF_TERM atom_ebadflags; static ERL_NIF_TERM atom_efail; static ERL_NIF_TERM atom_efamily; static ERL_NIF_TERM atom_efault; -static ERL_NIF_TERM atom_einval; -// static ERL_NIF_TERM atom_eisconn; static ERL_NIF_TERM atom_emem; static ERL_NIF_TERM atom_enametoolong; static ERL_NIF_TERM atom_enodata; static ERL_NIF_TERM atom_enoname; -// static ERL_NIF_TERM atom_enotclosing; -// static ERL_NIF_TERM atom_enotconn; static ERL_NIF_TERM atom_enxio; static ERL_NIF_TERM atom_eoverflow; static ERL_NIF_TERM atom_eservice; static ERL_NIF_TERM atom_esocktype; static ERL_NIF_TERM atom_esystem; -// static ERL_NIF_TERM atom_exalloc; -// static ERL_NIF_TERM atom_exbadstate; -// static ERL_NIF_TERM atom_exbusy; -// static ERL_NIF_TERM atom_exmon; -// static ERL_NIF_TERM atom_exself; -// static ERL_NIF_TERM atom_exsend; + /* *** net *** */ static ErlNifResourceType* net; @@ -738,7 +465,7 @@ ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, if (argc != 0) return enif_make_badarg(env); - return atom_true; + return esock_atom_true; } @@ -755,13 +482,13 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, { ERL_NIF_TERM info, tmp; - NDBG( ("info -> entry\r\n") ); + NDBG( ("NET", "info -> entry\r\n") ); tmp = enif_make_new_map(env); if (!enif_make_map_put(env, tmp, atom_debug, BOOL2ATOM(data.debug), &info)) info = tmp; - NDBG( ("info -> done: %T\r\n", info) ); + NDBG( ("NET", "info -> done: %T\r\n", info) ); return info; } @@ -787,18 +514,18 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env, { ERL_NIF_TERM ecmd, result; - NDBG( ("command -> entry (%d)\r\n", argc) ); + NDBG( ("NET", "command -> entry (%d)\r\n", argc) ); if (argc != 1) return enif_make_badarg(env); ecmd = argv[0]; - NDBG( ("command -> ecmd: %T\r\n", ecmd) ); + NDBG( ("NET", "command -> ecmd: %T\r\n", ecmd) ); result = ncommand(env, ecmd); - NDBG( ("command -> result: %T\r\n", result) ); + NDBG( ("NET", "command -> result: %T\r\n", result) ); return result; } @@ -819,19 +546,19 @@ ERL_NIF_TERM ncommand(ErlNifEnv* env, if (IS_TUPLE(env, cmd)) { /* Could be the debug tuple */ if (!GET_TUPLE(env, cmd, &tsz, &t)) - return make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); if (tsz != 2) - return make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); /* First element should be the atom 'debug' */ if (COMPARE(t[0], atom_debug) != 0) - return make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); return decode_bool(env, t[1], &data.debug); } else { - return make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); } } @@ -852,14 +579,14 @@ ERL_NIF_TERM nif_gethostname(ErlNifEnv* env, { ERL_NIF_TERM result; - NDBG( ("nif_gethostname -> entry (%d)\r\n", argc) ); + NDBG( ("NET", "nif_gethostname -> entry (%d)\r\n", argc) ); if (argc != 0) return enif_make_badarg(env); result = ngethostname(env); - NDBG( ("nif_gethostname -> done when result: %T\r\n", result) ); + NDBG( ("NET", "nif_gethostname -> done when result: %T\r\n", result) ); return result; } @@ -874,27 +601,27 @@ ERL_NIF_TERM ngethostname(ErlNifEnv* env) res = net_gethostname(buf, sizeof(buf)); - NDBG( ("ngethostname -> gethostname res: %d\r\n", res) ); + NDBG( ("NET", "ngethostname -> gethostname res: %d\r\n", res) ); switch (res) { case 0: - result = make_ok2(env, MKS(env, buf)); + result = esock_make_ok2(env, MKS(env, buf)); break; case EFAULT: - result = make_error(env, atom_efault); + result = esock_make_error(env, atom_efault); break; case EINVAL: - result = make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; case ENAMETOOLONG: - result = make_error(env, atom_enametoolong); + result = esock_make_error(env, atom_enametoolong); break; default: - result = make_error(env, MKI(env, res)); + result = esock_make_error(env, MKI(env, res)); break; } @@ -920,33 +647,35 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM result, eSockAddr; - ERL_NIF_TERM eFlags; - int flags = 0; // Just in case... - SockAddress sa; - SOCKLEN_T saLen = 0; // Just in case... + ERL_NIF_TERM result; + ERL_NIF_TERM eSockAddr, eFlags; + int flags = 0; // Just in case... + SocketAddress sa; + SOCKLEN_T saLen = 0; // Just in case... - NDBG( ("nif_getnameinfo -> entry (%d)\r\n", argc) ); + NDBG( ("NET", "nif_getnameinfo -> entry (%d)\r\n", argc) ); if (argc != 2) return enif_make_badarg(env); eSockAddr = argv[0]; eFlags = argv[1]; - NDBG( ("nif_getnameinfo -> " + NDBG( ("NET", + "nif_getnameinfo -> " "\r\n SockAddr: %T" "\r\n Flags: %T" "\r\n", eSockAddr, eFlags) ); - if (!decode_nameinfo_flags(env, eFlags, &flags)) + if (!esock_decode_sockaddr(env, eSockAddr, &sa, &saLen)) return enif_make_badarg(env); - if (!decode_in_sockaddr(env, eSockAddr, &sa, &saLen)) + if (!decode_nameinfo_flags(env, eFlags, &flags)) return enif_make_badarg(env); result = ngetnameinfo(env, &sa, saLen, flags); - NDBG( ("nif_getnameinfo -> done when result: " + NDBG( ("NET", + "nif_getnameinfo -> done when result: " "\r\n %T\r\n", result) ); return result; @@ -958,10 +687,10 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, * service info. */ static -ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, - const SockAddress* saP, - SOCKLEN_T saLen, - int flags) +ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, + const SocketAddress* saP, + SOCKLEN_T saLen, + int flags) { ERL_NIF_TERM result; char host[HOSTNAME_LEN]; @@ -974,7 +703,7 @@ ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, serv, servLen, flags); - NDBG( ("ngetnameinfo -> res: %d\r\n", res) ); + NDBG( ("NET", "ngetnameinfo -> res: %d\r\n", res) ); switch (res) { case 0: @@ -983,44 +712,44 @@ ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, atom_name_info, MKS(env, host), MKS(env, serv)); - result = make_ok2(env, info); + result = esock_make_ok2(env, info); } break; case EAI_AGAIN: - result = make_error(env, atom_eagain); + result = esock_make_error(env, esock_atom_eagain); break; case EAI_BADFLAGS: - result = make_error(env, atom_ebadflags); + result = esock_make_error(env, atom_ebadflags); break; case EAI_FAIL: - result = make_error(env, atom_efail); + result = esock_make_error(env, atom_efail); break; case EAI_FAMILY: - result = make_error(env, atom_efamily); + result = esock_make_error(env, atom_efamily); break; case EAI_MEMORY: - result = make_error(env, atom_emem); + result = esock_make_error(env, atom_emem); break; case EAI_NONAME: - result = make_error(env, atom_enoname); + result = esock_make_error(env, atom_enoname); break; case EAI_OVERFLOW: - result = make_error(env, atom_eoverflow); + result = esock_make_error(env, atom_eoverflow); break; case EAI_SYSTEM: - result = make_error2(env, get_errno()); + result = esock_make_error_errno(env, get_errno()); break; default: - result = make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -1051,7 +780,7 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, char* servName; // struct addrinfo* hints; - NDBG( ("nif_getaddrinfo -> entry (%d)\r\n", argc) ); + NDBG( ("NET", "nif_getaddrinfo -> entry (%d)\r\n", argc) ); if (argc != 3) { return enif_make_badarg(env); @@ -1060,7 +789,8 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, eServName = argv[1]; // eHints = argv[2]; - NDBG( ("nif_getaddrinfo -> " + NDBG( ("NET", + "nif_getaddrinfo -> " "\r\n ehost: %T" "\r\n eservice: %T" "\r\n ehints: %T" @@ -1093,7 +823,8 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, FREE(hints); */ - NDBG( ("nif_getaddrinfo -> done when result: " + NDBG( ("NET", + "nif_getaddrinfo -> done when result: " "\r\n %T\r\n", result) ); return result; @@ -1109,7 +840,7 @@ ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, struct addrinfo* addrInfoP; int res; - NDBG( ("ngetaddrinfo -> entry with" + NDBG( ("NET", "ngetaddrinfo -> entry with" "\r\n host: %s" "\r\n serv: %s" "\r\n", @@ -1118,63 +849,63 @@ ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, res = getaddrinfo(host, serv, NULL, &addrInfoP); - NDBG( ("ngetaddrinfo -> res: %d\r\n", res) ); + NDBG( ("NET", "ngetaddrinfo -> res: %d\r\n", res) ); switch (res) { case 0: { ERL_NIF_TERM addrInfo = encode_address_infos(env, addrInfoP); freeaddrinfo(addrInfoP); - result = make_ok2(env, addrInfo); + result = esock_make_ok2(env, addrInfo); } break; case EAI_ADDRFAMILY: - result = make_error(env, atom_eaddrfamily); + result = esock_make_error(env, atom_eaddrfamily); break; case EAI_AGAIN: - result = make_error(env, atom_eagain); + result = esock_make_error(env, esock_atom_eagain); break; case EAI_BADFLAGS: - result = make_error(env, atom_ebadflags); + result = esock_make_error(env, atom_ebadflags); break; case EAI_FAIL: - result = make_error(env, atom_efail); + result = esock_make_error(env, atom_efail); break; case EAI_FAMILY: - result = make_error(env, atom_efamily); + result = esock_make_error(env, atom_efamily); break; case EAI_MEMORY: - result = make_error(env, atom_emem); + result = esock_make_error(env, atom_emem); break; case EAI_NODATA: - result = make_error(env, atom_enodata); + result = esock_make_error(env, atom_enodata); break; case EAI_NONAME: - result = make_error(env, atom_enoname); + result = esock_make_error(env, atom_enoname); break; case EAI_SERVICE: - result = make_error(env, atom_eservice); + result = esock_make_error(env, atom_eservice); break; case EAI_SOCKTYPE: - result = make_error(env, atom_esocktype); + result = esock_make_error(env, atom_esocktype); break; case EAI_SYSTEM: - result = make_error(env, atom_esystem); + result = esock_make_error(env, atom_esystem); break; default: - result = make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -1200,23 +931,24 @@ ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, ERL_NIF_TERM eifn, result; char ifn[IF_NAMESIZE+1]; - NDBG( ("nif_if_name2index -> entry (%d)\r\n", argc) ); + NDBG( ("NET", "nif_if_name2index -> entry (%d)\r\n", argc) ); if (argc != 1) { return enif_make_badarg(env); } eifn = argv[0]; - NDBG( ("nif_if_name2index -> " + NDBG( ("NET", + "nif_if_name2index -> " "\r\n Ifn: %T" "\r\n", argv[0]) ); if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) - return make_error2(env, atom_einval); + return esock_make_error(env, esock_atom_einval); result = nif_name2index(env, ifn); - NDBG( ("nif_if_name2index -> done when result: %T\r\n", result) ); + NDBG( ("NET", "nif_if_name2index -> done when result: %T\r\n", result) ); return result; } @@ -1229,16 +961,16 @@ ERL_NIF_TERM nif_name2index(ErlNifEnv* env, { unsigned int idx; - NDBG( ("nif_name2index -> entry with ifn: %s\r\n", ifn) ); + NDBG( ("NET", "nif_name2index -> entry with ifn: %s\r\n", ifn) ); idx = if_nametoindex(ifn); - NDBG( ("nif_name2index -> idx: %d\r\n", idx) ); + NDBG( ("NET", "nif_name2index -> idx: %d\r\n", idx) ); if (idx == 0) - return make_error2(env, get_errno()); + return esock_make_error_errno(env, get_errno()); else - return make_ok2(env, MKI(env, idx)); + return esock_make_ok2(env, MKI(env, idx)); } @@ -1262,20 +994,20 @@ ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env, ERL_NIF_TERM result; unsigned int idx; - NDBG( ("nif_if_index2name -> entry (%d)\r\n", argc) ); + NDBG( ("NET", "nif_if_index2name -> entry (%d)\r\n", argc) ); if ((argc != 1) || !GET_UINT(env, argv[0], &idx)) { return enif_make_badarg(env); } - NDBG( ("nif_index2name -> " + NDBG( ("NET", "nif_index2name -> " "\r\n Idx: %T" "\r\n", argv[0]) ); result = nif_index2name(env, idx); - NDBG( ("nif_if_index2name -> done when result: %T\r\n", result) ); + NDBG( ("NET", "nif_if_index2name -> done when result: %T\r\n", result) ); return result; } @@ -1293,9 +1025,9 @@ ERL_NIF_TERM nif_index2name(ErlNifEnv* env, return enif_make_badarg(env); // PLACEHOLDER if (NULL != if_indextoname(idx, ifn)) { - result = make_ok2(env, MKS(env, ifn)); + result = esock_make_ok2(env, MKS(env, ifn)); } else { - result = make_error(env, atom_enxio); + result = esock_make_error(env, atom_enxio); } FREE(ifn); @@ -1320,7 +1052,7 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env, { ERL_NIF_TERM result; - NDBG( ("nif_if_names -> entry (%d)\r\n", argc) ); + NDBG( ("NET", "nif_if_names -> entry (%d)\r\n", argc) ); if (argc != 0) { return enif_make_badarg(env); @@ -1328,7 +1060,7 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env, result = nif_names(env); - NDBG( ("nif_if_names -> done when result: %T\r\n", result) ); + NDBG( ("NET", "nif_if_names -> done when result: %T\r\n", result) ); return result; } @@ -1341,10 +1073,10 @@ ERL_NIF_TERM nif_names(ErlNifEnv* env) ERL_NIF_TERM result; struct if_nameindex* ifs = if_nameindex(); - NDBG( ("nif_names -> ifs: 0x%lX\r\n", ifs) ); + NDBG( ("NET", "nif_names -> ifs: 0x%lX\r\n", ifs) ); if (ifs == NULL) { - result = make_error2(env, get_errno()); + result = esock_make_error_errno(env, get_errno()); } else { /* * We got some interfaces: @@ -1360,7 +1092,7 @@ ERL_NIF_TERM nif_names(ErlNifEnv* env) */ unsigned int len = nif_names_length(ifs); - NDBG( ("nif_names -> len: %d\r\n", len) ); + NDBG( ("NET", "nif_names -> len: %d\r\n", len) ); if (len > 0) { ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); @@ -1372,10 +1104,10 @@ ERL_NIF_TERM nif_names(ErlNifEnv* env) MKS(env, ifs[i].if_name)); } - result = make_ok2(env, MKLA(env, array, len)); + result = esock_make_ok2(env, MKLA(env, array, len)); FREE(array); } else { - result = make_ok2(env, enif_make_list(env, 0)); + result = esock_make_ok2(env, enif_make_list(env, 0)); } } @@ -1394,7 +1126,7 @@ unsigned int nif_names_length(struct if_nameindex* p) while (!done) { - NDBG( ("nif_names_length -> %d: " + NDBG( ("NET", "nif_names_length -> %d: " "\r\n if_index: %d" "\r\n if_name: 0x%lX" "\r\n", len, p[len].if_index, p[len].if_name) ); @@ -1415,357 +1147,6 @@ unsigned int nif_names_length(struct if_nameindex* p) * ---------------------------------------------------------------------- */ -/* Decode an in_sockaddr (the socket address). - * This is the (erlang) type: in_sockaddr(), which is either - * a in4_sockaddr() (tuple of size 3) or a in6_sockaddr() - * (tuple of size 5). See the net erlang module for details. - * - * We first detect which which of the tuples it is: - * - Size 3: maybe in4_sockaddr - * - Size 5: maybe in6_sockaddr - */ -static -BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, - const ERL_NIF_TERM eAddr, - SockAddress* saP, - SOCKLEN_T* saLen) -{ - const ERL_NIF_TERM* addrt; - int addrtSz; - - if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) - return FALSE; - - switch (addrtSz) { - case 3: - return decode_in4_sockaddr(env, addrt, saP, saLen); - break; - -#ifdef HAVE_IN6 - case 5: - return decode_in6_sockaddr(env, addrt, saP, saLen); - break; -#endif - - default: - return FALSE; - break; - } - -} - - -/* Decode an in4_sockaddr record. This is a tuple of size 3. - * The size has already been verified, but not its content. - * So, the first element should be the atom 'in4_sockaddr'. - * The second the port number, an integer. And the third, - * the actual ip address, a 4-tuple. - */ -static -BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, - const ERL_NIF_TERM* addrt, - SockAddress* saP, - SOCKLEN_T* saLen) -{ - unsigned int len; - char tag[16]; // Just in case... - int port; - int ipAddrSz; - const ERL_NIF_TERM* ipAddrT; - int a, v; - char addr[4]; - - /* The first element: Verify record tag (atom()): in4_sockaddr */ - - if (!(GET_ATOM_LEN(env, addrt[0], &len) && - (len > 0) && - (len <= (sizeof(tag))))) - return FALSE; - - if (!GET_ATOM(env, addrt[0], tag, sizeof(tag))) - return FALSE; - - if (strncmp(tag, "in4_sockaddr", len) != 0) - return FALSE; - - - /* Get second element: port number (integer) */ - - if (!GET_INT(env, addrt[1], &port)) - return FALSE; - - - /* And finally, get the third element, the ip address (a 4 tuple) */ - - if (!GET_TUPLE(env, addrt[2], &ipAddrSz, &ipAddrT)) - return FALSE; - - if (ipAddrSz != 4) - return FALSE; - - - /* And finally initialize the sockaddr structure (and size) */ - sys_memzero((char*)saP, sizeof(struct sockaddr_in)); - saP->in4.sin_family = AF_INET; - saP->in4.sin_port = htons(port); - for (a = 0; a < 4; a++) { - if (!GET_INT(env, ipAddrT[a], &v)) - return FALSE; - addr[a] = v; - } - sys_memcpy(&saP->in4.sin_addr, &addr, sizeof(addr)); - *saLen = sizeof(struct sockaddr_in); - return TRUE; -} - - - -#if defined(HAVE_IN6) && defined(AF_INET6) -/* Decode an in6_sockaddr record. This is a tuple of size 5. - * The size has already been verified, but not its content. - * So, the first element should be the atom 'in6_sockaddr'. - * The second the port number, an integer. The third, the - * actual ip address, a 8-tuple. The forth, the flowinfo, - * an integer. And finally, the fifth element, the scope_id, - * also an integer. *Not used here*. - */ -static -BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, - const ERL_NIF_TERM* addrt, - SockAddress* saP, - SOCKLEN_T* saLen) -{ - unsigned int len; - char tag[16]; // Just in case... - int port; - int ipAddrSz; - const ERL_NIF_TERM* ipAddrT; - int flowInfo; - int a, v; - char addr[16]; - - - /* The first element: Verify record tag (atom()): in6_sockaddr */ - - if (!(GET_ATOM_LEN(env, addrt[0], &len) && - (len > 0) && - (len <= (sizeof(tag))))) - return FALSE; - - if (!GET_ATOM(env, addrt[0], tag, sizeof(tag))) - return FALSE; - - if (strncmp(tag, "in6_sockaddr", len) != 0) - return FALSE; - - - /* Get second element: port number (integer) */ - - if (!GET_INT(env, addrt[1], &port)) - return FALSE; - - - /* Get the third element, the ip address (a 8 tuple) */ - - if (!GET_TUPLE(env, addrt[2], &ipAddrSz, &ipAddrT)) - return FALSE; - - if (ipAddrSz != 8) - return FALSE; - - - /* And finally, get the forth element, the flowinfo (integer) */ - - if (!GET_INT(env, addrt[3], &flowInfo)) - return FALSE; - - - /* And finally initialize the sockaddr structure (and size) */ - - sys_memzero((char*)saP, sizeof(struct sockaddr_in6)); - saP->in6.sin6_family = AF_INET6; - saP->in6.sin6_port = htons(port); - saP->in6.sin6_flowinfo = flowInfo; - /* 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, addrt[a], &v)) - return FALSE; - addr[a*2 ] = ((v >> 8) & 0xFF); - addr[a*2+1] = (v & 0xFF); - } - sys_memcpy(&saP->in6.sin6_addr, &addr, sizeof(addr)); - *saLen = sizeof(struct sockaddr_in6); - - return TRUE; -} -#endif - - - -static -ERL_NIF_TERM encode_in_sockaddr(ErlNifEnv* env, - struct sockaddr* addrP, - SOCKLEN_T addrLen) -{ - SockAddress* sockAddrP = (SockAddress*) addrP; - ERL_NIF_TERM sockAddr; - - switch (sockAddrP->sa.sa_family) { - case AF_INET: - sockAddr = encode_in4_sockaddr(env, &sockAddrP->in4, addrLen); - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - sockAddr = encode_in6_sockaddr(env, &sockAddrP->in6, addrLen); - break; -#endif - -#ifdef HAVE_SYS_UN_H - case AF_UNIX: - sockAddr = encode_un_sockaddr(env, &sockAddrP->un, addrLen); - break; -#endif - - default: - sockAddr = atom_undefined; - break; - } - - return sockAddr; -} - - - -/* Encode an IPv4 socket address; in4_sockaddr */ -static -ERL_NIF_TERM encode_in4_sockaddr(ErlNifEnv* env, - struct sockaddr_in* addrP, - SOCKLEN_T addrLen) -{ - ERL_NIF_TERM sockAddr; - short port; - ERL_NIF_TERM ePort, eAddr; - - - if (addrLen >= sizeof(struct sockaddr_in)) { - unsigned int i; - ERL_NIF_TERM at[4]; - unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); - unsigned char* a = (unsigned char*) &addrP->sin_addr; - - /* The port */ - port = ntohs(addrP->sin_port); - ePort = MKI(env, port); - - /* The address */ - for (i = 0; i < atLen; i++) { - at[i] = MKI(env, a[i]); - } - eAddr = MKTA(env, at, atLen); - // eAddr = MKT4(env, at[0], at[1], at[2], at[3]); - - /* And finally construct the in4_sockaddr record */ - sockAddr = make_in4_sockaddr(env, ePort, eAddr); - - } else { - sockAddr = atom_undefined; - } - - return sockAddr; -} - - - -/* Encode an IPv6 socket address; in6_sockaddr */ -#if defined(HAVE_IN6) && defined(AF_INET6) -static -ERL_NIF_TERM encode_in6_sockaddr(ErlNifEnv* env, - struct sockaddr_in6* addrP, - SOCKLEN_T addrLen) -{ - ERL_NIF_TERM sockAddr; - short port; - ERL_NIF_TERM ePort, eAddr, eFlowInfo, eScopeId; - - - if (addrLen >= sizeof(struct sockaddr_in6)) { - unsigned int i; - ERL_NIF_TERM at[8]; - unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); - unsigned char* a = (unsigned char*) &addrP->sin6_addr; - - /* The port */ - port = ntohs(addrP->sin6_port); - ePort = MKI(env, port); - - /* The address */ - for (i = 0; i < atLen; i++) { - at[i] = MKI(env, get_int16(a + i*2)); - } - eAddr = MKTA(env, at, atLen); - - /* The flowInfo */ - eFlowInfo = MKI(env, addrP->sin6_flowinfo); - - /* The scopeId */ - eScopeId = MKI(env, addrP->sin6_scope_id); - - /* And finally construct the in6_sockaddr record */ - sockAddr = make_in6_sockaddr(env, ePort, eAddr, eFlowInfo, eScopeId); - - } else { - sockAddr = atom_undefined; - } - - return sockAddr; -} -#endif - - - -/* Encode an Unix Domain socket address: string() */ -#ifdef HAVE_SYS_UN_H -static -ERL_NIF_TERM encode_un_sockaddr(ErlNifEnv* env, - struct sockaddr_un* addrP, - SOCKLEN_T addrLen) -{ - ERL_NIF_TERM sockAddr; - size_t n, m; - - if (addrLen >= offsetof(struct sockaddr_un, sun_path)) { - n = addrLen - offsetof(struct sockaddr_un, sun_path); - if (255 < n) { - sockAddr = atom_undefined; - } else { - m = my_strnlen(addrP->sun_path, n); -#ifdef __linux__ - /* Assume that the address is a zero terminated string, - * except when the first byte is \0 i.e the string length is 0, - * then use the reported length instead. - * This fix handles Linux's nonportable - * abstract socket address extension. - */ - if (m == 0) { - m = n; - } -#endif - - sockAddr = MKSL(env, addrP->sun_path, m); - } - } else { - sockAddr = atom_undefined; - } - - return sockAddr; -} -#endif - - - /* The erlang format for a set of flags is a list of atoms. * A special case is when there is no flags, which is * represented by the atom undefined. @@ -1778,14 +1159,14 @@ BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, BOOLEAN_T result; if (IS_ATOM(env, eflags)) { - if (COMPARE(eflags, atom_undefined) == 0) { + if (COMPARE(eflags, esock_atom_undefined) == 0) { *flags = 0; result = TRUE; } else { result = FALSE; } } else if (IS_LIST(env, eflags)) { - NDBG( ("decode_nameinfo_flags -> is atom\r\n") ); + NDBG( ("NET", "decode_nameinfo_flags -> is atom\r\n") ); result = decode_nameinfo_flags_list(env, eflags, flags); } else { result = FALSE; @@ -1809,7 +1190,7 @@ BOOLEAN_T decode_nameinfo_flags_list(ErlNifEnv* env, if (GET_LIST_ELEM(env, list, &elem, &tail)) { if (COMPARE(elem, atom_namereqd) == 0) { tmp |= NI_NAMEREQD; - } else if (COMPARE(elem, atom_dgram) == 0) { + } else if (COMPARE(elem, esock_atom_dgram) == 0) { tmp |= NI_DGRAM; } else if (COMPARE(elem, atom_nofqdn) == 0) { tmp |= NI_NOFQDN; @@ -1864,7 +1245,7 @@ BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, BOOLEAN_T result; if (IS_ATOM(env, eString)) { - if (COMPARE(eString, atom_undefined) == 0) { + if (COMPARE(eString, esock_atom_undefined) == 0) { *stringP = NULL; result = TRUE; } else { @@ -1880,12 +1261,12 @@ BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, result = FALSE; } - NDBG( ("decode_addrinfo_string -> len: %d\r\n", len) ); + NDBG( ("NET", "decode_addrinfo_string -> len: %d\r\n", len) ); bufP = MALLOC(len + 1); // We shall NULL-terminate if (GET_STR(env, eString, bufP, len+1)) { - NDBG( ("decode_addrinfo_string -> buf: %s\r\n", bufP) ); + NDBG( ("NET", "decode_addrinfo_string -> buf: %s\r\n", bufP) ); // bufP[len] = '\0'; *stringP = bufP; result = TRUE; @@ -1906,14 +1287,14 @@ ERL_NIF_TERM decode_bool(ErlNifEnv* env, ERL_NIF_TERM eBool, BOOLEAN_T* bool) { - if (COMPARE(eBool, atom_true) == 0) { + if (COMPARE(eBool, esock_atom_true) == 0) { *bool = TRUE; - return atom_ok; - } else if (COMPARE(eBool, atom_false) == 0) { + return esock_atom_ok; + } else if (COMPARE(eBool, esock_atom_false) == 0) { *bool = FALSE; - return atom_ok; + return esock_atom_ok; } else { - return make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); } } @@ -1930,10 +1311,10 @@ ERL_NIF_TERM encode_address_infos(ErlNifEnv* env, ERL_NIF_TERM result; unsigned int len = address_info_length(addrInfo); - NDBG( ("encode_address_infos -> len: %d\r\n", len) ); + NDBG( ("NET", "encode_address_infos -> len: %d\r\n", len) ); if (len > 0) { - ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); // LEAK? unsigned int i = 0; struct addrinfo* p = addrInfo; @@ -1948,7 +1329,7 @@ ERL_NIF_TERM encode_address_infos(ErlNifEnv* env, result = MKEL(env); } - NDBG( ("encode_address_infos -> result: " + NDBG( ("NET", "encode_address_infos -> result: " "\r\n %T\r\n", result) ); return result; @@ -1997,7 +1378,10 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env, fam = encode_address_info_family(env, addrInfoP->ai_family); type = encode_address_info_type(env, addrInfoP->ai_socktype); proto = encode_address_info_proto(env, addrInfoP->ai_protocol); - addr = encode_in_sockaddr(env, addrInfoP->ai_addr, addrInfoP->ai_addrlen); + esock_encode_sockaddr(env, + (SocketAddress*) addrInfoP->ai_addr, + addrInfoP->ai_addrlen, + &addr); result = make_address_info(env, fam, type, proto, addr); @@ -2057,23 +1441,23 @@ ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env, switch (socktype) { case SOCK_STREAM: - etype = atom_stream; + etype = esock_atom_stream; break; case SOCK_DGRAM: - etype = atom_dgram; + etype = esock_atom_dgram; break; case SOCK_RAW: - etype = atom_raw; + etype = esock_atom_raw; break; case SOCK_RDM: - etype = atom_rdm; + etype = esock_atom_rdm; break; case SOCK_SEQPACKET: - etype = atom_seqpacket; + etype = esock_atom_seqpacket; break; default: @@ -2103,26 +1487,26 @@ ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, #else case IPPROTO_IP: #endif - eproto = atom_ip; + eproto = esock_atom_ip; break; #if defined(SOL_IPV6) case SOL_IPV6: - eproto = atom_ipv6; + eproto = esock_atom_ipv6; break; #endif case IPPROTO_TCP: - eproto = atom_tcp; + eproto = esock_atom_tcp; break; case IPPROTO_UDP: - eproto = atom_udp; + eproto = esock_atom_udp; break; #if defined(HAVE_SCTP) case IPPROTO_SCTP: - eproto = atom_sctp; + eproto = esock_atom_sctp; break; #endif @@ -2136,110 +1520,6 @@ ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, -/* Convert an "native" address to an erlang address - * Note that this is not currently exhaustive, but only supports - * IPv4 and IPv6 addresses. Values of other families will be - * returned as an undefined. - */ -/* -static -ERL_NIF_TERM encode_address_info_addr(ErlNifEnv* env, - struct sockaddr* addrP, - SOCKLEN_T addrLen) -{ - ERL_NIF_TERM port, addr, eaddr; - SockAddress* p = (SockAddress*) addrP; - - NDBG( ("encode_address_info_addr -> entry with" - "\r\n family: %d" - "\r\n addrLen: %d" - "\r\n", addrP->sa_family, addrLen) ); - - switch (addrP->sa_family) { - case AF_INET: - { - unsigned char* a = (unsigned char*) &p->in.sin_addr; - port = MKI(env, ntohs(p->in.sin_port)); - addr = MKT4(env, - MKI(env, a[0]), - MKI(env, a[1]), - MKI(env, a[2]), - MKI(env, a[3])); - eaddr = MKT2(env, port, addr); - } - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - { - unsigned char* a = (unsigned char*) &p->in6.sin6_addr; - port = MKI(env, ntohs(p->in6.sin6_port)); - addr = MKT8(env, - MKI(env, get_int16(a)), - MKI(env, get_int16(&a[ 2])), - MKI(env, get_int16(&a[ 4])), - MKI(env, get_int16(&a[ 6])), - MKI(env, get_int16(&a[ 8])), - MKI(env, get_int16(&a[10])), - MKI(env, get_int16(&a[12])), - MKI(env, get_int16(&a[14]))); - eaddr = MKT2(env, port, addr); - } - break; -#endif - - default: - eaddr = atom_undefined; - break; - } - - NDBG( ("make_addrinfo_addr -> eaddr: " - "\r\n %T\r\n", eaddr) ); - - return eaddr; -} -*/ - - - -#if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) -/* strnlen doesn't exist everywhere */ -static -size_t my_strnlen(const char *s, size_t maxlen) -{ - size_t i = 0; - while (i < maxlen && s[i] != '\0') - i++; - return i; -} -#endif - - - -/* Construct the IPv4 socket address record: in4_sockaddr */ -static -ERL_NIF_TERM make_in4_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM port, - ERL_NIF_TERM addr) -{ - return MKT3(env, atom_in4_sockaddr, port, addr); -} - - - -/* Construct the IPv6 socket address record: in6_sockaddr */ -static -ERL_NIF_TERM make_in6_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM port, - ERL_NIF_TERM addr, - ERL_NIF_TERM flowInfo, - ERL_NIF_TERM scopeId) -{ - return MKT5(env, atom_in6_sockaddr, port, addr, flowInfo, scopeId); -} - - - static ERL_NIF_TERM make_address_info(ErlNifEnv* env, ERL_NIF_TERM fam, @@ -2252,87 +1532,6 @@ ERL_NIF_TERM make_address_info(ErlNifEnv* env, -/* Create an ok two (2) tuple in the form: {ok, Any}. - * The second element (Any) is already in the form of an - * ERL_NIF_TERM so all we have to do is create the tuple. - */ -static -ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM any) -{ - return MKT2(env, atom_ok, any); -} - - -/* Create an ok three (3) tuple in the form: {ok, Val1, Val2}. - * The second (Val1) and third (Val2) elements are already in - * the form of an ERL_NIF_TERM so all we have to do is create - * the tuple. - */ -/* -static -ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2) -{ - return MKT3(env, atom_ok, val1, val2); -} -*/ - - -/* Create an error two (2) tuple in the form: {error, Reason}. - * The second element (Reason) is already in the form of an - * ERL_NIF_TERM so all we have to do is create the tuple. - */ -static -ERL_NIF_TERM make_error(ErlNifEnv* env, ERL_NIF_TERM reason) -{ - return MKT2(env, atom_error, reason); -} - - -/* Create an error two (2) tuple in the form: {error, Reason}. - * The second element, Reason, is a string to be converted into - * an atom. - */ -static -ERL_NIF_TERM make_error1(ErlNifEnv* env, char* reason) -{ - return make_error(env, MKA(env, reason)); -} - - -/* Create an error two (2) tuple in the form: {error, Reason}. - * The second element, Reason, is the errno value in its - * basic form (integer) which will (eventually) be converted - * into an atom. - */ -static -ERL_NIF_TERM make_error2(ErlNifEnv* env, int err) -{ - NDBG( ("make_error2 -> err: %d\r\n", err) ); - return make_error1(env, erl_errno_id(err)); -} - - -/* -static -void xabort(const char* expr, - const char* func, - const char* file, - int line) -{ - fflush(stdout); - fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", - file, line, func, expr); - fflush(stderr); - abort(); -} -*/ - - -/* ---------------------------------------------------------------------- - * C o u n t e r F u n c t i o n s - * ---------------------------------------------------------------------- - */ - /* ---------------------------------------------------------------------- * C a l l b a c k F u n c t i o n s * ---------------------------------------------------------------------- @@ -2380,88 +1579,6 @@ void net_down(ErlNifEnv* env, -/* ---------------------------------------------------------------------- - * D e b u g F u n c t i o n s - * ---------------------------------------------------------------------- - */ - -/* - * Print a debug format string *with* both a timestamp and the - * the name of the *current* thread. - */ -static -void dbg_printf( const char* format, ... ) -{ - va_list args; - char f[512 + sizeof(format)]; // This has to suffice... - char stamp[30]; - struct timespec ts; - int res; - - /* - * We should really include self in the printout, so we can se which process - * are executing the code. But then I must change the API.... - * ....something for later. - */ - - if (!dbg_realtime(&ts)) { - if (dbg_timespec2str(stamp, sizeof(stamp), &ts) != 0) { - // res = enif_snprintf(f, sizeof(f), "NET [%s] %s", TSNAME(), format); - res = enif_snprintf(f, sizeof(f), "NET [%s]", format); - } else { - // res = enif_snprintf(f, sizeof(f), "NET[%s] [%s] %s", stamp, TSNAME(), format); - res = enif_snprintf(f, sizeof(f), "NET [%s] %s", stamp, format); - } - - if (res > 0) { - va_start (args, format); - erts_vfprintf (stdout, f, args); // TMP: use enif_vfprintf - va_end (args); - fflush(stdout); - } - } - - return; -} - - -static -int dbg_realtime(struct timespec* tsP) -{ - return clock_gettime(CLOCK_REALTIME, tsP); -} - - - - -/* - * Convert a timespec struct into a readable/printable string - */ -static -int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts) -{ - int ret, buflen; - struct tm t; - - tzset(); - if (localtime_r(&(ts->tv_sec), &t) == NULL) - return 1; - - ret = strftime(buf, len, "%F %T", &t); - if (ret == 0) - return 2; - len -= ret - 1; - buflen = strlen(buf); - - ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); - if (ret >= len) - return 3; - - return 0; -} - - - /* ---------------------------------------------------------------------- * L o a d / u n l o a d / u p g r a d e F u n c t i o n s * ---------------------------------------------------------------------- @@ -2500,78 +1617,40 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) // We should make it possible to use load_info to get default values data.debug = FALSE; - // NDBG( ("on_load -> entry\r\n") ); + NDBG( ("NET", "on_load -> entry\r\n") ); /* +++ Misc atoms +++ */ atom_address_info = MKA(env, str_address_info); - atom_dccp = MKA(env, str_dccp); atom_debug = MKA(env, str_debug); - atom_dgram = MKA(env, str_dgram); - atom_error = MKA(env, str_error); - atom_false = MKA(env, str_false); atom_idn = MKA(env, str_idn); atom_idna_allow_unassigned = MKA(env, str_idna_allow_unassigned); atom_idna_use_std3_ascii_rules = MKA(env, str_idna_use_std3_ascii_rules); - atom_in4_sockaddr = MKA(env, str_in4_sockaddr); - atom_in6_sockaddr = MKA(env, str_in6_sockaddr); - atom_inet = MKA(env, str_inet); - atom_inet6 = MKA(env, str_inet6); - atom_ip = MKA(env, str_ip); - atom_ipv6 = MKA(env, str_ipv6); atom_namereqd = MKA(env, str_namereqd); atom_name_info = MKA(env, str_name_info); atom_nofqdn = MKA(env, str_nofqdn); atom_numerichost = MKA(env, str_numerichost); atom_numericserv = MKA(env, str_numericserv); - atom_ok = MKA(env, str_ok); - atom_raw = MKA(env, str_raw); - atom_rdm = MKA(env, str_rdm); - atom_seqpacket = MKA(env, str_seqpacket); - atom_stream = MKA(env, str_stream); - atom_tcp = MKA(env, str_tcp); - atom_true = MKA(env, str_true); - atom_udp = MKA(env, str_udp); - atom_undefined = MKA(env, str_undefined); - // atom_version = MKA(env, str_version); - - // atom_lowdelay = MKA(env, str_lowdelay); - // atom_throughput = MKA(env, str_throughput); - // atom_reliability = MKA(env, str_reliability); - // atom_mincost = MKA(env, str_mincost); /* Error codes */ - // atom_eafnosupport = MKA(env, str_eafnosupport); atom_eaddrfamily = MKA(env, str_eaddrfamily); - atom_eagain = MKA(env, str_eagain); atom_ebadflags = MKA(env, str_ebadflags); atom_efail = MKA(env, str_efail); atom_efamily = MKA(env, str_efamily); atom_efault = MKA(env, str_efault); - atom_einval = MKA(env, str_einval); - // atom_eisconn = MKA(env, str_eisconn); atom_emem = MKA(env, str_emem); atom_enametoolong = MKA(env, str_enametoolong); atom_enodata = MKA(env, str_enodata); atom_enoname = MKA(env, str_enoname); - // atom_enotclosing = MKA(env, str_enotclosing); - // atom_enotconn = MKA(env, str_enotconn); atom_enxio = MKA(env, str_enxio); atom_eoverflow = MKA(env, str_eoverflow); atom_eservice = MKA(env, str_eservice); atom_esocktype = MKA(env, str_esocktype); atom_esystem = MKA(env, str_esystem); - // atom_exalloc = MKA(env, str_exalloc); - // atom_exbadstate = MKA(env, str_exbadstate); - // atom_exbusy = MKA(env, str_exbusy); - // atom_exnotopen = MKA(env, str_exnotopen); - // atom_exmon = MKA(env, str_exmon); - // atom_exself = MKA(env, str_exself); - // atom_exsend = MKA(env, str_exsend); // For storing "global" things... - // socketData.env = enif_alloc_env(); // We should really check - // socketData.version = MKA(env, ERTS_VERSION); - // socketData.buildDate = MKA(env, ERTS_BUILD_DATE); + // data.env = enif_alloc_env(); // We should really check + // data.version = MKA(env, ERTS_VERSION); + // data.buildDate = MKA(env, ERTS_BUILD_DATE); net = enif_open_resource_type_x(env, "net", @@ -2579,7 +1658,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) ERL_NIF_RT_CREATE, NULL); - // NDBG( ("on_load -> done\r\n") ); + NDBG( ("NET", "on_load -> done\r\n") ); return !net; } diff --git a/erts/emulator/nifs/common/socket_dbg.h b/erts/emulator/nifs/common/socket_dbg.h index 36e2be1679..ad0fcdada9 100644 --- a/erts/emulator/nifs/common/socket_dbg.h +++ b/erts/emulator/nifs/common/socket_dbg.h @@ -28,6 +28,16 @@ #define SOCKET_DBG_H__ +/* Used in debug printouts */ +#ifdef __WIN32__ +#define LLU "%I64u" +#else +#define LLU "%llu" +#endif +typedef unsigned long long llu_t; + + + #define ESOCK_DBG_PRINTF( ___COND___ , proto ) \ if ( ___COND___ ) { \ esock_dbg_printf proto; \ diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 83794f0b95..d6a612cab6 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -67,7 +67,7 @@ /* The general purpose sockaddr */ typedef union { /* General sockaddr */ - struct sockaddr in; + struct sockaddr sa; /* IPv4 sockaddr */ struct sockaddr_in in4; @@ -85,44 +85,56 @@ typedef union { } SocketAddress; +/* *** Boolean *type* stuff... *** */ typedef unsigned int BOOLEAN_T; #define TRUE 1 #define FALSE 0 +#define BOOL2ATOM(__B__) ((__B__) ? esock_atom_true : esock_atom_false) + /* Misc error strings */ -#define ESOCK_STR_EINVAL "einval" #define ESOCK_STR_EAFNOSUPPORT "eafnosupport" +#define ESOCK_STR_EAGAIN "eagain" +#define ESOCK_STR_EINVAL "einval" /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - * Misc atoms + * "Global" atoms */ extern ERL_NIF_TERM esock_atom_addr; extern ERL_NIF_TERM esock_atom_any; extern ERL_NIF_TERM esock_atom_dgram; extern ERL_NIF_TERM esock_atom_error; +extern ERL_NIF_TERM esock_atom_false; extern ERL_NIF_TERM esock_atom_family; extern ERL_NIF_TERM esock_atom_flowinfo; extern ERL_NIF_TERM esock_atom_inet; extern ERL_NIF_TERM esock_atom_inet6; +extern ERL_NIF_TERM esock_atom_ip; +extern ERL_NIF_TERM esock_atom_ipv6; extern ERL_NIF_TERM esock_atom_local; extern ERL_NIF_TERM esock_atom_loopback; extern ERL_NIF_TERM esock_atom_ok; extern ERL_NIF_TERM esock_atom_path; extern ERL_NIF_TERM esock_atom_port; extern ERL_NIF_TERM esock_atom_raw; +extern ERL_NIF_TERM esock_atom_rdm; extern ERL_NIF_TERM esock_atom_scope_id; +extern ERL_NIF_TERM esock_atom_sctp; extern ERL_NIF_TERM esock_atom_seqpacket; extern ERL_NIF_TERM esock_atom_stream; +extern ERL_NIF_TERM esock_atom_tcp; +extern ERL_NIF_TERM esock_atom_true; +extern ERL_NIF_TERM esock_atom_udp; extern ERL_NIF_TERM esock_atom_undefined; /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * Error value (=reason) atoms */ -extern ERL_NIF_TERM esock_atom_eagain; extern ERL_NIF_TERM esock_atom_eafnosupport; +extern ERL_NIF_TERM esock_atom_eagain; extern ERL_NIF_TERM esock_atom_einval; diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 335d773f89..d2455a7b3a 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -181,22 +181,6 @@ #define FATAL_MALLOC - -/* *** Boolean *type* stuff... *** */ -typedef unsigned int BOOLEAN_T; -#define TRUE 1 -#define FALSE 0 -#define BOOL2STR(__B__) ((__B__) ? "true" : "false") -#define BOOL2ATOM(__B__) ((__B__) ? atom_true : atom_false) - -/* Two byte integer decoding */ -#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ - (((unsigned char*) (s))[1])) - -#define SASSERT(e) \ - ((void) ((e) ? 1 : (xabort(#e, __func__, __FILE__, __LINE__), 0))) - - /* Debug stuff... */ #define SOCKET_NIF_DEBUG_DEFAULT TRUE #define SOCKET_DEBUG_DEFAULT TRUE @@ -205,15 +189,6 @@ typedef unsigned int BOOLEAN_T; #define SOCKET_NIF_IOW_DEFAULT FALSE -/* Used in debug printouts */ -#ifdef __WIN32__ -#define LLU "%I64u" -#else -#define LLU "%llu" -#endif -typedef unsigned long long llu_t; - - /* Socket stuff */ #define INVALID_SOCKET -1 @@ -301,9 +276,9 @@ typedef unsigned long long llu_t; #define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048 -#define SOCKET_OPT_VALLUE_TYPE_UNSPEC 0 -#define SOCKET_OPT_VALLUE_TYPE_INT 1 -#define SOCKET_OPT_VALLUE_TYPE_BOOL 2 +#define SOCKET_OPT_VALUE_TYPE_UNSPEC 0 +#define SOCKET_OPT_VALUE_TYPE_INT 1 +#define SOCKET_OPT_VALUE_TYPE_BOOL 2 typedef union { struct { @@ -1248,15 +1223,17 @@ static BOOLEAN_T decode_sock_linger(ErlNifEnv* env, static BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val); +/* static BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val); +*/ static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, int* opt, uint16_t* valueType, int* valueSz); -static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal); +// static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal); static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val); static void inform_waiting_procs(ErlNifEnv* env, @@ -1330,11 +1307,6 @@ static char* send_msg(ErlNifEnv* env, ERL_NIF_TERM msg, ErlNifPid* pid); -static void xabort(const char* expr, - const char* func, - const char* file, - int line); - static BOOLEAN_T extract_item_on_load(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, @@ -1407,9 +1379,6 @@ static char str_reliability[] = "reliability"; static char str_mincost[] = "mincost"; /* (special) error string constants */ -static char str_eagain[] = "eagain"; -static char str_eafnosupport[] = "eafnosupport"; -static char str_einval[] = "einval"; static char str_eisconn[] = "eisconn"; static char str_enotclosing[] = "enotclosing"; static char str_enotconn[] = "enotconn"; @@ -1426,21 +1395,33 @@ ERL_NIF_TERM esock_atom_addr; ERL_NIF_TERM esock_atom_any; ERL_NIF_TERM esock_atom_dgram; ERL_NIF_TERM esock_atom_error; +ERL_NIF_TERM esock_atom_false; ERL_NIF_TERM esock_atom_family; ERL_NIF_TERM esock_atom_flowinfo; ERL_NIF_TERM esock_atom_inet; ERL_NIF_TERM esock_atom_inet6; +ERL_NIF_TERM esock_atom_ip; +ERL_NIF_TERM esock_atom_ipv6; ERL_NIF_TERM esock_atom_local; ERL_NIF_TERM esock_atom_loopback; ERL_NIF_TERM esock_atom_ok; ERL_NIF_TERM esock_atom_path; ERL_NIF_TERM esock_atom_port; ERL_NIF_TERM esock_atom_raw; +ERL_NIF_TERM esock_atom_rdm; ERL_NIF_TERM esock_atom_scope_id; +ERL_NIF_TERM esock_atom_sctp; ERL_NIF_TERM esock_atom_seqpacket; ERL_NIF_TERM esock_atom_stream; +ERL_NIF_TERM esock_atom_tcp; +ERL_NIF_TERM esock_atom_true; +ERL_NIF_TERM esock_atom_udp; ERL_NIF_TERM esock_atom_undefined; +/* *** "Global" error (=reason) atoms *** */ +ERL_NIF_TERM esock_atom_eagain; +ERL_NIF_TERM esock_atom_eafnosupport; +ERL_NIF_TERM esock_atom_einval; /* *** Atoms *** */ static ERL_NIF_TERM atom_close; @@ -1473,9 +1454,6 @@ static ERL_NIF_TERM atom_throughput; static ERL_NIF_TERM atom_reliability; static ERL_NIF_TERM atom_mincost; -static ERL_NIF_TERM atom_eagain; -static ERL_NIF_TERM atom_eafnosupport; -static ERL_NIF_TERM atom_einval; static ERL_NIF_TERM atom_eisconn; static ERL_NIF_TERM atom_enotclosing; static ERL_NIF_TERM atom_enotconn; @@ -1597,7 +1575,7 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); unsigned int numVals = sizeof(keys) / sizeof(ERL_NIF_TERM); - SASSERT( (numKeys == numVals) ); + ESOCK_ASSERT( (numKeys == numVals) ); if (!MKMA(env, keys, vals, numKeys, &info)) return enif_make_badarg(env); @@ -1982,7 +1960,7 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, if (port == 0) { SOCKLEN_T len = sizeof(local); sys_memzero((char *) &local, len); - sock_name(descP->sock, &local.in, &len); + sock_name(descP->sock, &local.sa, &len); port = which_address_port(&local); } else if (port == -1) { port = 0; @@ -2012,7 +1990,7 @@ char* decode_laddress(ErlNifEnv* env, } else if (IS_TUPLE(env, localAddr)) { return decode_in_sockaddr(env, localAddr, localP, addrLenP); } else { - return str_einval; + return ESOCK_STR_EINVAL; } } @@ -2035,10 +2013,10 @@ char* decode_laddress_binary(ErlNifEnv* env, ErlNifBinary bin; if (domain != AF_UNIX) - return str_einval; + return ESOCK_STR_EINVAL; if (!GET_BIN(env, localAddr, &bin)) - return str_einval; + return ESOCK_STR_EINVAL; if ((bin.size + #ifdef __linux__ @@ -2054,7 +2032,7 @@ char* decode_laddress_binary(ErlNifEnv* env, 1 #endif ) > sizeof(localP->un.sun_path)) - return str_einval; + return ESOCK_STR_EINVAL; sys_memzero((char*)localP, sizeof(struct sockaddr_un)); localP->un.sun_family = domain; @@ -2128,7 +2106,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, return esock_make_error(env, atom_eisconn); if (IS_CONNECTING(descP)) - return esock_make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); code = sock_connect(descP->sock, (struct sockaddr*) &descP->remote, @@ -2364,7 +2342,7 @@ ERL_NIF_TERM naccept(ErlNifEnv* env, break; default: - res = esock_make_error(env, atom_einval); + res = esock_make_error(env, esock_atom_einval); break; } @@ -2437,7 +2415,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, descP->state = SOCKET_STATE_ACCEPTING; - return esock_make_error(env, atom_eagain); + return esock_make_error(env, esock_atom_eagain); } else { return esock_make_error_errno(env, save_errno); @@ -2540,7 +2518,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, (ERL_NIF_SELECT_READ), descP, NULL, ref); - return esock_make_error(env, atom_eagain); + return esock_make_error(env, esock_atom_eagain); } else { return esock_make_error_errno(env, save_errno); } @@ -2742,10 +2720,10 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, /* THIS TEST IS NOT CORRECT!!! */ if (!IS_OPEN(descP)) - return esock_make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); if (!esendflags2sendflags(eflags, &flags)) - return esock_make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); if ((xres = decode_in_sockaddr(env, eSockAddr, &remoteAddr, @@ -2778,7 +2756,7 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, if (toAddrP != NULL) { written = sock_sendto(descP->sock, dataP->data, dataP->size, flags, - &toAddrP->in, toAddrLen); + &toAddrP->sa, toAddrLen); } else { written = sock_sendto(descP->sock, dataP->data, dataP->size, flags, @@ -3069,7 +3047,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, sys_memzero((char*) &fromAddr, addrLen); read = sock_recvfrom(descP->sock, buf.data, buf.size, flags, - &fromAddr.in, &addrLen); + &fromAddr.sa, &addrLen); return recvfrom_check_result(env, descP, read, @@ -3368,11 +3346,10 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, eIsEncoded = argv[1]; eVal = argv[4]; - if (!decode_bool(env, eIsEncoded, &isEncoded)) - return esock_make_error(env, atom_einval); + isEncoded = esock_decode_bool(eIsEncoded); if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) - return esock_make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); return nsetopt(env, descP, isEncoded, isOTP, level, eOpt, eVal); } @@ -3425,7 +3402,7 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, break; default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -3440,15 +3417,9 @@ ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - ERL_NIF_TERM result; + descP->dbg = esock_decode_bool(eVal); - if (decode_bool(env, eVal, &descP->dbg)) { - result = esock_atom_ok; - } else { - result = esock_make_error(env, atom_einval); - } - - return result; + return esock_atom_ok; } @@ -3459,15 +3430,9 @@ ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - ERL_NIF_TERM result; + descP->iow = esock_decode_bool(eVal); - if (decode_bool(env, eVal, &descP->iow)) { - result = esock_atom_ok; - } else { - result = esock_make_error(env, atom_einval); - } - - return result; + return esock_atom_ok; } @@ -3493,7 +3458,7 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, else result = esock_atom_ok; } else { - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); } return result; @@ -3546,7 +3511,7 @@ ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -3615,7 +3580,7 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -3674,7 +3639,7 @@ ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, else result = esock_atom_ok; } else { - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); } return result; @@ -3763,7 +3728,7 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -3834,7 +3799,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, result = esock_atom_ok; } else { - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); } return result; @@ -3881,7 +3846,7 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -3934,7 +3899,7 @@ ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -4002,7 +3967,7 @@ ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -4050,7 +4015,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -4113,7 +4078,7 @@ ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, result = esock_atom_ok; } else { - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); } FREE(val); @@ -4133,19 +4098,17 @@ ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, { ERL_NIF_TERM result; BOOLEAN_T val; + int ival, res; - if (decode_bool(env, eVal, &val)) { - int ival = (val) ? 1 : 0; - int res = socket_setopt(descP->sock, level, opt, &ival, sizeof(ival)); - - if (res != 0) - result = esock_make_error_errno(env, res); - else - result = esock_atom_ok; + val = esock_decode_bool(eVal); + + ival = (val) ? 1 : 0; + res = socket_setopt(descP->sock, level, opt, &ival, sizeof(ival)); - } else { - result = esock_make_error(env, atom_einval); - } + if (res != 0) + result = esock_make_error_errno(env, res); + else + result = esock_atom_ok; return result; } @@ -4172,7 +4135,7 @@ ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, result = esock_atom_ok; } else { - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); } return result; @@ -4378,11 +4341,10 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, } eIsEncoded = argv[1]; - if (!decode_bool(env, eIsEncoded, &isEncoded)) - return esock_make_error(env, atom_einval); + isEncoded = esock_decode_bool(eIsEncoded); if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) - return esock_make_error(env, atom_einval); + return esock_make_error(env, esock_atom_einval); return ngetopt(env, descP, isEncoded, isOTP, level, eOpt); } @@ -4434,7 +4396,7 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, break; default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -4448,9 +4410,7 @@ static ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, SocketDescriptor* descP) { - ERL_NIF_TERM eVal; - - encode_bool(descP->dbg, &eVal); + ERL_NIF_TERM eVal = esock_encode_bool(descP->dbg); return esock_make_ok2(env, eVal); } @@ -4462,9 +4422,7 @@ static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, SocketDescriptor* descP) { - ERL_NIF_TERM eVal; - - encode_bool(descP->iow, &eVal); + ERL_NIF_TERM eVal = esock_encode_bool(descP->iow); return esock_make_ok2(env, eVal); } @@ -4494,21 +4452,21 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, if (decode_native_get_opt(env, eOpt, &opt, &valueType, (int*) &valueSz)) { switch (valueType) { - case SOCKET_OPT_VALLUE_TYPE_UNSPEC: + case SOCKET_OPT_VALUE_TYPE_UNSPEC: result = ngetopt_native_unspec(env, descP, level, opt, valueSz); break; - case SOCKET_OPT_VALLUE_TYPE_INT: + case SOCKET_OPT_VALUE_TYPE_INT: result = ngetopt_int_opt(env, descP, level, opt); break; - case SOCKET_OPT_VALLUE_TYPE_BOOL: + case SOCKET_OPT_VALUE_TYPE_BOOL: result = ngetopt_bool_opt(env, descP, level, opt); break; default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } } else { - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); } return result; @@ -4601,7 +4559,7 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -4668,7 +4626,7 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -4811,7 +4769,7 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -4922,7 +4880,7 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -4973,7 +4931,7 @@ ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -5037,7 +4995,7 @@ ERL_NIF_TERM ngetopt_lvl_udp(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -5082,7 +5040,7 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, #endif default: - result = esock_make_error(env, atom_einval); + result = esock_make_error(env, esock_atom_einval); break; } @@ -5352,7 +5310,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, } else if ((save_errno == ERRNO_BLOCK) || (save_errno == EAGAIN)) { - return esock_make_error(env, atom_eagain); + return esock_make_error(env, esock_atom_eagain); } else { return esock_make_error_errno(env, save_errno); } @@ -5437,7 +5395,7 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, } else if ((save_errno == ERRNO_BLOCK) || (save_errno == EAGAIN)) { - return esock_make_error(env, atom_eagain); + return esock_make_error(env, esock_atom_eagain); } else { return esock_make_error_errno(env, save_errno); } @@ -5501,23 +5459,23 @@ char* decode_send_addr(ErlNifEnv* env, if (!(GET_ATOM_LEN(env, addr, &len) && (len > 0) && (len <= (sizeof("null"))))) - return str_einval; + return ESOCK_STR_EINVAL; if (!GET_ATOM(env, addr, a, sizeof(a))) - return str_einval; + return ESOCK_STR_EINVAL; *toAddrP = NULL; if (strncmp(a, "null", len) == 0) return NULL; else - return str_einval; + return ESOCK_STR_EINVAL; } else if (IS_TUPLE(env, addr)) { / * 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; + return ESOCK_STR_EINVAL; } } */ @@ -5540,18 +5498,18 @@ char* decode_send_addr_tuple(ErlNifEnv* env, int addrtSz; if (!GET_TUPLE(env, addr, &addrtSz, &addrt)) - return str_einval; // PLACEHOLDER + return ESOCK_STR_EINVAL; // PLACEHOLDER switch (domain) { case AF_INET: if (addrtSz != 4) - return str_einval; + return ESOCK_STR_EINVAL; break; #if defined(HAVE_IN6) && defined(AF_INET6) case AF_INET6: if (addrtSz != 8) - return str_einval; + return ESOCK_STR_EINVAL; break; #endif @@ -5592,7 +5550,7 @@ char* decode_in_sockaddr(ErlNifEnv* env, char* result = NULL; if (!GET_TUPLE(env, eSockAddr, &addrtSz, &addrt)) - return str_einval; + return ESOCK_STR_EINVAL; /* * We use the tuple size to figure out which @@ -5610,7 +5568,7 @@ char* decode_in_sockaddr(ErlNifEnv* env, #endif default: - result = str_eafnosupport; + result = ESOCK_STR_EAFNOSUPPORT; break; } @@ -5634,11 +5592,11 @@ char* decode_in4_sockaddr(ErlNifEnv* env, /* 1: Ensure that the tuple has the correct tag: in4_sockaddr */ if (COMPARE(atom_in4_sockaddr, eIn4SockAddr[0]) != 0) - return str_einval; + return ESOCK_STR_EINVAL; /* 2: Get the port number */ if (!GET_INT(env, eIn4SockAddr[1], &port)) - return str_einval; + return ESOCK_STR_EINVAL; /* 3: Get the address. * It can either be the atoms: any | loopback, @@ -5651,7 +5609,7 @@ char* decode_in4_sockaddr(ErlNifEnv* env, return decode_in4_sockaddr_addr(env, eIn4SockAddr[2], port, sockAddrP, addrLenP); } else { - return str_einval; + return ESOCK_STR_EINVAL; } } @@ -5671,7 +5629,7 @@ char* decode_in4_sockaddr_atomaddr(ErlNifEnv* env, } else if (COMPARE(esock_atom_any, eAddr) == 0) { addr.s_addr = sock_htonl(INADDR_ANY); } else { - return str_einval; + return ESOCK_STR_EINVAL; } sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); @@ -5704,10 +5662,10 @@ char* decode_in4_sockaddr_addr(ErlNifEnv* env, /* This shall be a 4 tuple */ if (!GET_TUPLE(env, eAddr, &ip4AddrTSz, &ip4AddrT)) - return str_einval; + return ESOCK_STR_EINVAL; if (ip4AddrTSz != 4) - return str_einval; + return ESOCK_STR_EINVAL; sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in)); #ifndef NO_SA_LEN @@ -5717,7 +5675,7 @@ char* decode_in4_sockaddr_addr(ErlNifEnv* env, sockAddrP->in4.sin_port = sock_htons(port); for (a = 0; a < 4; a++) { if (!GET_INT(env, ip4AddrT[a], &v)) - return str_einval; + return ESOCK_STR_EINVAL; addr[a] = v; } sys_memcpy(&sockAddrP->in4.sin_addr, &addr, sizeof(addr)); @@ -5747,19 +5705,19 @@ char* decode_in6_sockaddr(ErlNifEnv* env, /* 1: Ensure that the tuple has the correct tag: in6_sockaddr */ if (COMPARE(atom_in6_sockaddr, eIn6SockAddr[0]) != 0) - return str_einval; + return ESOCK_STR_EINVAL; /* 2: Get the port number */ if (!GET_INT(env, eIn6SockAddr[1], &port)) - return str_einval; + return ESOCK_STR_EINVAL; /* 4: Get the flowinfo */ if (!GET_UINT(env, eIn6SockAddr[3], &flowInfo)) - return str_einval; + return ESOCK_STR_EINVAL; /* 5: Get the scope_id */ if (!GET_UINT(env, eIn6SockAddr[4], &scopeId)) - return str_einval; + return ESOCK_STR_EINVAL; /* 3: Get the address. * It can either be the atoms: any | loopback, @@ -5774,7 +5732,7 @@ char* decode_in6_sockaddr(ErlNifEnv* env, flowInfo, scopeId, sockAddrP, addrLenP); } else { - return str_einval; + return ESOCK_STR_EINVAL; } } #endif @@ -5797,7 +5755,7 @@ char* decode_in6_sockaddr_atomaddr(ErlNifEnv* env, } else if (COMPARE(esock_atom_any, eAddr) == 0) { addr = &in6addr_any; } else { - return str_einval; + return ESOCK_STR_EINVAL; } sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); @@ -5835,10 +5793,10 @@ char* decode_in6_sockaddr_addr(ErlNifEnv* env, /* This shall be a 8 tuple */ if (!GET_TUPLE(env, eAddr, &ip6AddrTSz, &ip6AddrT)) - return str_einval; + return ESOCK_STR_EINVAL; if (ip6AddrTSz != 8) - return str_einval; + return ESOCK_STR_EINVAL; sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN @@ -5853,7 +5811,7 @@ char* decode_in6_sockaddr_addr(ErlNifEnv* env, */ for (a = 0; a < 8; a++) { if (!GET_INT(env, ip6AddrT[a], &v)) - return str_einval; + return ESOCK_STR_EINVAL; addr[a*2 ] = ((v >> 8) & 0xFF); addr[a*2+1] = (v & 0xFF); } @@ -5896,7 +5854,7 @@ char* decode_address_tuple(ErlNifEnv* env, addrP->sai.sin_port = sock_htons(port); for (a = 0; a < 4; a++) { if (!GET_INT(env, addrt[a], &v)) - return str_einval; + return ESOCK_STR_EINVAL; laddr[a] = v; } sys_memcpy(&addrP->sai.sin_addr, &laddr, sizeof(laddr)); @@ -5923,7 +5881,7 @@ char* decode_address_tuple(ErlNifEnv* env, * / for (a = 0; a < 8; a++) { if (!GET_INT(env, addrt[a], &v)) - return str_einval; + return ESOCK_STR_EINVAL; laddr[a*2 ] = ((v >> 8) & 0xFF); laddr[a*2+1] = (v & 0xFF); } @@ -5936,7 +5894,7 @@ char* decode_address_tuple(ErlNifEnv* env, } / * switch (domain) * / - return str_eafnosupport; + return ESOCK_STR_EAFNOSUPPORT; } */ @@ -5956,14 +5914,14 @@ char* decode_address_tuple(ErlNifEnv* env, */ static void encode_address(ErlNifEnv* env, - SocketAddress* addrP, + SocketAddress* sockAddrP, unsigned int addrLen, ERL_NIF_TERM* domainT, ERL_NIF_TERM* sourceT) { short port; - switch (addrP->in.sa_family) { + switch (sockAddrP->sa.sa_family) { /* +++ inet (IPv4) +++ */ @@ -5972,9 +5930,9 @@ void encode_address(ErlNifEnv* env, ERL_NIF_TERM addrT, portT; unsigned int i; ERL_NIF_TERM at4[4]; - char* a4 = (char*) &addrP->in4.sin_addr; + char* a4 = (char*) &sockAddrP->in4.sin_addr; - port = sock_ntohs(addrP->in4.sin_port); + port = sock_ntohs(sockAddrP->in4.sin_port); for (i = 0; i < 4; i++) { at4[i] = MKI(env, a4[i]); } @@ -5998,9 +5956,9 @@ void encode_address(ErlNifEnv* env, ERL_NIF_TERM addrT, portT; unsigned int i; ERL_NIF_TERM at6[8]; - char* a16 = (char*) &addrP->in6.sin6_addr; + char* a16 = (char*) &sockAddrP->in6.sin6_addr; - port = sock_ntohs(addrP->in6.sin6_port); + port = sock_ntohs(sockAddrP->in6.sin6_port); /* The address tuple is of size 8 * and each element is a two byte integer */ @@ -6037,7 +5995,7 @@ void encode_address(ErlNifEnv* env, if (255 < n) { *sourceT = esock_atom_undefined; } else { - m = my_strnlen(addrP->un.sun_path, n); + m = my_strnlen(sockAddrP->un.sun_path, n); #ifdef __linux__ /* Assume that the address is a zero terminated string, * except when the first byte is \0 i.e the string length is 0, @@ -6050,7 +6008,7 @@ void encode_address(ErlNifEnv* env, } #endif - *sourceT = MKSL(env, addrP->un.sun_path, m); + *sourceT = MKSL(env, sockAddrP->un.sun_path, m); } } } @@ -6087,7 +6045,7 @@ char* decode_address_atom(ErlNifEnv* env, } if (strncmp(addr, "loopback", addrLen) == 0) { any = FALSE; } else { - return str_einval; + return ESOCK_STR_EINVAL; } / * If we get this far, we *know* its either 'any' or 'loopback' * / @@ -6135,7 +6093,7 @@ char* decode_address_atom(ErlNifEnv* env, #endif default: - return str_einval; + return ESOCK_STR_EINVAL; break; } @@ -6143,23 +6101,24 @@ char* decode_address_atom(ErlNifEnv* env, } */ +/* static BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val) { unsigned int len; char b[16]; // Just in case... - /* Verify that the value is actually an atom */ + / * Verify that the value is actually an atom * / if (!IS_ATOM(env, eVal)) return FALSE; - /* Verify that the value is of acceptable length */ + / * Verify that the value is of acceptable length * / if (!(GET_ATOM_LEN(env, eVal, &len) && (len > 0) && (len <= sizeof("false")))) return FALSE; - /* And finally try to extract the value */ + / * And finally try to extract the value * / if (!GET_ATOM(env, eVal, b, sizeof(b))) return FALSE; @@ -6170,6 +6129,7 @@ BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val) return TRUE; } +*/ /* +++ decode the linger value +++ @@ -6195,8 +6155,7 @@ BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* v /* So fas so good - now check the two elements of the tuple. */ - if (!decode_bool(env, lt[0], &onOff)) - return FALSE; + onOff = esock_decode_bool(lt[0]); if (!GET_INT(env, lt[1], &secs)) return FALSE; @@ -6326,10 +6285,10 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, return FALSE; if (strncmp(t, "bool", len) == 0) { - *valueType = SOCKET_OPT_VALLUE_TYPE_BOOL; + *valueType = SOCKET_OPT_VALUE_TYPE_BOOL; *valueSz = sizeof(int); // Just to be sure } else if (strncmp(t, "int", len) == 0) { - *valueType = SOCKET_OPT_VALLUE_TYPE_INT; + *valueType = SOCKET_OPT_VALUE_TYPE_INT; *valueSz = sizeof(int); // Just to be sure } else { return FALSE; @@ -6337,7 +6296,7 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, } else if (IS_NUM(env, nativeOptT[1])) { if (GET_INT(env, nativeOptT[1], valueSz)) { - *valueType = SOCKET_OPT_VALLUE_TYPE_UNSPEC; + *valueType = SOCKET_OPT_VALUE_TYPE_UNSPEC; } else { return FALSE; } @@ -6349,14 +6308,16 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, } +/* static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal) { if (val) - *eVal = atom_true; + *eVal = esock_atom_true; else - *eVal = atom_false; + *eVal = esock_atom_false; } +*/ /* +++ encode the ip socket option tos +++ @@ -6772,13 +6733,13 @@ char* decode_sockaddr(ErlNifEnv* env, char* res; if (!IS_MAP(env, eSockAddr)) - return str_einval; + return ESOCK_STR_EINVAL; if (!GET_MAP_VAL(env, eSockAddr, esock_atom_family, &efam)) - return str_einval; + return ESOCK_STR_EINVAL; if (!decode_domain(env, efam, &fam)) - return str_einval; + return ESOCK_STR_EINVAL; switch (fam) { case AF_INET: @@ -6798,7 +6759,7 @@ char* decode_sockaddr(ErlNifEnv* env, #endif default: - result = str_eafnosupport; + result = ESOCK_STR_EAFNOSUPPORT; break; } @@ -6841,20 +6802,20 @@ char* decode_sockaddr_in4(ErlNifEnv* env, / * Extract (e) port number from map * / if (!GET_MAP_VAL(env, eSockAddr, atom_port, &eport)) - return str_einval; + return ESOCK_STR_EINVAL; / * Decode port number * / if (!GET_INT(env, eport, &port)) - return str_einval; + return ESOCK_STR_EINVAL; sockAddrP->sin_port = sock_htons(port); / * Extract (e) address from map * / if (!GET_MAP_VAL(env, eSockAddr, atom_addr, &eaddr)) - return str_einval; + return ESOCK_STR_EINVAL; / * Decode address * / if (!decode_ip4_address(env, eaddr, sockAddrP, addrLen)) - return str_einval; + return ESOCK_STR_EINVAL; return NULL; } @@ -6896,41 +6857,41 @@ char* decode_sockaddr_in6(ErlNifEnv* env, / * *** Extract (e) port number from map *** * / if (!GET_MAP_VAL(env, eSockAddr, atom_port, &eport)) - return str_einval; + return ESOCK_STR_EINVAL; / * Decode port number * / if (!GET_INT(env, eport, &port)) - return str_einval; + return ESOCK_STR_EINVAL; sockAddrP->sin6_port = sock_htons(port); / * *** Extract (e) flowinfo from map *** * / if (!GET_MAP_VAL(env, eSockAddr, atom_flowinfo, &eflowInfo)) - return str_einval; + return ESOCK_STR_EINVAL; / * 4: Get the flowinfo * / if (!GET_UINT(env, eflowInfo, &flowInfo)) - return str_einval; + return ESOCK_STR_EINVAL; sockAddrP->sin6_flowinfo = flowInfo; / * *** Extract (e) scope_id from map *** * / if (!GET_MAP_VAL(env, eSockAddr, atom_scope_id, &escopeId)) - return str_einval; + return ESOCK_STR_EINVAL; / * *** Get the scope_id *** * / if (!GET_UINT(env, escopeId, &scopeId)) - return str_einval; + return ESOCK_STR_EINVAL; sockAddrP->sin6_scope_id = scopeId; / * *** Extract (e) address from map *** * / if (!GET_MAP_VAL(env, eSockAddr, atom_addr, &eaddr)) - return str_einval; + return ESOCK_STR_EINVAL; / * Decode address * / if (!decode_ip6_address(env, eaddr, sockAddrP, addrLen)) - return str_einval; + return ESOCK_STR_EINVAL; return NULL; } @@ -6964,11 +6925,11 @@ char* decode_sockaddr_un(ErlNifEnv* env, / * *** Extract (e) path (a binary) from map *** * / if (!GET_MAP_VAL(env, eSockAddr, atom_port, &epath)) - return str_einval; + return ESOCK_STR_EINVAL; / * Get the path * / if (!GET_BIN(env, epath, &bin)) - return str_einval; + return ESOCK_STR_EINVAL; if ((bin.size + #ifdef __linux__ @@ -6984,7 +6945,7 @@ char* decode_sockaddr_un(ErlNifEnv* env, 1 #endif ) > sizeof(sockaAddrP->sun_path)) - return str_einval; + return ESOCK_STR_EINVAL; sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_un)); @@ -7032,7 +6993,7 @@ char* decode_ip4_address(ErlNifEnv* env, } else if (COMPARE(esock_atom_any, eAddr) == 0) { addr.s_addr = sock_htonl(INADDR_ANY); } else { - return str_einval; + return ESOCK_STR_EINVAL; } sockAddrP->sin_addr.s_addr = addr.s_addr; @@ -7047,14 +7008,14 @@ char* decode_ip4_address(ErlNifEnv* env, char addr[4]; if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) - return str_einval; + return ESOCK_STR_EINVAL; if (addrtSz != 4) - return str_einval; + return ESOCK_STR_EINVAL; for (a = 0; a < 4; a++) { if (!GET_INT(env, addrt[a], &v)) - return str_einval; + return ESOCK_STR_EINVAL; addr[a] = v; } @@ -7098,7 +7059,7 @@ char* decode_ip6_address(ErlNifEnv* env, } else if (COMPARE(esock_atom_any, eAddr) == 0) { addr = &in6addr_any; } else { - return str_einval; + return ESOCK_STR_EINVAL; } sockAddrP->sin6_addr = *addr; @@ -7113,14 +7074,14 @@ char* decode_ip6_address(ErlNifEnv* env, char addr[16]; if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) - return str_einval; + return ESOCK_STR_EINVAL; if (addrtSz != 8) - return str_einval; + return ESOCK_STR_EINVAL; for (a = 0; a < 8; a++) { if (!GET_INT(env, addrt[a], &v)) - return str_einval; + return ESOCK_STR_EINVAL; addr[a*2 ] = ((v >> 8) & 0xFF); addr[a*2+1] = (v & 0xFF); } @@ -7219,20 +7180,6 @@ char* send_msg(ErlNifEnv* env, } -static -void xabort(const char* expr, - const char* func, - const char* file, - int line) -{ - fflush(stdout); - fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", - file, line, func, expr); - fflush(stderr); - abort(); -} - - /* ---------------------------------------------------------------------- * C o u n t e r F u n c t i o n s @@ -7332,10 +7279,10 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * writers waiting. */ - SASSERT( (NULL == send_msg_nif_abort(env, - descP->currentWriter.ref, - atom_closed, - &descP->currentWriter.pid)) ); + ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, + descP->currentWriter.ref, + atom_closed, + &descP->currentWriter.pid)) ); /* And also deal with the waiting writers (in the same way) */ inform_waiting_procs(env, descP, &descP->writersQ, TRUE, atom_closed); @@ -7347,10 +7294,10 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * readers waiting. */ - SASSERT( (NULL == send_msg_nif_abort(env, - descP->currentReader.ref, - atom_closed, - &descP->currentReader.pid)) ); + ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, + descP->currentReader.ref, + atom_closed, + &descP->currentReader.pid)) ); /* And also deal with the waiting readers (in the same way) */ inform_waiting_procs(env, descP, &descP->readersQ, TRUE, atom_closed); @@ -7361,10 +7308,10 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * acceptors waiting. */ - SASSERT( (NULL == send_msg_nif_abort(env, - descP->currentAcceptor.ref, - atom_closed, - &descP->currentAcceptor.pid)) ); + ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, + descP->currentAcceptor.ref, + atom_closed, + &descP->currentAcceptor.pid)) ); /* And also deal with the waiting acceptors (in the same way) */ inform_waiting_procs(env, descP, &descP->acceptorsQ, TRUE, atom_closed); @@ -7446,10 +7393,10 @@ void inform_waiting_procs(ErlNifEnv* env, * */ - SASSERT( (NULL == send_msg_nif_abort(env, - currentP->data.ref, - reason, - ¤tP->data.pid)) ); + ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, + currentP->data.ref, + reason, + ¤tP->data.pid)) ); DEMONP(env, descP, ¤tP->data.mon); nextP = currentP->nextP; if (free) FREE(currentP); @@ -7656,34 +7603,45 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); + /* Global atom(s) */ esock_atom_addr = MKA(env, "addr"); esock_atom_any = MKA(env, "any"); esock_atom_dgram = MKA(env, "dgram"); esock_atom_error = MKA(env, "error"); + esock_atom_false = MKA(env, "famlse"); esock_atom_family = MKA(env, "family"); esock_atom_flowinfo = MKA(env, "flowinfo"); esock_atom_inet = MKA(env, "inet"); esock_atom_inet6 = MKA(env, "inet6"); + esock_atom_ip = MKA(env, "ip"); + esock_atom_ipv6 = MKA(env, "ipvp"); esock_atom_local = MKA(env, "local"); esock_atom_loopback = MKA(env, "loopback"); esock_atom_ok = MKA(env, "ok"); esock_atom_path = MKA(env, "path"); esock_atom_port = MKA(env, "port"); esock_atom_raw = MKA(env, "raw"); + esock_atom_rdm = MKA(env, "rdm"); esock_atom_scope_id = MKA(env, "scope_id"); + esock_atom_sctp = MKA(env, "sctp"); esock_atom_seqpacket = MKA(env, "seqpacket"); esock_atom_stream = MKA(env, "stream"); + esock_atom_tcp = MKA(env, "tcp"); + esock_atom_true = MKA(env, "true"); + esock_atom_udp = MKA(env, "udp"); esock_atom_undefined = MKA(env, "undefined"); + /* Global error codes */ + esock_atom_eafnosupport = MKA(env, ESOCK_STR_EAFNOSUPPORT); + esock_atom_eagain = MKA(env, ESOCK_STR_EAGAIN); + esock_atom_einval = MKA(env, ESOCK_STR_EINVAL); + atom_lowdelay = MKA(env, str_lowdelay); atom_throughput = MKA(env, str_throughput); atom_reliability = MKA(env, str_reliability); atom_mincost = MKA(env, str_mincost); /* Error codes */ - atom_eagain = MKA(env, str_eagain); - atom_eafnosupport = MKA(env, str_eafnosupport); - atom_einval = MKA(env, str_einval); atom_eisconn = MKA(env, str_eisconn); atom_enotclosing = MKA(env, str_enotclosing); atom_enotconn = MKA(env, str_enotconn); diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 818e259ae8..37adee682b 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -29,8 +29,22 @@ #include "sys.h" -/* THIS IS JUST TEMPORARY */ -extern char* erl_errno_id(int error); + +extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */ + +static char* make_sockaddr_in4(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM* sa); +static char* make_sockaddr_in6(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM flowInfo, + ERL_NIF_TERM scopeId, + ERL_NIF_TERM* sa); +static char* make_sockaddr_un(ErlNifEnv* env, + ERL_NIF_TERM path, + ERL_NIF_TERM* sa); /* +++ esock_decode_sockaddr +++ @@ -95,10 +109,59 @@ char* esock_decode_sockaddr(ErlNifEnv* env, +/* +++ esock_encode_sockaddr +++ + * + * Encode a socket address - sockaddr. In erlang its represented as + * a map, which has a specific set of attributes, depending on one + * mandatory attribute; family. So depending on the value of the family + * attribute: + * + * local - sockaddr_un: path + * inet - sockaddr_in4: port, addr + * inet6 - sockaddr_in6: port, addr, flowinfo, scope_id + */ + +extern +char* esock_encode_sockaddr(ErlNifEnv* env, + SocketAddress* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr) +{ + char* xres; + + switch (sockAddrP->sa.sa_family) { + case AF_INET: + xres = esock_encode_sockaddr_in4(env, &sockAddrP->in4, addrLen, eSockAddr); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + xres = esock_encode_sockaddr_in6(env, &sockAddrP->in6, addrLen, eSockAddr); + break; +#endif + +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + xres = esock_encode_sockaddr_un(env, &sockAddrP->un, addrLen, eSockAddr); + break; +#endif + + default: + xres = ESOCK_STR_EAFNOSUPPORT; + break; + + } + + return xres; +} + + + /* +++ esock_decode_sockaddr_in4 +++ * * Decode a IPv4 socket address - sockaddr_in4. In erlang its represented as - * a map, which has a specific set of attributes: + * a map, which has a specific set of attributes (beside the mandatory family + * attribute, which is "inherited" from the "sockaddr" type): * * port :: port_numbber() * addr :: ip4_address() @@ -147,10 +210,58 @@ char* esock_decode_sockaddr_in4(ErlNifEnv* env, } -/* +++ decode_sockaddr_in6 +++ + +/* +++ esock_encode_sockaddr_in4 +++ + * + * Encode a IPv4 socket address - sockaddr_in4. In erlang its represented as + * a map, which has a specific set of attributes (beside the mandatory family + * attribute, which is "inherited" from the "sockaddr" type): + * + * port :: port_numbber() + * addr :: ip4_address() + * + */ + +extern +char* esock_encode_sockaddr_in4(ErlNifEnv* env, + struct sockaddr_in* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr) +{ + ERL_NIF_TERM ePort, eAddr; + int port; + char* xres = NULL; + + if (addrLen >= sizeof(struct sockaddr_in)) { + /* The port */ + port = ntohs(sockAddrP->sin_port); + ePort = MKI(env, port); + + /* The address */ + if ((xres = esock_encode_ip4_address(env, &sockAddrP->sin_addr, + &eAddr)) != NULL) { + /* And finally construct the in4_sockaddr record */ + xres = make_sockaddr_in4(env, ePort, eAddr, eSockAddr); + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + return xres; +} + + + +/* +++ esock_decode_sockaddr_in6 +++ * * Decode a IPv6 socket address - sockaddr_in6. In erlang its represented as - * a map, which has a specific set of attributes: + * a map, which has a specific set of attributes (beside the mandatory family + * attribute, which is "inherited" from the "sockaddr" type): * * port :: port_numbber() (integer) * addr :: ip6_address() (tuple) @@ -224,10 +335,67 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, +/* +++ esock_encode_sockaddr_in6 +++ + * + * Encode a IPv6 socket address - sockaddr_in6. In erlang its represented as + * a map, which has a specific set of attributes (beside the mandatory family + * attribute, which is "inherited" from the "sockaddr" type): + * + * port :: port_numbber() (integer) + * addr :: ip6_address() (tuple) + * flowinfo :: in6_flow_info() (integer) + * scope_id :: in6_scope_id() (integer) + * + */ + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_encode_sockaddr_in6(ErlNifEnv* env, + struct sockaddr_in6* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr) +{ + ERL_NIF_TERM ePort, eAddr, eFlowInfo, eScopeId; + char* xres; + + if (addrLen >= sizeof(struct sockaddr_in6)) { + /* The port */ + ePort = MKI(env, ntohs(sockAddrP->sin6_port)); + + /* The flowInfo */ + eFlowInfo = MKI(env, sockAddrP->sin6_flowinfo); + + /* The scopeId */ + eScopeId = MKI(env, sockAddrP->sin6_scope_id); + + /* The address */ + if ((xres = esock_encode_ip6_address(env, &sockAddrP->sin6_addr, + &eAddr)) != NULL) { + /* And finally construct the in6_sockaddr record */ + xres = make_sockaddr_in6(env, + ePort, eAddr, eFlowInfo, eScopeId, eSockAddr); + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + return xres; +} +#endif + + + /* +++ esock_decode_sockaddr_un +++ * * Decode a Unix Domain socket address - sockaddr_un. In erlang its - * represented as a map, which has a specific set of attributes: + * represented as a map, which has a specific set of attributes + * (beside the mandatory family attribute, which is "inherited" from + * the "sockaddr" type): * * path :: binary() * @@ -256,16 +424,16 @@ char* esock_decode_sockaddr_un(ErlNifEnv* env, if ((bin.size + #ifdef __linux__ - /* Make sure the address gets zero terminated - * except when the first byte is \0 because then it is - * sort of zero terminated although the zero termination - * comes before the address... - * This fix handles Linux's nonportable - * abstract socket address extension. - */ - (bin.data[0] == '\0' ? 0 : 1) + /* Make sure the address gets zero terminated + * except when the first byte is \0 because then it is + * sort of zero terminated although the zero termination + * comes before the address... + * This fix handles Linux's nonportable + * abstract socket address extension. + */ + (bin.data[0] == '\0' ? 0 : 1) #else - 1 + 1 #endif ) > sizeof(sockAddrP->sun_path)) return ESOCK_STR_EINVAL; @@ -288,7 +456,65 @@ char* esock_decode_sockaddr_un(ErlNifEnv* env, -/* +++ decode_ip4_address +++ +/* +++ esock_encode_sockaddr_un +++ + * + * Encode a Unix Domain socket address - sockaddr_un. In erlang its + * represented as a map, which has a specific set of attributes + * (beside the mandatory family attribute, which is "inherited" from + * the "sockaddr" type): + * + * path :: binary() + * + */ + +#ifdef HAVE_SYS_UN_H +extern +char* esock_encode_sockaddr_un(ErlNifEnv* env, + struct sockaddr_un* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr) +{ + ERL_NIF_TERM ePath; + size_t n, m; + char* xres; + + if (addrLen >= offsetof(struct sockaddr_un, sun_path)) { + n = addrLen - offsetof(struct sockaddr_un, sun_path); + if (255 < n) { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } else { + m = esock_strnlen(sockAddrP->sun_path, n); +#ifdef __linux__ + /* Assume that the address is a zero terminated string, + * except when the first byte is \0 i.e the string length is 0, + * then use the reported length instead. + * This fix handles Linux's nonportable + * abstract socket address extension. + */ + if (m == 0) { + m = n; + } +#endif + + /* And finally build the 'path' attribute */ + ePath = MKSL(env, sockAddrP->sun_path, m); + + /* And the socket address */ + xres = make_sockaddr_un(env, ePath, eSockAddr); + } + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + return xres; +} +#endif + + + +/* +++ esock_decode_ip4_address +++ * * Decode a IPv4 address. This can be three things: * @@ -351,7 +577,39 @@ char* esock_decode_ip4_address(ErlNifEnv* env, -/* +++ decode_ip6_address +++ +/* +++ esock_encode_ip4_address +++ + * + * Encode a IPv4 address: + * + * + An ip4_address() (4 tuple) + * + * Note that this *only* decodes the "address" part of a + * (IPv4) socket address. There are several other things (port). + */ + +extern +char* esock_encode_ip4_address(ErlNifEnv* env, + struct in_addr* addrP, + ERL_NIF_TERM* eAddr) +{ + unsigned int i; + ERL_NIF_TERM at[4]; + unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); + unsigned char* a = (unsigned char*) addrP; + + /* The address */ + for (i = 0; i < atLen; i++) { + at[i] = MKI(env, a[i]); + } + + *eAddr = MKTA(env, at, atLen); + + return NULL; +} + + + +/* +++ esock_decode_ip6_address +++ * * Decode a IPv6 address. This can be three things: * @@ -417,6 +675,42 @@ char* esock_decode_ip6_address(ErlNifEnv* env, #endif + +/* +++ esock_encode_ip6_address +++ + * + * Encode a IPv6 address: + * + * + An ip6_address() (8 tuple) + * + * Note that this *only* encodes the "address" part of a + * (IPv6) socket address. There are several other things + * (port, flowinfo and scope_id) that are handled elsewhere). + */ + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_encode_ip6_address(ErlNifEnv* env, + struct in6_addr* addrP, + ERL_NIF_TERM* eAddr) +{ + unsigned int i; + ERL_NIF_TERM at[8]; + unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); + unsigned char* a = (unsigned char*) &addrP; + + /* The address */ + for (i = 0; i < atLen; i++) { + at[i] = MKI(env, get_int16(a + i*2)); + } + + *eAddr = MKTA(env, at, atLen); + + return NULL; +} +#endif + + + /* +++ esock_decode_domain +++ * * Decode the Erlang form of the 'domain' type, that is: @@ -584,6 +878,36 @@ char* esock_encode_type(ErlNifEnv* env, +/* *** esock_decode_bool *** + * + * Decode a boolean value. + * + */ +extern +BOOLEAN_T esock_decode_bool(ERL_NIF_TERM val) +{ + if (COMPARE(esock_atom_true, val) == 0) + return TRUE; + else + return FALSE; +} + + +/* *** esock_encode_bool *** + * + * Encode a boolean value. + * + */ +extern +ERL_NIF_TERM esock_encode_bool(BOOLEAN_T val) +{ + if (val) + return esock_atom_true; + else + return esock_atom_false; +} + + /* Create an ok two (2) tuple in the form: * * {ok, Any} @@ -657,3 +981,119 @@ ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err) } + +/* strnlen doesn't exist everywhere */ +extern +size_t esock_strnlen(const char *s, size_t maxlen) +{ + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; +} + + + +/* *** esock_abort *** + * + * Generate an abort with "extra" info. This should be called + * via the ESOCK_ABORT macro. + * Basically it prints the extra info onto stderr before aborting. + * + */ +extern +void esock_abort(const char* expr, + const char* func, + const char* file, + int line) +{ + fflush(stdout); + fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", + file, line, func, expr); + fflush(stderr); + abort(); +} + + + + +/* =================================================================== * + * * + * Various utility functions * + * * + * =================================================================== */ + +/* Construct the IPv4 socket address */ +static +char* make_sockaddr_in4(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM* sa) +{ + ERL_NIF_TERM keys[] = {esock_atom_family, esock_atom_port, esock_atom_addr}; + ERL_NIF_TERM vals[] = {esock_atom_inet, port, addr}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, sa)) { + *sa = esock_atom_undefined; + return ESOCK_STR_EINVAL; + } else { + return NULL; + } +} + + +/* Construct the IPv6 socket address */ +static +char* make_sockaddr_in6(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM flowInfo, + ERL_NIF_TERM scopeId, + ERL_NIF_TERM* sa) +{ + ERL_NIF_TERM keys[] = {esock_atom_family, + esock_atom_port, + esock_atom_addr, + esock_atom_flowinfo, + esock_atom_scope_id}; + ERL_NIF_TERM vals[] = {esock_atom_inet6, port, addr, flowInfo, scopeId}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, sa)) { + *sa = esock_atom_undefined; + return ESOCK_STR_EINVAL; + } else { + return NULL; + } +} + + +/* Construct the Unix Domain socket address */ +static +char* make_sockaddr_un(ErlNifEnv* env, + ERL_NIF_TERM path, + ERL_NIF_TERM* sa) +{ + ERL_NIF_TERM keys[] = {esock_atom_family, esock_atom_path}; + ERL_NIF_TERM vals[] = {esock_atom_inet, path}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, sa)) { + *sa = esock_atom_undefined; + return ESOCK_STR_EINVAL; + } else { + return NULL; + } +} + + diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index babfebc17b..f5594c408e 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -29,17 +29,34 @@ #include #include "socket_int.h" +#define ESOCK_ABORT(E) esock_abort(E, __func__, __FILE__, __LINE__) +#define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0))) + +/* Two byte integer decoding */ +#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ + (((unsigned char*) (s))[1])) + extern char* esock_decode_sockaddr(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, SocketAddress* sockAddrP, unsigned int* addrLen); +extern +char* esock_encode_sockaddr(ErlNifEnv* env, + SocketAddress* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr); extern char* esock_decode_sockaddr_in4(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, struct sockaddr_in* sockAddrP, unsigned int* addrLen); +extern +char* esock_encode_sockaddr_in4(ErlNifEnv* env, + struct sockaddr_in* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr); #if defined(HAVE_IN6) && defined(AF_INET6) extern @@ -47,6 +64,11 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, struct sockaddr_in6* sockAddrP, unsigned int* addrLen); +extern +char* esock_encode_sockaddr_in6(ErlNifEnv* env, + struct sockaddr_in6* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr); #endif #ifdef HAVE_SYS_UN_H @@ -55,6 +77,11 @@ char* esock_decode_sockaddr_un(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, struct sockaddr_un* sockAddrP, unsigned int* addrLen); +extern +char* esock_encode_sockaddr_un(ErlNifEnv* env, + struct sockaddr_un* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr); #endif extern @@ -62,6 +89,10 @@ char* esock_decode_ip4_address(ErlNifEnv* env, ERL_NIF_TERM eAddr, struct sockaddr_in* sockAddrP, unsigned int* addrLen); +extern +char* esock_encode_ip4_address(ErlNifEnv* env, + struct in_addr* addrP, + ERL_NIF_TERM* eAddr); #if defined(HAVE_IN6) && defined(AF_INET6) extern @@ -69,6 +100,10 @@ char* esock_decode_ip6_address(ErlNifEnv* env, ERL_NIF_TERM eAddr, struct sockaddr_in6* sockAddrP, unsigned int* addrLen); +extern +char* esock_encode_ip6_address(ErlNifEnv* env, + struct in6_addr* addrP, + ERL_NIF_TERM* eAddr); #endif extern @@ -91,6 +126,18 @@ char* esock_encode_type(ErlNifEnv* env, int type, ERL_NIF_TERM* eType); +extern +BOOLEAN_T esock_decode_bool(ERL_NIF_TERM val); +extern +ERL_NIF_TERM esock_encode_bool(BOOLEAN_T val); + +extern +size_t esock_strnlen(const char *s, size_t maxlen); +extern +void esock_abort(const char* expr, + const char* func, + const char* file, + int line); extern ERL_NIF_TERM esock_make_ok2(ErlNifEnv* env, ERL_NIF_TERM any); -- cgit v1.2.3 From b63a16d0958bd748644d22f13f35f8956a903d6c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 18 Jun 2018 18:19:39 +0200 Subject: [socket+net-nif] Moved common functions into util files The common stuff, like decode and encode of common types (soch as socket address), has been moved into a util file (socket_util). The debug stuff has also been moved into its own file. Also introduced a common include file for common macros and types. OTP-14831 --- erts/emulator/nifs/common/net_nif.c | 87 +---------- erts/emulator/nifs/common/socket_nif.c | 266 ++++++++++++-------------------- erts/emulator/nifs/common/socket_util.c | 168 +++++++++++++++++++- erts/emulator/nifs/common/socket_util.h | 11 +- 4 files changed, 272 insertions(+), 260 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 9a96eff654..572813ac62 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -1390,7 +1390,7 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env, } -/* Convert an "native" family to an erlang family +/* Convert an "native" family to an erlang family (=domain). * Note that this is not currently exhaustive, but only supports * inet and inet6. Other values will be returned as is, that is * in the form of an integer. @@ -1401,34 +1401,15 @@ ERL_NIF_TERM encode_address_info_family(ErlNifEnv* env, { ERL_NIF_TERM efam; - switch (family) { - case AF_INET: - efam = esock_atom_inet; - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - efam = esock_atom_inet6; - break; -#endif - - #ifdef HAVE_SYS_UN_H - case AF_UNIX: - efam = esock_atom_local; - break; -#endif - - default: + if (NULL != esock_encode_type(env, family, &efam)) efam = MKI(env, family); - break; - } return efam; } -/* Convert an "native" socket type to an erlang socket type +/* Convert an "native" socket type to an erlang socket type. * Note that this is not currently exhaustive, but only supports * stream and dgram. Other values will be returned as is, that is * in the form of an integer. @@ -1439,38 +1420,15 @@ ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env, { ERL_NIF_TERM etype; - switch (socktype) { - case SOCK_STREAM: - etype = esock_atom_stream; - break; - - case SOCK_DGRAM: - etype = esock_atom_dgram; - break; - - case SOCK_RAW: - etype = esock_atom_raw; - break; - - case SOCK_RDM: - etype = esock_atom_rdm; - break; - - case SOCK_SEQPACKET: - etype = esock_atom_seqpacket; - break; - - default: + if (NULL != esock_encode_type(env, socktype, &etype)) etype = MKI(env, socktype); - break; - } return etype; } -/* Convert an "native" protocol to an erlang protocol +/* Convert an "native" protocol to an erlang protocol. * Note that this is not currently exhaustive, but only supports * tcp and udp. Other values will be returned as is, that is * in the form of an integer. @@ -1481,39 +1439,8 @@ ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, { ERL_NIF_TERM eproto; - switch (proto) { -#if defined(SOL_IP) - case SOL_IP: -#else - case IPPROTO_IP: -#endif - eproto = esock_atom_ip; - break; - -#if defined(SOL_IPV6) - case SOL_IPV6: - eproto = esock_atom_ipv6; - break; -#endif - - case IPPROTO_TCP: - eproto = esock_atom_tcp; - break; - - case IPPROTO_UDP: - eproto = esock_atom_udp; - break; - -#if defined(HAVE_SCTP) - case IPPROTO_SCTP: - eproto = esock_atom_sctp; - break; -#endif - - default: + if (NULL != esock_encode_protocol(env, proto, &eproto)) eproto = MKI(env, proto); - break; - } return eproto; } @@ -1615,7 +1542,7 @@ static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { // We should make it possible to use load_info to get default values - data.debug = FALSE; + data.debug = TRUE; NDBG( ("NET", "on_load -> entry\r\n") ); diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index d2455a7b3a..848e502ec5 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -757,7 +757,8 @@ static ERL_NIF_TERM nopen(ErlNifEnv* env, char* netns); static ERL_NIF_TERM nbind(ErlNifEnv* env, SocketDescriptor* descP, - ERL_NIF_TERM addr); + SocketAddress* sockAddrP, + unsigned int addrLen); static ERL_NIF_TERM nconnect(ErlNifEnv* env, SocketDescriptor* descP); static ERL_NIF_TERM nlisten(ErlNifEnv* env, @@ -1131,7 +1132,7 @@ static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, static ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, SocketDescriptor* descP); - +/* static char* decode_in_sockaddr(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, SocketAddress* sockAddrP, @@ -1162,7 +1163,7 @@ static char* decode_in6_sockaddr_atomaddr(ErlNifEnv* env, unsigned int scopeId, SocketAddress* sockAddrP, unsigned int* addrLenP); -/* Decode an in6_sockaddr where the address field is a tuple */ +/ * Decode an in6_sockaddr where the address field is a tuple * / static char* decode_in6_sockaddr_addr(ErlNifEnv* env, ERL_NIF_TERM eAddr, int port, @@ -1171,6 +1172,8 @@ static char* decode_in6_sockaddr_addr(ErlNifEnv* env, SocketAddress* sockAddrP, unsigned int* addrLenP); #endif +*/ +/* static char* decode_laddress(ErlNifEnv* env, int domain, ERL_NIF_TERM localAddr, @@ -1181,6 +1184,7 @@ static char* decode_laddress_binary(ErlNifEnv* env, ERL_NIF_TERM localAddr, SocketAddress* localP, unsigned int* addrLenP); +*/ /* static char* decode_address_tuple(ErlNifEnv* env, int domain, @@ -1212,11 +1216,13 @@ static char* decode_send_addr_tuple(ErlNifEnv* env, SocketAddress* toAddrP, unsigned int* addrLenP); */ +/* static void encode_address(ErlNifEnv* env, SocketAddress* fromAddrP, unsigned int fromAddrLen, ERL_NIF_TERM* fromDomainT, ERL_NIF_TERM* fromSourceT); +*/ static BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* valP); @@ -1276,9 +1282,11 @@ static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err); static BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc); +/* #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) static size_t my_strnlen(const char *s, size_t maxlen); #endif +*/ static void socket_dtor(ErlNifEnv* env, void* obj); static void socket_stop(ErlNifEnv* env, @@ -1875,16 +1883,8 @@ BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err) * Bind a name to a socket. * * Arguments: - * Socket (ref) - Points to the socket descriptor. - * LocalAddr - Local address is either: - * - a binary - when the socket domain = local - * - a tuple of size 2 with - * - first element is the address - * - second element is the port number - * The address can be in the form of either: - * - A tuple of size 4 or 8 (depending on domain) - * - The atom 'any' - * - The atom 'loopback' + * [0] Socket (ref) - Points to the socket descriptor. + * [1] LocalAddr - Local address is a sockaddr map ( socket:sockaddr() ). */ static ERL_NIF_TERM nif_bind(ErlNifEnv* env, @@ -1892,6 +1892,12 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { SocketDescriptor* descP; + ERL_NIF_TERM eSockAddr; + SocketAddress sockAddr; + unsigned int addrLen; + char* xres; + + SGDBG( ("SOCKET", "nif_bind -> entry with argc: %d\r\n", argc) ); /* Extract arguments and perform preliminary validation */ @@ -1899,25 +1905,13 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { return enif_make_badarg(env); } + eSockAddr = argv[1]; SSDBG( descP, - ("SOCKET", "nif_bind -> " - "\r\n Socket: %T" - "\r\n Addr: %T" - "\r\n", argv[0], argv[1]) ); - - /* Basic arg validation: - * - if binary domain must be local (unix) - * - if tuple domain must be either inet or inet6 - */ - if (IS_BIN(env, argv[1]) && (descP->domain != AF_UNIX)) { - return enif_make_badarg(env); - } else if (IS_TUPLE(env, argv[1]) && - (descP->domain != AF_INET) && - (descP->domain != AF_INET6)) { - return enif_make_badarg(env); - } - + ("SOCKET", "nif_bind -> args:" + "\r\n Socket: %T" + "\r\n SockAddr: %T" + "\r\n", argv[0], eSockAddr) ); /* Make sure we are ready * Not sure how this would even happen, but... @@ -1926,42 +1920,35 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, if (descP->state != SOCKET_STATE_OPEN) return esock_make_error(env, atom_exbadstate); - return nbind(env, descP, argv[1]); + if ((xres = esock_decode_sockaddr(env, eSockAddr, &sockAddr, &addrLen)) != NULL) + return esock_make_error_str(env, xres); + + return nbind(env, descP, &sockAddr, addrLen); } static ERL_NIF_TERM nbind(ErlNifEnv* env, SocketDescriptor* descP, - ERL_NIF_TERM addr) + SocketAddress* sockAddrP, + unsigned int addrLen) { - SocketAddress local; - unsigned int addrLen = 0; - char* xerr; - int port; - - SSDBG( descP, - ("SOCKET", "nbind -> entry with" - "\r\n addr: %T" - "\r\n", addr) ); - - if ((xerr = decode_laddress(env, - descP->domain, addr, &local, &addrLen)) != NULL) - return esock_make_error_str(env, xerr); + int port; SSDBG( descP, ("SOCKET", "nbind -> try bind\r\n") ); if (IS_SOCKET_ERROR(sock_bind(descP->sock, - (struct sockaddr*) &local, addrLen))) { + (struct sockaddr*) sockAddrP, addrLen))) { return esock_make_error_errno(env, sock_errno()); } - port = which_address_port(&local); + port = which_address_port(sockAddrP); + SSDBG( descP, ("SOCKET", "nbind -> port: %d\r\n", port) ); if (port == 0) { - SOCKLEN_T len = sizeof(local); - sys_memzero((char *) &local, len); - sock_name(descP->sock, &local.sa, &len); - port = which_address_port(&local); + SOCKLEN_T len = sizeof(SocketAddress); + sys_memzero((char *) sockAddrP, len); + sock_name(descP->sock, &sockAddrP->sa, &len); + port = which_address_port(sockAddrP); } else if (port == -1) { port = 0; } @@ -1973,83 +1960,6 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, } -/* Decode the (local) address. The format of the address should - * either be an binary (domain = local) or an in_sockaddr(), which - * is either a in4_sockaddr record or a int6_sockaddr record - * (if domain is either inet or inet6). - */ -static -char* decode_laddress(ErlNifEnv* env, - int domain, - ERL_NIF_TERM localAddr, - SocketAddress* localP, - unsigned int* addrLenP) -{ - if (IS_BIN(env, localAddr)) { - return decode_laddress_binary(env, domain, localAddr, localP, addrLenP); - } else if (IS_TUPLE(env, localAddr)) { - return decode_in_sockaddr(env, localAddr, localP, addrLenP); - } else { - return ESOCK_STR_EINVAL; - } - -} - - -/* Only for domain = local (unix) - * The erlang interface module (socket) ensures that the size of the - * binary is > 0, so we need not do that here. - */ -static -char* decode_laddress_binary(ErlNifEnv* env, - int domain, - ERL_NIF_TERM localAddr, - SocketAddress* localP, - unsigned int* addrLenP) -{ - unsigned int addrLen; - -#ifdef HAVE_SYS_UN_H - ErlNifBinary bin; - - if (domain != AF_UNIX) - return ESOCK_STR_EINVAL; - - if (!GET_BIN(env, localAddr, &bin)) - return ESOCK_STR_EINVAL; - - if ((bin.size + -#ifdef __linux__ - /* Make sure the address gets zero terminated - * except when the first byte is \0 because then it is - * sort of zero terminated although the zero termination - * comes before the address... - * This fix handles Linux's nonportable - * abstract socket address extension. - */ - (bin.data[0] == '\0' ? 0 : 1) -#else - 1 -#endif - ) > sizeof(localP->un.sun_path)) - return ESOCK_STR_EINVAL; - - sys_memzero((char*)localP, sizeof(struct sockaddr_un)); - localP->un.sun_family = domain; - sys_memcpy(localP->un.sun_path, bin.data, bin.size); - addrLen = offsetof(struct sockaddr_un, sun_path) + bin.size; -#ifndef NO_SA_LEN - localP->un.sun_len = addrLen; -#endif - *addrLenP = addrLen; - return NULL; - -#else // HAVE_SYS_UN_H - return str_eafnosupport; -#endif - -} - /* ---------------------------------------------------------------------- @@ -2061,9 +1971,8 @@ char* decode_laddress_binary(ErlNifEnv* env, * Arguments: * Socket (ref) - Points to the socket descriptor. * SockAddr - Socket Address of "remote" host. - * This is in_sockaddr(), which is either - * in4_sockaddr (#in4_sockaddr{}) or - * in6_sockaddr (#in6_sockaddr{}). + * This is sockaddr(), which is either + * sockaddr_in4 or sockaddr_in6. */ static ERL_NIF_TERM nif_connect(ErlNifEnv* env, @@ -2082,8 +1991,8 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, } eSockAddr = argv[1]; - if ((xres = decode_in_sockaddr(env, eSockAddr, - &descP->remote, &descP->addrLen)) != NULL) { + if ((xres = esock_decode_sockaddr(env, eSockAddr, + &descP->remote, &descP->addrLen)) != NULL) { return esock_make_error_str(env, xres); } @@ -2203,6 +2112,7 @@ BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err) */ #ifndef SO_ERROR + int sz, code; sz = sizeof(descP->remote); @@ -2689,7 +2599,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. - * DestSockAddr - Destination (socket) address. + * Dest - Destination (socket) address. */ static @@ -2725,9 +2635,9 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, if (!esendflags2sendflags(eflags, &flags)) return esock_make_error(env, esock_atom_einval); - if ((xres = decode_in_sockaddr(env, eSockAddr, - &remoteAddr, - &remoteAddrLen)) != NULL) + if ((xres = esock_decode_sockaddr(env, eSockAddr, + &remoteAddr, + &remoteAddrLen)) != NULL) return esock_make_error_str(env, xres); return nsendto(env, descP, sendRef, &data, flags, &remoteAddr, remoteAddrLen); @@ -5404,11 +5314,11 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, /* +++ We sucessfully got a message - time to encode the address +++ */ - ERL_NIF_TERM fromDomainT, fromSourceT; + ERL_NIF_TERM eSockAddr; - encode_address(env, - fromAddrP, fromAddrLen, - &fromDomainT, &fromSourceT); + esock_encode_sockaddr(env, + fromAddrP, fromAddrLen, + &eSockAddr); if (read == bufP->size) { data = MKBIN(env, bufP); @@ -5424,7 +5334,7 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, data = MKSBIN(env, data, 0, read); } - return esock_make_ok2(env, MKT3(env, fromDomainT, fromSourceT, data)); + return esock_make_ok2(env, MKT2(env, eSockAddr, data)); } } @@ -5539,6 +5449,7 @@ char* decode_send_addr_tuple(ErlNifEnv* env, * 5: Scope Id: integer() * */ +/* static char* decode_in_sockaddr(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, @@ -5552,10 +5463,10 @@ char* decode_in_sockaddr(ErlNifEnv* env, if (!GET_TUPLE(env, eSockAddr, &addrtSz, &addrt)) return ESOCK_STR_EINVAL; - /* + / * * 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); @@ -5574,7 +5485,7 @@ char* decode_in_sockaddr(ErlNifEnv* env, return result; } - +*/ /* Decode an in4_sockaddr(). @@ -5582,6 +5493,7 @@ char* decode_in_sockaddr(ErlNifEnv* env, * The second, the port number integer . * The third and final, the ip4_address tuple. */ +/* static char* decode_in4_sockaddr(ErlNifEnv* env, const ERL_NIF_TERM* eIn4SockAddr, @@ -5590,18 +5502,18 @@ char* decode_in4_sockaddr(ErlNifEnv* env, { int port; - /* 1: Ensure that the tuple has the correct tag: in4_sockaddr */ + / * 1: Ensure that the tuple has the correct tag: in4_sockaddr * / if (COMPARE(atom_in4_sockaddr, eIn4SockAddr[0]) != 0) return ESOCK_STR_EINVAL; - /* 2: Get the port number */ + / * 2: Get the port number * / if (!GET_INT(env, eIn4SockAddr[1], &port)) return ESOCK_STR_EINVAL; - /* 3: Get the address. + / * 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); @@ -5612,9 +5524,10 @@ char* decode_in4_sockaddr(ErlNifEnv* env, return ESOCK_STR_EINVAL; } } +*/ - +/* static char* decode_in4_sockaddr_atomaddr(ErlNifEnv* env, ERL_NIF_TERM eAddr, @@ -5643,11 +5556,13 @@ char* decode_in4_sockaddr_atomaddr(ErlNifEnv* env, return NULL; } +*/ /* Decode an in4_sockaddr where the address field is a tuple. * Its *supposed* to be an ip4_address (tuple). */ +/* static char* decode_in4_sockaddr_addr(ErlNifEnv* env, ERL_NIF_TERM eAddr, @@ -5660,7 +5575,7 @@ char* decode_in4_sockaddr_addr(ErlNifEnv* env, int a, v; char addr[4]; - /* This shall be a 4 tuple */ + / * This shall be a 4 tuple * / if (!GET_TUPLE(env, eAddr, &ip4AddrTSz, &ip4AddrT)) return ESOCK_STR_EINVAL; @@ -5683,6 +5598,7 @@ char* decode_in4_sockaddr_addr(ErlNifEnv* env, return NULL; } +*/ @@ -5693,6 +5609,7 @@ char* decode_in4_sockaddr_addr(ErlNifEnv* env, * The forth, the flowinfo integer. * The fifth and final, the scope_id integer. */ +/* #if defined(HAVE_IN6) && defined(AF_INET6) static char* decode_in6_sockaddr(ErlNifEnv* env, @@ -5703,26 +5620,26 @@ char* decode_in6_sockaddr(ErlNifEnv* env, int port; unsigned int flowInfo, scopeId; - /* 1: Ensure that the tuple has the correct tag: in6_sockaddr */ + / * 1: Ensure that the tuple has the correct tag: in6_sockaddr * / if (COMPARE(atom_in6_sockaddr, eIn6SockAddr[0]) != 0) return ESOCK_STR_EINVAL; - /* 2: Get the port number */ + / * 2: Get the port number * / if (!GET_INT(env, eIn6SockAddr[1], &port)) return ESOCK_STR_EINVAL; - /* 4: Get the flowinfo */ + / * 4: Get the flowinfo * / if (!GET_UINT(env, eIn6SockAddr[3], &flowInfo)) return ESOCK_STR_EINVAL; - /* 5: Get the scope_id */ + / * 5: Get the scope_id * / if (!GET_UINT(env, eIn6SockAddr[4], &scopeId)) return ESOCK_STR_EINVAL; - /* 3: Get the address. + / * 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, @@ -5736,8 +5653,10 @@ char* decode_in6_sockaddr(ErlNifEnv* env, } } #endif +*/ +/* #if defined(HAVE_IN6) && defined(AF_INET6) static char* decode_in6_sockaddr_atomaddr(ErlNifEnv* env, @@ -5772,11 +5691,13 @@ char* decode_in6_sockaddr_atomaddr(ErlNifEnv* env, return NULL; } #endif +*/ #if defined(HAVE_IN6) && defined(AF_INET6) /* Decode an in6_sockaddr where the address field is a tuple */ +/* static char* decode_in6_sockaddr_addr(ErlNifEnv* env, ERL_NIF_TERM eAddr, @@ -5791,7 +5712,7 @@ char* decode_in6_sockaddr_addr(ErlNifEnv* env, int a, v; char addr[16]; - /* This shall be a 8 tuple */ + / * This shall be a 8 tuple * / if (!GET_TUPLE(env, eAddr, &ip6AddrTSz, &ip6AddrT)) return ESOCK_STR_EINVAL; @@ -5806,9 +5727,9 @@ char* decode_in6_sockaddr_addr(ErlNifEnv* env, sockAddrP->in6.sin6_port = sock_htons(port); sockAddrP->in6.sin6_flowinfo = flowInfo; sockAddrP->in6.sin6_scope_id = scopeId; - /* The address tuple is of size 8 + / * 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 ESOCK_STR_EINVAL; @@ -5820,6 +5741,7 @@ char* decode_in6_sockaddr_addr(ErlNifEnv* env, return NULL; } +*/ #endif @@ -5912,6 +5834,7 @@ char* decode_address_tuple(ErlNifEnv* env, * Source: {Address, Port} | string() * */ +/* static void encode_address(ErlNifEnv* env, SocketAddress* sockAddrP, @@ -5923,7 +5846,7 @@ void encode_address(ErlNifEnv* env, switch (sockAddrP->sa.sa_family) { - /* +++ inet (IPv4) +++ */ + / * +++ inet (IPv4) +++ * / case AF_INET: if (addrLen >= sizeof(struct sockaddr_in)) { @@ -5948,7 +5871,7 @@ void encode_address(ErlNifEnv* env, break; - /* +++ inet6 (IPv6) +++ */ + / * +++ inet6 (IPv6) +++ * / #if defined(HAVE_IN6) && defined(AF_INET6) case AF_INET6: @@ -5959,9 +5882,9 @@ void encode_address(ErlNifEnv* env, char* a16 = (char*) &sockAddrP->in6.sin6_addr; port = sock_ntohs(sockAddrP->in6.sin6_port); - /* The address tuple is of size 8 + / * The address tuple is of size 8 * and each element is a two byte integer - */ + * / for (i = 0; i < 8; i++) { // at6[i] = MKI(env, get_int16(a16[i*2])); at6[i] = MKI(env, get_int16(a16 + i*2)); @@ -5980,7 +5903,7 @@ void encode_address(ErlNifEnv* env, break; #endif - /* +++ local (Unix Domain Sockets) +++ */ + / * +++ local (Unix Domain Sockets) +++ * / #ifdef HAVE_SYS_UN_H case AF_UNIX: @@ -5997,12 +5920,12 @@ void encode_address(ErlNifEnv* env, } else { m = my_strnlen(sockAddrP->un.sun_path, n); #ifdef __linux__ - /* Assume that the address is a zero terminated string, + / * Assume that the address is a zero terminated string, * except when the first byte is \0 i.e the string length is 0, * then use the reported length instead. * This fix handles Linux's nonportable * abstract socket address extension. - */ + * / if (m == 0) { m = n; } @@ -6020,9 +5943,10 @@ void encode_address(ErlNifEnv* env, *sourceT = esock_atom_undefined; break; - } /* switch (addrP->sa.sa_family) */ + } / * switch (addrP->sa.sa_family) * / } +*/ /* Decode the address when its an atom. @@ -7100,6 +7024,7 @@ char* decode_ip6_address(ErlNifEnv* env, #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) /* strnlen doesn't exist everywhere */ +/* static size_t my_strnlen(const char *s, size_t maxlen) { @@ -7108,6 +7033,7 @@ size_t my_strnlen(const char *s, size_t maxlen) i++; return i; } +*/ #endif @@ -7439,7 +7365,7 @@ ErlNifFunc socket_funcs[] = // {"nif_debug", 1, nif_debug_, 0}, // The proper "socket" interface - // This is used when we already have a file descriptor + // nif_open/1 is used when we already have a file descriptor // {"nif_open", 1, nif_open, 0}, {"nif_open", 4, nif_open, 0}, {"nif_bind", 2, nif_bind, 0}, @@ -7608,7 +7534,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_any = MKA(env, "any"); esock_atom_dgram = MKA(env, "dgram"); esock_atom_error = MKA(env, "error"); - esock_atom_false = MKA(env, "famlse"); + esock_atom_false = MKA(env, "false"); esock_atom_family = MKA(env, "family"); esock_atom_flowinfo = MKA(env, "flowinfo"); esock_atom_inet = MKA(env, "inet"); diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 37adee682b..05fb40e286 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -26,9 +26,23 @@ #include #include "socket_int.h" #include "socket_util.h" +#include "socket_dbg.h" #include "sys.h" +/* We don't have a "debug flag" to check here, so we + * should use the compile debug flag, whatever that is... + */ + +// #define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 +#if defined(COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK) +#define UTIL_DEBUG TRUE +#else +#define UTIL_DEBUG FALSE +#endif + +#define UDBG( proto ) ESOCK_DBG_PRINTF( UTIL_DEBUG , proto ) + extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */ @@ -69,15 +83,19 @@ char* esock_decode_sockaddr(ErlNifEnv* env, int fam; char* xres; + UDBG( ("SUTIL", "esock_decode_sockaddr -> entry\r\n") ); + if (!IS_MAP(env, eSockAddr)) return ESOCK_STR_EINVAL; if (!GET_MAP_VAL(env, eSockAddr, esock_atom_family, &efam)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr -> try decode domain (%T)\r\n", efam) ); if ((xres = esock_decode_domain(env, efam, &fam)) != NULL) return xres; + UDBG( ("SUTIL", "esock_decode_sockaddr -> fam: %d\r\n", fam) ); switch (fam) { case AF_INET: xres = esock_decode_sockaddr_in4(env, eSockAddr, @@ -147,7 +165,8 @@ char* esock_encode_sockaddr(ErlNifEnv* env, #endif default: - xres = ESOCK_STR_EAFNOSUPPORT; + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EAFNOSUPPORT; break; } @@ -178,6 +197,9 @@ char* esock_decode_sockaddr_in4(ErlNifEnv* env, { ERL_NIF_TERM eport, eaddr; int port; + char* xres; + + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> entry\r\n") ); /* Basic init */ sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); @@ -189,22 +211,28 @@ char* esock_decode_sockaddr_in4(ErlNifEnv* env, sockAddrP->sin_family = AF_INET; /* Extract (e) port number from map */ + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try get port number\r\n") ); if (!GET_MAP_VAL(env, eSockAddr, esock_atom_port, &eport)) return ESOCK_STR_EINVAL; /* Decode port number */ + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try decode port number\r\n") ); if (!GET_INT(env, eport, &port)) return ESOCK_STR_EINVAL; sockAddrP->sin_port = htons(port); /* Extract (e) address from map */ + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try get (ip) address\r\n") ); if (!GET_MAP_VAL(env, eSockAddr, esock_atom_addr, &eaddr)) return ESOCK_STR_EINVAL; /* Decode address */ - if (!esock_decode_ip4_address(env, eaddr, sockAddrP, addrLen)) - return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try decode (ip) address\r\n") ); + if ((xres = esock_decode_ip4_address(env, eaddr, sockAddrP, addrLen)) != NULL) + return xres; + + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> done\r\n") ); return NULL; } @@ -232,6 +260,8 @@ char* esock_encode_sockaddr_in4(ErlNifEnv* env, int port; char* xres = NULL; + UDBG( ("SUTIL", "esock_encode_sockaddr_in4 -> entry\r\n") ); + if (addrLen >= sizeof(struct sockaddr_in)) { /* The port */ port = ntohs(sockAddrP->sin_port); @@ -239,15 +269,23 @@ char* esock_encode_sockaddr_in4(ErlNifEnv* env, /* The address */ if ((xres = esock_encode_ip4_address(env, &sockAddrP->sin_addr, - &eAddr)) != NULL) { + &eAddr)) == NULL) { /* And finally construct the in4_sockaddr record */ xres = make_sockaddr_in4(env, ePort, eAddr, eSockAddr); } else { + UDBG( ("SUTIL", "esock_encode_sockaddr_in4 -> " + "failed encoding (ip) address: " + "\r\n xres: %s" + "\r\n", xres) ); *eSockAddr = esock_atom_undefined; xres = ESOCK_STR_EINVAL; } } else { + UDBG( ("SUTIL", "esock_encode_sockaddr_in4 -> wrong size: " + "\r\n addrLen: %d" + "\r\n addr size: %d" + "\r\n", addrLen, sizeof(struct sockaddr_in)) ); *eSockAddr = esock_atom_undefined; xres = ESOCK_STR_EINVAL; } @@ -282,6 +320,7 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, ERL_NIF_TERM eport, eaddr, eflowInfo, escopeId; int port; unsigned int flowInfo, scopeId; + char* xres; /* Basic init */ sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); @@ -326,8 +365,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, return ESOCK_STR_EINVAL; /* Decode address */ - if (!esock_decode_ip6_address(env, eaddr, sockAddrP, addrLen)) - return ESOCK_STR_EINVAL; + if ((xres = esock_decode_ip6_address(env, eaddr, sockAddrP, addrLen)) != NULL) + return xres; return NULL; } @@ -370,7 +409,7 @@ char* esock_encode_sockaddr_in6(ErlNifEnv* env, /* The address */ if ((xres = esock_encode_ip6_address(env, &sockAddrP->sin6_addr, - &eAddr)) != NULL) { + &eAddr)) == NULL) { /* And finally construct the in6_sockaddr record */ xres = make_sockaddr_in6(env, ePort, eAddr, eFlowInfo, eScopeId, eSockAddr); @@ -532,15 +571,22 @@ char* esock_decode_ip4_address(ErlNifEnv* env, struct sockaddr_in* sockAddrP, unsigned int* addrLen) { + UDBG( ("SUTIL", "esock_decode_ip4_address -> entry with" + "\r\n eAddr: %T" + "\r\n", eAddr) ); + if (IS_ATOM(env, eAddr)) { /* This is either 'any' or 'loopback' */ struct in_addr addr; if (COMPARE(esock_atom_loopback, eAddr) == 0) { + UDBG( ("SUTIL", "esock_decode_ip4_address -> address: lookback\r\n") ); addr.s_addr = htonl(INADDR_LOOPBACK); } else if (COMPARE(esock_atom_any, eAddr) == 0) { + UDBG( ("SUTIL", "esock_decode_ip4_address -> address: any\r\n") ); addr.s_addr = htonl(INADDR_ANY); } else { + UDBG( ("SUTIL", "esock_decode_ip4_address -> address: unknown\r\n") ); return ESOCK_STR_EINVAL; } @@ -596,13 +642,17 @@ char* esock_encode_ip4_address(ErlNifEnv* env, ERL_NIF_TERM at[4]; unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); unsigned char* a = (unsigned char*) addrP; + ERL_NIF_TERM addr; /* The address */ for (i = 0; i < atLen; i++) { at[i] = MKI(env, a[i]); } - *eAddr = MKTA(env, at, atLen); + addr = MKTA(env, at, atLen); + UDBG( ("SUTIL", "esock_encode_ip4_address -> addr: %T\r\n", addr) ); + // *eAddr = MKTA(env, at, atLen); + *eAddr = addr; return NULL; } @@ -878,6 +928,108 @@ char* esock_encode_type(ErlNifEnv* env, +/* +++ esock_decode_protocol +++ + * + * Encode the native protocol to the Erlang form, that is: + * + * SOL_IP | IPPROTO_IP => ip + * SOL_IPV6 => ipv6 + * SOL_TCP => tcp + * SOL_UDP => udp + * SOL_SCTP => sctp + * + */ +extern +char* esock_encode_protocol(ErlNifEnv* env, + int proto, + ERL_NIF_TERM* eProto) +{ + char* xres = NULL; + + switch (proto) { +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + *eProto = esock_atom_ip; + break; + +#if defined(SOL_IPV6) + case SOL_IPV6: + *eProto = esock_atom_ipv6; + break; +#endif + + case IPPROTO_TCP: + *eProto = esock_atom_tcp; + break; + + case IPPROTO_UDP: + *eProto = esock_atom_udp; + break; + +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + *eProto = esock_atom_sctp; + break; +#endif + + default: + *eProto = esock_atom_undefined; + xres = ESOCK_STR_EAFNOSUPPORT; + break; + } + + return xres; +} + + + +/* +++ esock_decode_protocol +++ + * + * Decode the Erlang form of the 'protocol' type, that is: + * + * ip => SOL_IP | IPPROTO_IP + * ipv6 => SOL_IPV6 + * tcp => SOL_TCP + * udp => SOL_UDP + * sctp => SOL_SCTP + * + */ +extern +char* esock_decode_protocol(ErlNifEnv* env, + ERL_NIF_TERM eProto, + int* proto) +{ + char* xres = NULL; + + if (COMPARE(esock_atom_ip, eProto) == 0) { +#if defined(SOL_IP) + *proto = SOL_IP; +#else + *proto = IPPROTO_IP; +#endif + } else if (COMPARE(esock_atom_ipv6, eProto) == 0) { + *proto = SOL_IPV6; + } else if (COMPARE(esock_atom_tcp, eProto) == 0) { + *proto = IPPROTO_TCP; + } else if (COMPARE(esock_atom_udp, eProto) == 0) { + *proto = IPPROTO_UDP; +#if defined(HAVE_SCTP) + } else if (COMPARE(esock_atom_sctp, eProto) == 0) { + *proto = IPPROTO_SCTP; +#endif + } else { + *proto = -1; + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + /* *** esock_decode_bool *** * * Decode a boolean value. diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index f5594c408e..dedeb8dd7d 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -110,7 +110,6 @@ extern char* esock_decode_domain(ErlNifEnv* env, ERL_NIF_TERM eDomain, int* domain); - extern char* esock_encode_domain(ErlNifEnv* env, int domain, @@ -120,12 +119,20 @@ extern char* esock_decode_type(ErlNifEnv* env, ERL_NIF_TERM eType, int* type); - extern char* esock_encode_type(ErlNifEnv* env, int type, ERL_NIF_TERM* eType); +extern +char* esock_decode_protocol(ErlNifEnv* env, + ERL_NIF_TERM eProtocol, + int* protocol); +extern +char* esock_encode_protocol(ErlNifEnv* env, + int type, + ERL_NIF_TERM* eProtocol); + extern BOOLEAN_T esock_decode_bool(ERL_NIF_TERM val); extern -- cgit v1.2.3 From b7b80a767b2938014aa4cfb0c65d4ce5a9019bd5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 28 Jun 2018 13:50:52 +0200 Subject: [socket-nif] Fixed accept The pid compare test was incorrect. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 150 +++++++++++++++++---------------- 1 file changed, 76 insertions(+), 74 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 848e502ec5..7f45fb7bcd 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -282,6 +282,7 @@ typedef union { struct { + // 0 = not open, 1 = open unsigned int open:1; // 0 = not conn, 1 = connecting, 2 = connected unsigned int connect:2; @@ -377,63 +378,11 @@ typedef union { * * * =================================================================== */ -/* #define MALLOC(SZ) enif_alloc((SZ)) */ -/* #define FREE(P) enif_free((P)) */ - -/* #define MKA(E,S) enif_make_atom((E), (S)) */ -/* #define MKBIN(E,B) enif_make_binary((E), (B)) */ -/* #define MKI(E,I) enif_make_int((E), (I)) */ -/* #define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) */ -/* #define MKMA(E,KA,VA,L,M) enif_make_map_from_arrays((E), (KA), (VA), (L), (M)) */ -/* #define MKREF(E) enif_make_ref((E)) */ -/* #define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) */ -/* #define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) */ -/* #define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) */ -/* #define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) */ -/* #define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) */ -/* #define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) */ -/* #define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ */ -/* enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) */ - -/* #define MCREATE(N) enif_mutex_create((N)) */ -/* #define MDESTROY(M) enif_mutex_destroy((M)) */ -/* #define MLOCK(M) enif_mutex_lock((M)) */ -/* #define MUNLOCK(M) enif_mutex_unlock((M)) */ - -/* #define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) */ -/* #define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) */ - -/* #define SELECT(E,FD,M,O,P,R) \ */ -/* 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_MAP(E, TE) enif_is_map((E), (TE)) */ -/* #define IS_NUM(E, TE) enif_is_number((E), (TE)) */ -/* #define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) */ - -/* #define GET_ATOM_LEN(E, TE, LP) \ */ -/* enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) */ -/* #define GET_ATOM(E, TE, BP, MAX) \ */ -/* enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) */ -/* #define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) */ -/* #define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) */ -/* #define GET_STR(E, L, B, SZ) \ */ -/* enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) */ -/* #define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) */ -/* #define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) */ - -/* #define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) */ -/* #define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) */ - - #define SGDBG( proto ) ESOCK_DBG_PRINTF( data.dbg , proto ) #define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto ) + /* =================================================================== * * * * Basic socket operations * @@ -521,22 +470,6 @@ static unsigned long one_value = 1; #define SOCKOPTLEN_T SOCKLEN_T #endif -/* The general purpose sockaddr * / -typedef union { - struct sockaddr in; - struct sockaddr_in in4; - -#ifdef HAVE_IN6 - struct sockaddr_in6 in6; -#endif - -#ifdef HAVE_SYS_UN_H - struct sockaddr_un un; -#endif - -} SocketAddress; -*/ - /* We can use the IPv4 def for this since the beginning * is the same for INET and INET6 */ #define which_address_port(sap) \ @@ -1933,7 +1866,7 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, SocketAddress* sockAddrP, unsigned int addrLen) { - int port; + int port, ntohs_port; SSDBG( descP, ("SOCKET", "nbind -> try bind\r\n") ); @@ -1953,9 +1886,11 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, port = 0; } - SSDBG( descP, ("SOCKET", "nbind -> done with port = %d\r\n", port) ); + ntohs_port = sock_ntohs(port); + + SSDBG( descP, ("SOCKET", "nbind -> done with port = %d\r\n", ntohs_port) ); - return esock_make_ok2(env, MKI(env, port)); + return esock_make_ok2(env, MKI(env, ntohs_port)); } @@ -1983,6 +1918,8 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, ERL_NIF_TERM eSockAddr; char* xres; + SGDBG( ("SOCKET", "nif_connect -> entry with argc: %d\r\n", argc) ); + /* Extract arguments and perform preliminary validation */ if ((argc != 2) || @@ -1991,6 +1928,12 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, } eSockAddr = argv[1]; + SSDBG( descP, + ("SOCKET", "nif_connect -> args when sock = %d:" + "\r\n Socket: %T" + "\r\n SockAddr: %T" + "\r\n", descP->sock, argv[0], eSockAddr) ); + if ((xres = esock_decode_sockaddr(env, eSockAddr, &descP->remote, &descP->addrLen)) != NULL) { return esock_make_error_str(env, xres); @@ -2166,6 +2109,8 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, SocketDescriptor* descP; int backlog; + SGDBG( ("SOCKET", "nif_listen -> entry with argc: %d\r\n", argc) ); + /* Extract arguments and perform preliminary validation */ if ((argc != 2) || @@ -2174,6 +2119,12 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, return enif_make_badarg(env); } + SSDBG( descP, + ("SOCKET", "nif_listen -> args when sock = %d:" + "\r\n Socket: %T" + "\r\n backlog: %d" + "\r\n", descP->sock, argv[0], backlog) ); + return nlisten(env, descP, backlog); } @@ -2219,6 +2170,8 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, SocketDescriptor* descP; ERL_NIF_TERM ref; + SGDBG( ("SOCKET", "nif_accept -> entry with argc: %d\r\n", argc) ); + /* Extract arguments and perform preliminary validation */ if ((argc != 2) || @@ -2226,6 +2179,12 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, return enif_make_badarg(env); } ref = argv[1]; + + SSDBG( descP, + ("SOCKET", "nif_accept -> args when sock = %d:" + "\r\n Socket: %T" + "\r\n ReqRef: %T" + "\r\n", descP->sock, argv[0], ref) ); return naccept(env, descP, ref); } @@ -2275,17 +2234,27 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, int save_errno; ErlNifPid caller; + SSDBG( descP, ("SOCKET", "naccept_listening -> get caller\r\n") ); + if (enif_self(env, &caller) == NULL) return esock_make_error(env, atom_exself); n = sizeof(remote); sys_memzero((char *) &remote, n); + SSDBG( descP, ("SOCKET", "naccept_listening -> try accept\r\n") ); accSock = sock_accept(descP->sock, (struct sockaddr*) &remote, &n); if (accSock == INVALID_SOCKET) { + save_errno = sock_errno(); + + SSDBG( descP, + ("SOCKET", + "naccept_listening -> accept failed (%d)\r\n", save_errno) ); + if (save_errno == ERRNO_BLOCK) { /* *** Try again later *** */ + SSDBG( descP, ("SOCKET", "naccept_listening -> would block\r\n") ); descP->currentAcceptor.pid = caller; if (MONP(env, descP, @@ -2328,6 +2297,9 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, return esock_make_error(env, esock_atom_eagain); } else { + SSDBG( descP, + ("SOCKET", + "naccept_listening -> errno: %d\r\n", save_errno) ); return esock_make_error_errno(env, save_errno); } @@ -2339,6 +2311,8 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, * We got one */ + SSDBG( descP, ("SOCKET", "naccept_listening -> accept success\r\n") ); + if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) { save_errno = sock_errno(); while ((sock_close(accSock) == INVALID_SOCKET) && @@ -2400,29 +2374,53 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, ErlNifPid caller; int save_errno; + SSDBG( descP, ("SOCKET", "naccept_accepting -> get caller\r\n") ); + if (enif_self(env, &caller) == NULL) return esock_make_error(env, atom_exself); - if (compare_pids(env, &descP->currentAcceptor.pid, &caller) != 0) { + SSDBG( descP, ("SOCKET", "naccept_accepting -> check: " + "are caller current acceptor:" + "\r\n Caller: %T" + "\r\n Current: %T" + "\r\n", caller, descP->currentAcceptor.pid) ); + + if (!compare_pids(env, &descP->currentAcceptor.pid, &caller)) { /* This will have to do until we implement the queue. * When we have the queue, we should simply push this request, * and instead return with eagain (the caller will then wait * for the select message). */ + + SSDBG( descP, + ("SOCKET", + "naccept_accepting -> not current acceptor: busy\r\n") ); + return esock_make_error(env, atom_exbusy); } n = sizeof(descP->remote); sys_memzero((char *) &remote, n); + SSDBG( descP, ("SOCKET", "naccept_accepting -> try accept\r\n") ); accSock = sock_accept(descP->sock, (struct sockaddr*) &remote, &n); if (accSock == INVALID_SOCKET) { + save_errno = sock_errno(); + + SSDBG( descP, + ("SOCKET", + "naccept_accepting -> accept failed (%d)\r\n", save_errno) ); + if (save_errno == ERRNO_BLOCK) { /* * Just try again, no real error, just a ghost trigger from poll, */ + SSDBG( descP, + ("SOCKET", + "naccept_accepting -> would block: try again\r\n") ); + SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), @@ -2430,6 +2428,9 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, return esock_make_error(env, esock_atom_eagain); } else { + SSDBG( descP, + ("SOCKET", + "naccept_accepting -> errno: %d\r\n", save_errno) ); return esock_make_error_errno(env, save_errno); } } else { @@ -2440,6 +2441,8 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, * We got one */ + SSDBG( descP, ("SOCKET", "naccept_accepting -> accept success\r\n") ); + if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) { save_errno = sock_errno(); while ((sock_close(accSock) == INVALID_SOCKET) && @@ -2480,7 +2483,6 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, accDescP->state = SOCKET_STATE_CONNECTED; - /* Here we should have the test if we have something in the queue. * And if so, pop it and copy the (waiting) acceptor, and then * make a new select with that info). -- cgit v1.2.3 From 24be0729fe3a1ccfd5f0713b565463d6557d8aa7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 29 Jun 2018 18:23:55 +0200 Subject: [socket-nif] Fixed (stream) recv Fixed handling of closed in the recv function. We still need to properly handle when we get 0 bytes of data for other types ock sockets then stream (its valid for dgram for instance). OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 1 + erts/emulator/nifs/common/socket_nif.c | 695 ++++++++++++++++++++++++-------- erts/emulator/nifs/common/socket_util.c | 89 +++- erts/emulator/nifs/common/socket_util.h | 3 + 4 files changed, 624 insertions(+), 164 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index d6a612cab6..a3e54360fe 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -192,6 +192,7 @@ extern ERL_NIF_TERM esock_atom_einval; #define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) #define GET_LIST_ELEM(E, L, HP, TP) enif_get_list_cell((E), (L), (HP), (TP)) #define GET_LIST_LEN(E, L, LP) enif_get_list_length((E), (L), (LP)) +#define GET_LPID(E, T, P) enif_get_local_pid((E), (T), (P)) #define GET_STR(E, L, B, SZ) \ enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) #define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 7f45fb7bcd..027155fc92 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -182,8 +182,8 @@ /* Debug stuff... */ -#define SOCKET_NIF_DEBUG_DEFAULT TRUE -#define SOCKET_DEBUG_DEFAULT TRUE +#define SOCKET_NIF_DEBUG_DEFAULT FALSE +#define SOCKET_DEBUG_DEFAULT FALSE /* Counters and stuff (Don't know where to sent this stuff anyway) */ #define SOCKET_NIF_IOW_DEFAULT FALSE @@ -344,6 +344,7 @@ typedef union { #define SOCKET_OPT_OTP_DEBUG 0 #define SOCKET_OPT_OTP_IOW 1 +#define SOCKET_OPT_OTP_CTRL_PROC 2 #define SOCKET_OPT_SOCK_BROADCAST 4 #define SOCKET_OPT_SOCK_DONTROUTE 7 @@ -594,16 +595,16 @@ typedef struct { ERL_NIF_TERM version; ERL_NIF_TERM buildDate; BOOLEAN_T dbg; - BOOLEAN_T iow; + BOOLEAN_T iow; ErlNifMutex* cntMtx; uint32_t numSockets; - uint32_t numTypeDGrams; uint32_t numTypeStreams; + uint32_t numTypeDGrams; uint32_t numTypeSeqPkgs; - uint32_t numDomainLocal; uint32_t numDomainInet; uint32_t numDomainInet6; + uint32_t numDomainLocal; uint32_t numProtoIP; uint32_t numProtoTCP; uint32_t numProtoUDP; @@ -751,6 +752,9 @@ static ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env, static ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); static ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -1045,11 +1049,13 @@ static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, ssize_t written, ssize_t dataSize, + int saveErrno, ERL_NIF_TERM sendRef); static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SocketDescriptor* descP, int read, int toRead, + int saveErrno, ErlNifBinary* bufP, ERL_NIF_TERM recvRef); static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, @@ -1214,6 +1220,11 @@ static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err); #endif static BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc); +static void cnt_dec(uint32_t* cnt, uint32_t dec); + +static void inc_socket(int domain, int type, int protocol); +static void dec_socket(int domain, int type, int protocol); + /* #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) @@ -1698,12 +1709,13 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, * * */ - SELECT(env, - event, - (ERL_NIF_SELECT_READ), - descP, NULL, esock_atom_undefined); + SELECT(env, + event, + (ERL_NIF_SELECT_READ), + descP, NULL, esock_atom_undefined); #endif + inc_socket(domain, type, protocol); return esock_make_ok2(env, res); } @@ -2515,21 +2527,31 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, { SocketDescriptor* descP; ERL_NIF_TERM sendRef; - ErlNifBinary data; + ErlNifBinary sndData; unsigned int eflags; int flags; ERL_NIF_TERM res; + SGDBG( ("SOCKET", "nif_send -> 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_BIN(env, argv[2], &data) || + !GET_BIN(env, argv[2], &sndData) || !GET_UINT(env, argv[3], &eflags)) { return enif_make_badarg(env); } sendRef = argv[1]; + SSDBG( descP, + ("SOCKET", "nif_send -> args when sock = %d:" + "\r\n Socket: %T" + "\r\n SendRef: %T" + "\r\n Size of data: %d" + "\r\n eFlags: %d" + "\r\n", descP->sock, argv[0], sendRef, sndData.size, eflags) ); + if (!IS_CONNECTED(descP)) return esock_make_error(env, atom_enotconn); @@ -2551,7 +2573,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, * time we do. */ - res = nsend(env, descP, sendRef, &data, flags); + res = nsend(env, descP, sendRef, &sndData, flags); MUNLOCK(descP->writeMtx); @@ -2570,9 +2592,10 @@ static ERL_NIF_TERM nsend(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM sendRef, - ErlNifBinary* dataP, + ErlNifBinary* sndDataP, int flags) { + int save_errno; ssize_t written; if (!descP->isWritable) @@ -2583,9 +2606,15 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, */ cnt_inc(&descP->writeTries, 1); - written = sock_send(descP->sock, dataP->data, dataP->size, flags); + written = sock_send(descP->sock, sndDataP->data, sndDataP->size, flags); + if (IS_SOCKET_ERROR(written)) + save_errno = sock_errno(); + else + save_errno = -1; // The value does not actually matter in this case + - return send_check_result(env, descP, written, dataP->size, sendRef); + return send_check_result(env, descP, + written, sndDataP->size, save_errno, sendRef); } @@ -2655,6 +2684,7 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, SocketAddress* toAddrP, unsigned int toAddrLen) { + int save_errno; ssize_t written; if (!descP->isWritable) @@ -2674,8 +2704,12 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, dataP->data, dataP->size, flags, NULL, 0); } + if (IS_SOCKET_ERROR(written)) + save_errno = sock_errno(); + else + save_errno = -1; // The value does not actually matter in this case - return send_check_result(env, descP, written, dataP->size, sendRef); + return send_check_result(env, descP, written, dataP->size, save_errno, sendRef); } @@ -2829,6 +2863,13 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, { ssize_t read; ErlNifBinary buf; + int save_errno; + int bufSz = (len ? len : descP->rBufSz); + + SSDBG( descP, ("SOCKET", "nrecv -> entry with" + "\r\n len: %d (%d)" + "\r\n flags: %d" + "\r\n", len, bufSz, flags) ); if (!descP->isReadable) return enif_make_badarg(env); @@ -2837,7 +2878,7 @@ ERL_NIF_TERM nrecv(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((len ? len : descP->rBufSz), &buf)) + if (!ALLOC_BIN(bufSz, &buf)) return esock_make_error(env, atom_exalloc); /* We ignore the wrap for the moment. @@ -2845,10 +2886,19 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, */ cnt_inc(&descP->readTries, 1); + // If it fails (read = -1), we need errno... + SSDBG( descP, ("SOCKET", "nrecv -> try read (%d)\r\n", buf.size) ); read = sock_recv(descP->sock, buf.data, buf.size, flags); + if (IS_SOCKET_ERROR(read)) + save_errno = sock_errno(); + else + save_errno = -1; // The value does not actually matter in this case + + SSDBG( descP, ("SOCKET", "nrecv -> read: %d (%d)\r\n", read, save_errno) ); return recv_check_result(env, descP, read, len, + save_errno, &buf, recvRef); } @@ -3003,6 +3053,11 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, ERL_NIF_TERM reply, reason; BOOLEAN_T doClose; int selectRes; + int domain = descP->domain; + int type = descP->type; + int protocol = descP->protocol; + + SSDBG( descP, ("SOCKET", "nclose -> [%d] entry\r\n", descP->sock) ); MLOCK(descP->closeMtx); @@ -3049,10 +3104,16 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, descP, NULL, descP->closeRef); if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { /* Prep done - inform the caller it can finalize (close) directly */ + SSDBG( descP, + ("SOCKET", "nclose -> [%d] stop called\r\n", descP->sock) ); + dec_socket(domain, type, protocol); reply = esock_atom_ok; } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { /* The stop callback function has been *scheduled* which means that we * have to wait for it to complete. */ + SSDBG( descP, + ("SOCKET", "nclose -> [%d] stop scheduled\r\n", descP->sock) ); + dec_socket(domain, type, protocol); // SHALL WE DO THIS AT finalize? reply = esock_make_ok2(env, descP->closeRef); } else { /* @@ -3071,6 +3132,11 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, reply = esock_make_error(env, reason); } + SSDBG( descP, + ("SOCKET", "nclose -> [%d] done when: " + "\r\n reply: %T" + "\r\n", descP->sock, reply) ); + return reply; } @@ -3081,7 +3147,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, * * Description: * Perform the actual socket close! - * Note that this function is executed in a dirfty scheduler. + * Note that this function is executed in a dirty scheduler. * * Arguments: * Socket (ref) - Points to the socket descriptor. @@ -3249,6 +3315,10 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, ERL_NIF_TERM eVal; BOOLEAN_T isEncoded, isOTP; + SGDBG( ("SOCKET", "nif_setopt -> entry with argc: %d\r\n", argc) ); + + /* Extract arguments and perform preliminary validation */ + if ((argc != 5) || !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !GET_INT(env, argv[2], &eLevel) || @@ -3263,6 +3333,19 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) return esock_make_error(env, esock_atom_einval); + SSDBG( descP, + ("SOCKET", "nif_setopt -> args when sock = %d:" + "\r\n Socket: %T" + "\r\n Encoded: %T (%d)" + "\r\n Level: %d (%d)" + "\r\n Opt: %d" + "\r\n Value: %T" + "\r\n", + descP->sock, argv[0], + eIsEncoded, isEncoded, + eLevel, level, + eOpt, eVal) ); + return nsetopt(env, descP, isEncoded, isOTP, level, eOpt, eVal); } @@ -3304,6 +3387,12 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "nsetopt_otp -> entry with" + "\r\n eOpt: %d" + "\r\n eVal: %T" + "\r\n", eOpt, eVal) ); + switch (eOpt) { case SOCKET_OPT_OTP_DEBUG: result = nsetopt_otp_debug(env, descP, eVal); @@ -3313,6 +3402,10 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, result = nsetopt_otp_iow(env, descP, eVal); break; + case SOCKET_OPT_OTP_CTRL_PROC: + result = nsetopt_otp_ctrl_proc(env, descP, eVal); + break; + default: result = esock_make_error(env, esock_atom_einval); break; @@ -3349,6 +3442,48 @@ ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, +/* nsetopt_otp_ctrl_proc - Handle the OTP (level) controlling_process options + */ +static +ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ErlNifPid newCtrlPid; + ErlNifMonitor newCtrlMon; + int xres; + + SSDBG( descP, + ("SOCKET", "nsetopt_otp_ctrl_proc -> entry with" + "\r\n eVal: %T" + "\r\n", eVal) ); + + if (!GET_LPID(env, eVal, &newCtrlPid)) { + esock_warning_msg("Failed get pid of new controlling process\r\n"); + return esock_make_error(env, esock_atom_einval); + } + + if ((xres = MONP(env, descP, &newCtrlPid, &newCtrlMon)) != 0) { + esock_warning_msg("Failed monitor %d) (new) controlling process\r\n", xres); + return esock_make_error(env, esock_atom_einval); + } + + if ((xres = DEMONP(env, descP, &descP->ctrlMon)) != 0) { + esock_warning_msg("Failed demonitor (%d) " + "old controlling process %T (%T)\r\n", + xres, descP->ctrlPid, descP->ctrlMon); + } + + descP->ctrlPid = newCtrlPid; + descP->ctrlMon = newCtrlMon; + + SSDBG( descP, ("SOCKET", "nsetopt_otp_ctrl_proc -> done\r\n") ); + + return esock_atom_ok; +} + + + /* The option has *not* been encoded. Instead it has been provided * in "native mode" (option is provided as is and value as a binary). */ @@ -3362,6 +3497,12 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, ErlNifBinary val; ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "nsetopt_native -> entry with" + "\r\n opt: %d" + "\r\n eVal: %T" + "\r\n", opt, eVal) ); + if (GET_BIN(env, eVal, &val)) { int res = socket_setopt(descP->sock, level, opt, val.data, val.size); @@ -3389,6 +3530,11 @@ ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "nsetopt_level -> entry with" + "\r\n level: %d" + "\r\n", level) ); + switch (level) { case SOL_SOCKET: result = nsetopt_lvl_socket(env, descP, eOpt, eVal); @@ -5081,30 +5227,44 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, ssize_t written, ssize_t dataSize, + int saveErrno, ERL_NIF_TERM sendRef) { + SSDBG( descP, + ("SOCKET", "send_check_result -> entry with" + "\r\n written: %d" + "\r\n dataSize: %d" + "\r\n saveErrno: %d" + "\r\n", written, dataSize, saveErrno) ); + if (written == dataSize) { cnt_inc(&descP->writePkgCnt, 1); cnt_inc(&descP->writeByteCnt, written); + SSDBG( descP, + ("SOCKET", "send_check_result -> everything written - done\r\n") ); + return esock_atom_ok; } else if (written < 0) { /* Ouch, check what kind of failure */ - int save_errno = sock_errno(); - if ((save_errno != EAGAIN) && - (save_errno != EINTR)) { + if ((saveErrno != EAGAIN) && (saveErrno != EINTR)) { cnt_inc(&descP->writeFails, 1); - return esock_make_error_errno(env, save_errno); + SSDBG( descP, + ("SOCKET", "send_check_result -> error: %d\r\n", saveErrno) ); + + return esock_make_error_errno(env, saveErrno); } else { /* Ok, try again later */ + SSDBG( descP, ("SOCKET", "send_check_result -> try again\r\n") ); + /* * SHOULD RESULT IN {error, eagain}!!!! * @@ -5124,6 +5284,9 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), descP, NULL, sendRef); + SSDBG( descP, + ("SOCKET", "send_check_result -> not entire package written\r\n") ); + return esock_make_ok2(env, enif_make_int(env, written)); } @@ -5134,11 +5297,42 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SocketDescriptor* descP, int read, int toRead, + int saveErrno, ErlNifBinary* bufP, ERL_NIF_TERM recvRef) { ERL_NIF_TERM data; + SSDBG( descP, + ("SOCKET", "recv_check_result -> entry with" + "\r\n read: %d" + "\r\n toRead: %d" + "\r\n saveErrno: %d" + "\r\n recvRef: %T" + "\r\n", read, toRead, saveErrno, recvRef) ); + + + /* + * + * We need to handle read = 0 for other type(s) (DGRAM) when + * its actually valid to read 0 bytes. + * + * + */ + + if ((read == 0) && (descP->type == SOCK_STREAM)) { + + /* + * When a stream socket peer has performed an orderly shutdown, the return + * value will be 0 (the traditional "end-of-file" return). + * + * *We* do never actually try to read 0 bytes from a stream socket! + */ + + return esock_make_error(env, atom_closed); + + } + /* 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. @@ -5148,6 +5342,10 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, /* +++ We filled the buffer +++ */ + SSDBG( descP, + ("SOCKET", + "recv_check_result -> [%d] filled the buffer\r\n", toRead) ); + if (toRead == 0) { /* +++ Give us everything you have got => needs to continue +++ */ @@ -5168,6 +5366,11 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, data = MKBIN(env, bufP); + SSDBG( descP, + ("SOCKET", + "recv_check_result -> [%d] " + "we are done for now - read more\r\n", toRead) ); + return esock_make_ok3(env, atom_false, data); } else { @@ -5181,6 +5384,11 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, data = MKBIN(env, bufP); + SSDBG( descP, + ("SOCKET", + "recv_check_result -> [%d] " + "we got exactly what we could fit\r\n", toRead) ); + return esock_make_ok3(env, atom_true, data); } @@ -5189,12 +5397,13 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, /* +++ Error handling +++ */ - int save_errno = sock_errno(); - - if (save_errno == ECONNRESET) { + if (saveErrno == ECONNRESET) { /* +++ Oups - closed +++ */ + SSDBG( descP, ("SOCKET", + "recv_check_result -> [%d] closed\r\n", toRead) ); + /* * * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING @@ -5220,17 +5429,31 @@ ERL_NIF_TERM recv_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", + "recv_check_result -> [%d] eagain\r\n", toRead) ); + + 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", "recv_check_result -> [%d] errno: %d\r\n", + toRead, saveErrno) ); + return esock_make_error_errno(env, saveErrno); } } else { /* +++ We did not fill the buffer +++ */ + SSDBG( descP, + ("SOCKET", + "recv_check_result -> [%d] " + "did not fill the buffer (%d of %d)\r\n", + toRead, read, bufP->size) ); + if (toRead == 0) { /* +++ We got a chunk of data but +++ @@ -5239,9 +5462,14 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * +++ into a sub-binary. +++ */ + SSDBG( descP, ("SOCKET", + "recv_check_result -> [%d] split buffer\r\n", toRead) ); + data = MKBIN(env, bufP); data = MKSBIN(env, data, 0, read); + SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] done\r\n", toRead) ); + return esock_make_ok3(env, atom_true, data); } else { @@ -5249,6 +5477,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, /* +++ We got only a part of what was expected +++ * +++ => receive more later. +++ */ + SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] " + "only part of message - expect more\r\n", toRead) ); + return esock_make_ok3(env, atom_false, MKBIN(env, bufP)); } } @@ -6343,6 +6574,90 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) } + +/* decrement counters for when a socket is closed */ +static +void dec_socket(int domain, int type, int protocol) +{ + MLOCK(data.cntMtx); + + cnt_dec(&data.numSockets, 1); + + if (domain == AF_INET) + cnt_dec(&data.numDomainInet, 1); +#if defined(HAVE_IN6) && defined(AF_INET6) + else if (domain == AF_INET6) + cnt_dec(&data.numDomainInet6, 1); +#endif +#if defined(HAVE_SYS_UN_H) + else if (domain == AF_UNIX) + cnt_dec(&data.numDomainInet6, 1); +#endif + + if (type == SOCK_STREAM) + cnt_dec(&data.numTypeStreams, 1); + else if (type == SOCK_DGRAM) + cnt_dec(&data.numTypeDGrams, 1); + else if (type == SOCK_SEQPACKET) + cnt_dec(&data.numTypeSeqPkgs, 1); + + if (protocol == IPPROTO_IP) + cnt_dec(&data.numProtoIP, 1); + else if (protocol == IPPROTO_TCP) + cnt_dec(&data.numProtoTCP, 1); + else if (protocol == IPPROTO_UDP) + cnt_dec(&data.numProtoUDP, 1); +#if defined(HAVE_SCTP) + else if (protocol == IPPROTO_SCTP) + cnt_dec(&data.numProtoSCTP, 1); +#endif + + MUNLOCK(data.cntMtx); +} + + +/* increment counters for when a socket is opened */ +static +void inc_socket(int domain, int type, int protocol) +{ + MLOCK(data.cntMtx); + + cnt_inc(&data.numSockets, 1); + + if (domain == AF_INET) + cnt_inc(&data.numDomainInet, 1); +#if defined(HAVE_IN6) && defined(AF_INET6) + else if (domain == AF_INET6) + cnt_inc(&data.numDomainInet6, 1); +#endif +#if defined(HAVE_SYS_UN_H) + else if (domain == AF_UNIX) + cnt_inc(&data.numDomainInet6, 1); +#endif + + if (type == SOCK_STREAM) + cnt_inc(&data.numTypeStreams, 1); + else if (type == SOCK_DGRAM) + cnt_inc(&data.numTypeDGrams, 1); + else if (type == SOCK_SEQPACKET) + cnt_inc(&data.numTypeSeqPkgs, 1); + + if (protocol == IPPROTO_IP) + cnt_inc(&data.numProtoIP, 1); + else if (protocol == IPPROTO_TCP) + cnt_inc(&data.numProtoTCP, 1); + else if (protocol == IPPROTO_UDP) + cnt_inc(&data.numProtoUDP, 1); +#if defined(HAVE_SCTP) + else if (protocol == IPPROTO_SCTP) + cnt_inc(&data.numProtoSCTP, 1); +#endif + + MUNLOCK(data.cntMtx); +} + + + /* compare_pids - Test if two pids are equal * */ @@ -6446,9 +6761,11 @@ BOOLEAN_T eproto2proto(int eproto, int* proto) *proto = IPPROTO_UDP; break; +#if defined(HAVE_SCTP) case SOCKET_PROTOCOL_SCTP: *proto = IPPROTO_SCTP; break; +#endif default: return FALSE; @@ -6519,35 +6836,42 @@ BOOLEAN_T emap2netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns) * send flags. */ static -BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags) +BOOLEAN_T esendflags2sendflags(unsigned int eflags, int* flags) { unsigned int ef; int tmp = 0; for (ef = SOCKET_SEND_FLAG_LOW; ef <= SOCKET_SEND_FLAG_HIGH; ef++) { + switch (ef) { case SOCKET_SEND_FLAG_CONFIRM: - tmp |= MSG_CONFIRM; + if ((1 << SOCKET_SEND_FLAG_CONFIRM) & eflags) + tmp |= MSG_CONFIRM; break; case SOCKET_SEND_FLAG_DONTROUTE: - tmp |= MSG_DONTROUTE; + if ((1 << SOCKET_SEND_FLAG_DONTROUTE) & eflags) + tmp |= MSG_DONTROUTE; break; case SOCKET_SEND_FLAG_EOR: - tmp |= MSG_EOR; + if ((1 << SOCKET_SEND_FLAG_EOR) & eflags) + tmp |= MSG_EOR; break; case SOCKET_SEND_FLAG_MORE: - tmp |= MSG_MORE; + if ((1 << SOCKET_SEND_FLAG_MORE) & eflags) + tmp |= MSG_MORE; break; case SOCKET_SEND_FLAG_NOSIGNAL: - tmp |= MSG_NOSIGNAL; + if ((1 << SOCKET_SEND_FLAG_NOSIGNAL) & eflags) + tmp |= MSG_NOSIGNAL; break; case SOCKET_SEND_FLAG_OOB: - tmp |= MSG_OOB; + if ((1 << SOCKET_SEND_FLAG_OOB) & eflags) + tmp |= MSG_OOB; break; default: @@ -6556,7 +6880,7 @@ BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags) } - *sendflags = tmp; + *flags = tmp; return TRUE; } @@ -6567,31 +6891,53 @@ BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags) * send flags. */ static -BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags) +BOOLEAN_T erecvflags2recvflags(unsigned int eflags, int* flags) { unsigned int ef; int tmp = 0; + SGDBG( ("SOCKET", "erecvflags2recvflags -> entry with" + "\r\n eflags: %d" + "\r\n", eflags) ); + for (ef = SOCKET_RECV_FLAG_LOW; ef <= SOCKET_RECV_FLAG_HIGH; ef++) { + + SGDBG( ("SOCKET", "erecvflags2recvflags -> iteration" + "\r\n ef: %d" + "\r\n tmp: %d" + "\r\n", ef, tmp) ); + switch (ef) { case SOCKET_RECV_FLAG_CMSG_CLOEXEC: - tmp |= MSG_CMSG_CLOEXEC; + if ((1 << SOCKET_RECV_FLAG_CMSG_CLOEXEC) & eflags) + tmp |= MSG_CMSG_CLOEXEC; break; case SOCKET_RECV_FLAG_ERRQUEUE: - tmp |= MSG_ERRQUEUE; + if ((1 << SOCKET_RECV_FLAG_ERRQUEUE) & eflags) + tmp |= MSG_ERRQUEUE; break; case SOCKET_RECV_FLAG_OOB: - tmp |= MSG_OOB; + if ((1 << SOCKET_RECV_FLAG_OOB) & eflags) + tmp |= MSG_OOB; break; + /* + * + * + * We need to handle this, because it may effect the read algorithm + * + * + */ case SOCKET_RECV_FLAG_PEEK: - tmp |= MSG_PEEK; + if ((1 << SOCKET_RECV_FLAG_PEEK) & eflags) + tmp |= MSG_PEEK; break; case SOCKET_RECV_FLAG_TRUNC: - tmp |= MSG_TRUNC; + if ((1 << SOCKET_RECV_FLAG_TRUNC) & eflags) + tmp |= MSG_TRUNC; break; default: @@ -6600,7 +6946,7 @@ BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags) } - *recvflags = tmp; + *flags = tmp; return TRUE; } @@ -7117,19 +7463,33 @@ char* send_msg(ErlNifEnv* env, static BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc) { - BOOLEAN_T wrap; - uint32_t max = 0xFFFFFFFF; - uint32_t current = *cnt; + BOOLEAN_T wrap; + uint32_t max = 0xFFFFFFFF; + uint32_t current = *cnt; - if ((max - inc) >= current) { - *cnt += inc; - wrap = FALSE; - } else { - *cnt = inc - (max - current) - 1; - wrap = TRUE; - } + if ((max - inc) >= current) { + *cnt += inc; + wrap = FALSE; + } else { + *cnt = inc - (max - current) - 1; + wrap = TRUE; + } + + return (wrap); +} + + +static +void cnt_dec(uint32_t* cnt, uint32_t dec) +{ + uint32_t current = *cnt; + + if (dec > current) + *cnt = 0; // The counter cannot be < 0 so this is the best we can do... + else + *cnt -= dec; - return (wrap); + return; } @@ -7183,116 +7543,125 @@ void socket_dtor(ErlNifEnv* env, void* obj) static void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) { - SocketDescriptor* descP = (SocketDescriptor*) obj; - - MLOCK(descP->writeMtx); - MLOCK(descP->readMtx); - MLOCK(descP->accMtx); - MLOCK(descP->closeMtx); - - - descP->state = SOCKET_STATE_CLOSING; // Just in case...??? - descP->isReadable = FALSE; - descP->isWritable = FALSE; - - - /* We should check that we actually have a monitor. - * This *should* be done with a "NULL" monitor value, - * which there currently is none... - */ - DEMONP(env, descP, &descP->ctrlMon); - - if (descP->currentWriterP != NULL) { - /* We have a (current) writer and *may* therefor also have - * writers waiting. - */ - - ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, - descP->currentWriter.ref, - atom_closed, - &descP->currentWriter.pid)) ); - - /* And also deal with the waiting writers (in the same way) */ - inform_waiting_procs(env, descP, &descP->writersQ, TRUE, atom_closed); - } - - if (descP->currentReaderP != NULL) { - - /* We have a (current) reader and *may* therefor also have - * readers waiting. - */ - - ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, - descP->currentReader.ref, - atom_closed, - &descP->currentReader.pid)) ); - - /* And also deal with the waiting readers (in the same way) */ - inform_waiting_procs(env, descP, &descP->readersQ, TRUE, atom_closed); - } - - if (descP->currentAcceptorP != NULL) { - /* We have a (current) acceptor and *may* therefor also have - * acceptors waiting. - */ - - ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, - descP->currentAcceptor.ref, - atom_closed, - &descP->currentAcceptor.pid)) ); - - /* And also deal with the waiting acceptors (in the same way) */ - inform_waiting_procs(env, descP, &descP->acceptorsQ, TRUE, atom_closed); - } - - - if (descP->sock != INVALID_SOCKET) { - - /* - * - * - * WE NEED TO CHECK IF THIS OPERATION IS TRIGGERED - * LOCALLY (VIA A CALL TO CLOSE) OR REMOTELLY - * (VIA I.E. ECONSRESET). - * - * - */ - - if (descP->closeLocal) { - - /* +++ send close message to the waiting process +++ - * - * {close, CloseRef} - * - * - * - * WHAT HAPPENS IF THE RECEIVER HAS DIED IN THE MEANTIME???? - * - * - */ - - send_msg(env, MKT2(env, atom_close, descP->closeRef), &descP->closerPid); - - DEMONP(env, descP, &descP->closerMon); - - } else { - - /* - * - * - * ABORT? - * - * - */ - } - } - - - MUNLOCK(descP->closeMtx); - MUNLOCK(descP->accMtx); - MUNLOCK(descP->readMtx); - MUNLOCK(descP->writeMtx); + SocketDescriptor* descP = (SocketDescriptor*) obj; + + SSDBG( descP, + ("SOCKET", "socket_stop -> entry when" + "\r\n sock: %d (%d)" + "\r\n is_direct_call: %d" + "\r\n", descP->sock, fd, is_direct_call) ); + + MLOCK(descP->writeMtx); + MLOCK(descP->readMtx); + MLOCK(descP->accMtx); + MLOCK(descP->closeMtx); + + + descP->state = SOCKET_STATE_CLOSING; // Just in case...??? + descP->isReadable = FALSE; + descP->isWritable = FALSE; + + + /* We should check that we actually have a monitor. + * This *should* be done with a "NULL" monitor value, + * which there currently is none... + */ + DEMONP(env, descP, &descP->ctrlMon); + + if (descP->currentWriterP != NULL) { + /* We have a (current) writer and *may* therefor also have + * writers waiting. + */ + + ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, + descP->currentWriter.ref, + atom_closed, + &descP->currentWriter.pid)) ); + + /* And also deal with the waiting writers (in the same way) */ + inform_waiting_procs(env, descP, &descP->writersQ, TRUE, atom_closed); + } + + if (descP->currentReaderP != NULL) { + + /* We have a (current) reader and *may* therefor also have + * readers waiting. + */ + + ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, + descP->currentReader.ref, + atom_closed, + &descP->currentReader.pid)) ); + + /* And also deal with the waiting readers (in the same way) */ + inform_waiting_procs(env, descP, &descP->readersQ, TRUE, atom_closed); + } + + if (descP->currentAcceptorP != NULL) { + /* We have a (current) acceptor and *may* therefor also have + * acceptors waiting. + */ + + ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, + descP->currentAcceptor.ref, + atom_closed, + &descP->currentAcceptor.pid)) ); + + /* And also deal with the waiting acceptors (in the same way) */ + inform_waiting_procs(env, descP, &descP->acceptorsQ, TRUE, atom_closed); + } + + + if (descP->sock != INVALID_SOCKET) { + + /* + * + * + * WE NEED TO CHECK IF THIS OPERATION IS TRIGGERED + * LOCALLY (VIA A CALL TO CLOSE) OR REMOTELLY + * (VIA I.E. ECONSRESET). + * + * + */ + + if (descP->closeLocal) { + + /* +++ send close message to the waiting process +++ + * + * {close, CloseRef} + * + * + * + * WHAT HAPPENS IF THE RECEIVER HAS DIED IN THE MEANTIME???? + * + * + */ + + send_msg(env, MKT2(env, atom_close, descP->closeRef), &descP->closerPid); + + DEMONP(env, descP, &descP->closerMon); + + } else { + + /* + * + * + * ABORT? + * + * + */ + } + } + + + MUNLOCK(descP->closeMtx); + MUNLOCK(descP->accMtx); + MUNLOCK(descP->readMtx); + MUNLOCK(descP->writeMtx); + SSDBG( descP, + ("SOCKET", "socket_stop -> done (%d, %d)\r\n", descP->sock, fd) ); + } diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 05fb40e286..397f69f58d 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -29,6 +29,11 @@ #include "socket_dbg.h" #include "sys.h" +#include +#include +#include +#include +#include /* We don't have a "debug flag" to check here, so we * should use the compile debug flag, whatever that is... @@ -46,6 +51,9 @@ extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */ +static int realtime(struct timespec* tsP); +static int timespec2str(char *buf, unsigned int len, struct timespec *ts); + static char* make_sockaddr_in4(ErlNifEnv* env, ERL_NIF_TERM port, ERL_NIF_TERM addr, @@ -1168,10 +1176,89 @@ void esock_abort(const char* expr, +/* *** esock_warning_msg *** + * + * Temporary function for issuing warning messages. + * + */ +extern +void esock_warning_msg( const char* format, ... ) +{ + va_list args; + char f[512 + sizeof(format)]; // This has to suffice... + char stamp[32]; + struct timespec ts; + int res; + + /* + * We should really include self in the printout, so we can se which process + * are executing the code. But then I must change the API.... + * ....something for later. + */ + + // 2018-06-29 12:13:21.232089 + // 29-Jun-2018::13:47:25.097097 + + if (!realtime(&ts)) { + if (timespec2str(stamp, sizeof(stamp), &ts) != 0) { + res = enif_snprintf(f, sizeof(f), "=WARNING MSG==== %s", format); + } else { + res = enif_snprintf(f, sizeof(f), + "=WARNING MSG==== %s ===\r\n%s" , stamp, format); + } + + if (res > 0) { + va_start (args, format); + enif_vfprintf (stdout, f, args); + va_end (args); + fflush(stdout); + } + } + + return; +} + + +static +int realtime(struct timespec* tsP) +{ + return clock_gettime(CLOCK_REALTIME, tsP); +} + + +/* + * Convert a timespec struct into a readable/printable string. + * + * "%F::%T" => 2018-06-29 12:13:21[.232089] + * "%d-%b-%Y::%T" => 29-Jun-2018::13:47:25.097097 + */ +static +int timespec2str(char *buf, unsigned int len, struct timespec *ts) +{ + int ret, buflen; + struct tm t; + + tzset(); + if (localtime_r(&(ts->tv_sec), &t) == NULL) + return 1; + + ret = strftime(buf, len, "%d-%B-%Y::%T", &t); + if (ret == 0) + return 2; + len -= ret - 1; + buflen = strlen(buf); + + ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); + if (ret >= len) + return 3; + + return 0; +} + /* =================================================================== * * * - * Various utility functions * + * Various (internal) utility functions * * * * =================================================================== */ diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index dedeb8dd7d..add2c8f4be 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -158,5 +158,8 @@ ERL_NIF_TERM esock_make_error_str(ErlNifEnv* env, char* reason); extern ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err); +extern +void esock_warning_msg(const char* format, ... ); + #endif // SOCKET_UTIL_H__ -- cgit v1.2.3 From e39e25d84405e13ca0ce476e3ba473510e5548de Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 4 Jul 2018 15:18:18 +0200 Subject: [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 --- erts/emulator/nifs/common/socket_nif.c | 323 +++++++++++++++++++++++++++++++-- 1 file changed, 307 insertions(+), 16 deletions(-) (limited to 'erts/emulator/nifs') 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. + * + * + * + * 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. + * + * */ 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") ); + /* * 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); -- cgit v1.2.3 From 44cfb3d222ba4d20607af7cc654746f84ece3989 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 19 Jun 2018 12:20:21 +0200 Subject: [socket-nif] Add doc for net module and some cleanup Added doc for the net module. Also some socket-nif cleanup. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 1 + erts/emulator/nifs/common/socket_nif.c | 870 ++------------------------------- 2 files changed, 54 insertions(+), 817 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index a3e54360fe..878ed513c8 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -104,6 +104,7 @@ typedef unsigned int BOOLEAN_T; */ extern ERL_NIF_TERM esock_atom_addr; extern ERL_NIF_TERM esock_atom_any; +extern ERL_NIF_TERM esock_atom_debug; extern ERL_NIF_TERM esock_atom_dgram; extern ERL_NIF_TERM esock_atom_error; extern ERL_NIF_TERM esock_atom_false; diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 4efca1c72d..1534bd0766 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -397,29 +397,29 @@ typedef union { #define sock_accept(s, addr, len) \ make_noninheritable_handle(accept((s), (addr), (len))) -#define sock_bind(s, addr, len) bind((s), (addr), (len)) -#define sock_close(s) closesocket((s)) -#define sock_close_event(e) WSACloseEvent(e) -#define sock_connect(s, addr, len) connect((s), (addr), (len)) -#define sock_create_event(s) WSACreateEvent() -#define sock_errno() WSAGetLastError() -#define sock_getopt(s,l,o,v,ln) getsockopt((s),(l),(o),(v),(ln)) -#define sock_htons(x) htons((x)) -#define sock_htonl(x) htonl((x)) -#define sock_listen(s, b) listen((s), (b)) -#define sock_name(s, addr, len) getsockname((s), (addr), (len)) -#define sock_ntohs(x) ntohs((x)) +#define sock_bind(s, addr, len) bind((s), (addr), (len)) +#define sock_close(s) closesocket((s)) +#define sock_close_event(e) WSACloseEvent(e) +#define sock_connect(s, addr, len) connect((s), (addr), (len)) +#define sock_create_event(s) WSACreateEvent() +#define sock_errno() WSAGetLastError() +#define sock_getopt(s,l,o,v,ln) getsockopt((s),(l),(o),(v),(ln)) +#define sock_htons(x) htons((x)) +#define sock_htonl(x) htonl((x)) +#define sock_listen(s, b) listen((s), (b)) +#define sock_name(s, addr, len) getsockname((s), (addr), (len)) +#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)) -#define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) +#define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) -#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln)) -#define sock_shutdown(s, how) shutdown((s), (how)) +#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln)) +#define sock_shutdown(s, how) shutdown((s), (how)) #define SET_BLOCKING(s) ioctlsocket(s, FIONBIO, &zero_value) @@ -1277,17 +1277,14 @@ static char* send_msg(ErlNifEnv* env, ERL_NIF_TERM msg, ErlNifPid* pid); -static BOOLEAN_T extract_item_on_load(ErlNifEnv* env, - ERL_NIF_TERM map, - ERL_NIF_TERM key, - ERL_NIF_TERM* val); - static BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, - ERL_NIF_TERM map, - BOOLEAN_T def); + ERL_NIF_TERM map); static BOOLEAN_T extract_iow_on_load(ErlNifEnv* env, - ERL_NIF_TERM map, - BOOLEAN_T def); + ERL_NIF_TERM map); +static BOOLEAN_T extract_bool(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM ekey, + BOOLEAN_T def); static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); @@ -1320,7 +1317,6 @@ static const struct in6_addr in6addr_loopback = static char str_close[] = "close"; static char str_closed[] = "closed"; static char str_closing[] = "closing"; -static char str_debug[] = "debug"; static char str_false[] = "false"; static char str_global_counters[] = "global_counters"; static char str_in4_sockaddr[] = "in4_sockaddr"; @@ -1364,6 +1360,7 @@ static char str_exsend[] = "exsend"; // failed send ERL_NIF_TERM esock_atom_addr; ERL_NIF_TERM esock_atom_any; ERL_NIF_TERM esock_atom_dgram; +ERL_NIF_TERM esock_atom_debug; ERL_NIF_TERM esock_atom_error; ERL_NIF_TERM esock_atom_false; ERL_NIF_TERM esock_atom_family; @@ -1398,7 +1395,6 @@ ERL_NIF_TERM esock_atom_einval; static ERL_NIF_TERM atom_close; static ERL_NIF_TERM atom_closed; static ERL_NIF_TERM atom_closing; -static ERL_NIF_TERM atom_debug; static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_global_counters; static ERL_NIF_TERM atom_in4_sockaddr; @@ -1471,6 +1467,8 @@ static SocketData data; * nif_recvfrom(Sock, Flags) * nif_close(Sock) * nif_shutdown(Sock, How) + * nif_sockname(Sock) + * nif_peername(Sock) * * And some functions to manipulate and retrieve socket options: * ------------------------------------------------------------- @@ -1540,7 +1538,7 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, numProtoIP, numProtoTCP, numProtoUDP, numProtoSCTP}; unsigned int lenGCnt = sizeof(gcnt) / sizeof(ERL_NIF_TERM); ERL_NIF_TERM lgcnt = MKLA(env, gcnt, lenGCnt); - ERL_NIF_TERM keys[] = {atom_debug, atom_iow, atom_global_counters}; + ERL_NIF_TERM keys[] = {esock_atom_debug, atom_iow, atom_global_counters}; ERL_NIF_TERM vals[] = {BOOL2ATOM(data.dbg), BOOL2ATOM(data.iow), lgcnt}; ERL_NIF_TERM info; unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); @@ -1647,7 +1645,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, SOCKET sock; HANDLE event; #ifdef HAVE_SETNS - int current_ns; + int current_ns = 0; #endif SGDBG( ("SOCKET", "nopen -> entry with" @@ -1666,6 +1664,8 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, if ((sock = sock_open(domain, type, protocol)) == INVALID_SOCKET) return esock_make_error_errno(env, sock_errno()); + SGDBG( ("SOCKET", "nopen -> open success: %d\r\n", sock) ); + #ifdef HAVE_SETNS if ((netns != NULL) && !restore_network_namespace(current_ns, sock, &save_errno)) @@ -1682,6 +1682,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, return esock_make_error_errno(env, save_errno); } + SGDBG( ("SOCKET", "nopen -> event success: %d\r\n", event) ); SET_NONBLOCKING(sock); @@ -1872,10 +1873,10 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, eSockAddr = argv[1]; SSDBG( descP, - ("SOCKET", "nif_bind -> args:" + ("SOCKET", "nif_bind -> args when sock = %d:" "\r\n Socket: %T" "\r\n SockAddr: %T" - "\r\n", argv[0], eSockAddr) ); + "\r\n", descP->sock, argv[0], eSockAddr) ); /* Make sure we are ready * Not sure how this would even happen, but... @@ -3298,8 +3299,8 @@ ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, * Disable sends and/or receives on a socket. * * Arguments: - * Socket (ref) - Points to the socket descriptor. - * How - What will be shutdown. + * [0] Socket (ref) - Points to the socket descriptor. + * [1] How - What will be shutdown. */ static @@ -5846,721 +5847,6 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, } -/* *** decode_send_addr *** - * - * The rather odd thing about the 'toAddrP' (the **) is - * because we need to be able to return a NULL pointer, - * in the case of the dest address is the atom 'null'. - * Its possible to call the sendto function with the - * args NULL (address) and 0 (port number). - * - * This function whouls really have a char* return value - * type!! - */ -/* -static -char* decode_send_addr(ErlNifEnv* env, - int domain, - ERL_NIF_TERM addr, - int port, - SocketAddress** toAddrP, - unsigned int* toAddrLenP) -{ - if (IS_ATOM(env, addr)) { - unsigned int len; - char a[16]; // Just in case... - - / * The only acceptable value is the atom 'null' * / - - if (!(GET_ATOM_LEN(env, addr, &len) && - (len > 0) && - (len <= (sizeof("null"))))) - return ESOCK_STR_EINVAL; - - if (!GET_ATOM(env, addr, a, sizeof(a))) - return ESOCK_STR_EINVAL; - - *toAddrP = NULL; - if (strncmp(a, "null", len) == 0) - return NULL; - else - return ESOCK_STR_EINVAL; - - } else if (IS_TUPLE(env, addr)) { - / * We now know that the we have a proper address. * / - return decode_send_addr_tuple(env, domain, addr, port, - *toAddrP, toAddrLenP); - } else { - return ESOCK_STR_EINVAL; - } -} -*/ - -/* -static -char* decode_send_addr_tuple(ErlNifEnv* env, - int domain, - ERL_NIF_TERM addr, - int port, - SocketAddress* toAddrP, - unsigned int* toAddrLenP) -{ - / * We handle two different tuples: - * - size 4 (INET) - * - size 8 (INET6) - * / - - const ERL_NIF_TERM* addrt; - int addrtSz; - - if (!GET_TUPLE(env, addr, &addrtSz, &addrt)) - return ESOCK_STR_EINVAL; // PLACEHOLDER - - switch (domain) { - case AF_INET: - if (addrtSz != 4) - return ESOCK_STR_EINVAL; - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - if (addrtSz != 8) - return ESOCK_STR_EINVAL; - break; -#endif - - default: - return str_eafnosupport; - break; - } - - return decode_address_tuple(env, domain, - addrt, port, - 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 -char* decode_in_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - SocketAddress* sockAddrP, - unsigned int* addrLenP) -{ - const ERL_NIF_TERM* addrt; - int addrtSz; - char* result = NULL; - - if (!GET_TUPLE(env, eSockAddr, &addrtSz, &addrt)) - return ESOCK_STR_EINVAL; - - / * - * 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 = ESOCK_STR_EAFNOSUPPORT; - 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 -char* 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 ESOCK_STR_EINVAL; - - / * 2: Get the port number * / - if (!GET_INT(env, eIn4SockAddr[1], &port)) - return ESOCK_STR_EINVAL; - - / * 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 ESOCK_STR_EINVAL; - } -} -*/ - - -/* -static -char* decode_in4_sockaddr_atomaddr(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - int port, - SocketAddress* sockAddrP, - unsigned int* addrLenP) -{ - struct in_addr addr; - - if (COMPARE(esock_atom_loopback, eAddr) == 0) { - addr.s_addr = sock_htonl(INADDR_LOOPBACK); - } else if (COMPARE(esock_atom_any, eAddr) == 0) { - addr.s_addr = sock_htonl(INADDR_ANY); - } else { - return ESOCK_STR_EINVAL; - } - - sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); -#ifndef NO_SA_LEN - sockAddrP->sai.sin_len = sizeof(struct sockaddr_in); -#endif - sockAddrP->in4.sin_family = AF_INET; - sockAddrP->in4.sin_port = sock_htons(port); - sockAddrP->in4.sin_addr.s_addr = addr.s_addr; - *addrLenP = sizeof(struct sockaddr_in); - - return NULL; -} -*/ - - -/* Decode an in4_sockaddr where the address field is a tuple. - * Its *supposed* to be an ip4_address (tuple). - */ -/* -static -char* 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 ESOCK_STR_EINVAL; - - if (ip4AddrTSz != 4) - return ESOCK_STR_EINVAL; - - sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in)); -#ifndef NO_SA_LEN - sockAddrP->in4.sin_len = sizeof(struct sockaddr_in); -#endif - sockAddrP->in4.sin_family = AF_INET; - sockAddrP->in4.sin_port = sock_htons(port); - for (a = 0; a < 4; a++) { - if (!GET_INT(env, ip4AddrT[a], &v)) - return ESOCK_STR_EINVAL; - addr[a] = v; - } - sys_memcpy(&sockAddrP->in4.sin_addr, &addr, sizeof(addr)); - *addrLenP = sizeof(struct sockaddr_in); - - return NULL; -} -*/ - - - -/* 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 -char* 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 ESOCK_STR_EINVAL; - - / * 2: Get the port number * / - if (!GET_INT(env, eIn6SockAddr[1], &port)) - return ESOCK_STR_EINVAL; - - / * 4: Get the flowinfo * / - if (!GET_UINT(env, eIn6SockAddr[3], &flowInfo)) - return ESOCK_STR_EINVAL; - - / * 5: Get the scope_id * / - if (!GET_UINT(env, eIn6SockAddr[4], &scopeId)) - return ESOCK_STR_EINVAL; - - / * 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 ESOCK_STR_EINVAL; - } -} -#endif -*/ - - -/* -#if defined(HAVE_IN6) && defined(AF_INET6) -static -char* 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(esock_atom_loopback, eAddr) == 0) { - addr = &in6addr_loopback; - } else if (COMPARE(esock_atom_any, eAddr) == 0) { - addr = &in6addr_any; - } else { - return ESOCK_STR_EINVAL; - } - - sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); -#ifndef NO_SA_LEN - sockAddrP->in6.sin6_len = sizeof(struct sockaddr_in6); -#endif - sockAddrP->in6.sin6_family = AF_INET6; - sockAddrP->in6.sin6_port = sock_htons(port); - sockAddrP->in6.sin6_flowinfo = flowInfo; - sockAddrP->in6.sin6_scope_id = scopeId; - sockAddrP->in6.sin6_addr = *addr; - *addrLenP = sizeof(struct sockaddr_in6); - - return NULL; -} -#endif -*/ - - - -#if defined(HAVE_IN6) && defined(AF_INET6) -/* Decode an in6_sockaddr where the address field is a tuple */ -/* -static -char* 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 ESOCK_STR_EINVAL; - - if (ip6AddrTSz != 8) - return ESOCK_STR_EINVAL; - - sys_memzero((char*)sockAddrP, sizeof(struct sockaddr_in6)); -#ifndef NO_SA_LEN - sockAddrP->in6.sin6_len = sizeof(struct sockaddr_in6); -#endif - sockAddrP->in6.sin6_family = AF_INET6; - sockAddrP->in6.sin6_port = sock_htons(port); - sockAddrP->in6.sin6_flowinfo = flowInfo; - sockAddrP->in6.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 ESOCK_STR_EINVAL; - addr[a*2 ] = ((v >> 8) & 0xFF); - addr[a*2+1] = (v & 0xFF); - } - sys_memcpy(&sockAddrP->in6.sin6_addr, &addr, sizeof(addr)); - *addrLenP = sizeof(struct sockaddr_in6); - - return NULL; -} -*/ -#endif - - -/* Decode the 4- or 8-element address tuple - * and initiate the socket address structure. - */ -/* -static -char* decode_address_tuple(ErlNifEnv* env, - int domain, - const ERL_NIF_TERM* addrt, - int port, - SocketAddress* addrP, - unsigned int* addrLenP) -{ - - / * We now *know* that the size of the tuple is correct, - * so we don't need to check anything here, just unpack. - * / - - switch (domain) { - case AF_INET: - { - int a, v; - char laddr[4]; - - sys_memzero((char*)addrP, sizeof(struct sockaddr_in)); -#ifndef NO_SA_LEN - addrP->sai.sin_len = sizeof(struct sockaddr_in); -#endif - addrP->sai.sin_family = domain; - addrP->sai.sin_port = sock_htons(port); - for (a = 0; a < 4; a++) { - if (!GET_INT(env, addrt[a], &v)) - return ESOCK_STR_EINVAL; - laddr[a] = v; - } - sys_memcpy(&addrP->sai.sin_addr, &laddr, sizeof(laddr)); - *addrLenP = sizeof(struct sockaddr_in); - return NULL; - } - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - { - int a, v; - char laddr[16]; - - sys_memzero((char*)addrP, sizeof(struct sockaddr_in6)); -#ifndef NO_SA_LEN - addrP->sai6.sin6_len = sizeof(struct sockaddr_in6); -#endif - addrP->sai6.sin6_family = domain; - addrP->sai6.sin6_port = sock_htons(port); - addrP->sai6.sin6_flowinfo = 0; - / * 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, addrt[a], &v)) - return ESOCK_STR_EINVAL; - laddr[a*2 ] = ((v >> 8) & 0xFF); - laddr[a*2+1] = (v & 0xFF); - } - sys_memcpy(&addrP->sai6.sin6_addr, &laddr, sizeof(laddr)); - *addrLenP = sizeof(struct sockaddr_in6); - return NULL; - } - break; -#endif - - } / * switch (domain) * / - - return ESOCK_STR_EAFNOSUPPORT; - -} -*/ - -/* Encode the 4- or 8-element address tuple from the socket address structure. - * - * This function is called when we have received a message. So, if we for some - * reason fail to decode the address or parts of it, it makes more sense to - * return with "undefined" for the values rather then fail completely (and not - * deliver the received message). - * - * Returns two things (assuming the encode works): - * - * Domain: inet | inet6 | local - * Source: {Address, Port} | string() - * - */ -/* -static -void encode_address(ErlNifEnv* env, - SocketAddress* sockAddrP, - unsigned int addrLen, - ERL_NIF_TERM* domainT, - ERL_NIF_TERM* sourceT) -{ - short port; - - switch (sockAddrP->sa.sa_family) { - - / * +++ inet (IPv4) +++ * / - - case AF_INET: - if (addrLen >= sizeof(struct sockaddr_in)) { - ERL_NIF_TERM addrT, portT; - unsigned int i; - ERL_NIF_TERM at4[4]; - char* a4 = (char*) &sockAddrP->in4.sin_addr; - - port = sock_ntohs(sockAddrP->in4.sin_port); - for (i = 0; i < 4; i++) { - at4[i] = MKI(env, a4[i]); - } - - *domainT = MKA(env, "inet"); // Shall we encode these? See decode - addrT = MKT4(env, at4[0], at4[1], at4[2], at4[3]); - portT = MKI(env, port); - *sourceT = MKT2(env, addrT, portT); - } else { - *domainT = esock_atom_undefined; - *sourceT = esock_atom_undefined; - } - break; - - - / * +++ inet6 (IPv6) +++ * / - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - if (addrLen >= sizeof(struct sockaddr_in6)) { - ERL_NIF_TERM addrT, portT; - unsigned int i; - ERL_NIF_TERM at6[8]; - char* a16 = (char*) &sockAddrP->in6.sin6_addr; - - port = sock_ntohs(sockAddrP->in6.sin6_port); - / * The address tuple is of size 8 - * and each element is a two byte integer - * / - for (i = 0; i < 8; i++) { - // at6[i] = MKI(env, get_int16(a16[i*2])); - at6[i] = MKI(env, get_int16(a16 + i*2)); - } - - *domainT = MKA(env, "inet6"); // Shall we encode these? See decode - addrT = MKT8(env, - at6[0], at6[1], at6[2], at6[3], - at6[4], at6[5], at6[6], at6[7]); - portT = MKI(env, port); - *sourceT = MKT2(env, addrT, portT); - } else { - *domainT = esock_atom_undefined; - *sourceT = esock_atom_undefined; - } - break; -#endif - - / * +++ local (Unix Domain Sockets) +++ * / - -#ifdef HAVE_SYS_UN_H - case AF_UNIX: - { - size_t n, m; - - *domainT = MKA(env, "local"); - if (addrLen < offsetof(struct sockaddr_un, sun_path)) { - *sourceT = esock_atom_undefined; - } else { - n = addrLen - offsetof(struct sockaddr_un, sun_path); - if (255 < n) { - *sourceT = esock_atom_undefined; - } else { - m = my_strnlen(sockAddrP->un.sun_path, n); -#ifdef __linux__ - / * Assume that the address is a zero terminated string, - * except when the first byte is \0 i.e the string length is 0, - * then use the reported length instead. - * This fix handles Linux's nonportable - * abstract socket address extension. - * / - if (m == 0) { - m = n; - } -#endif - - *sourceT = MKSL(env, sockAddrP->un.sun_path, m); - } - } - } - break; -#endif - - default: - *domainT = esock_atom_undefined; - *sourceT = esock_atom_undefined; - break; - - } / * switch (addrP->sa.sa_family) * / - -} -*/ - - -/* Decode the address when its an atom. - * Currently we only accept two atoms: 'any' and 'loopback' - */ -/* -static -char* decode_address_atom(ErlNifEnv* env, - int domain, - char* addr, - int addrLen, - int port, - SocketAddress* addrP, - unsigned int* addrLenP) -{ - BOOLEAN_T any; - - if (strncmp(addr, "any", addrLen) == 0) { - any = TRUE; - } if (strncmp(addr, "loopback", addrLen) == 0) { - any = FALSE; - } else { - return ESOCK_STR_EINVAL; - } - - / * If we get this far, we *know* its either 'any' or 'loopback' * / - - switch (domain) { - case AF_INET: - { - struct in_addr addr; - if (any) { - addr.s_addr = sock_htonl(INADDR_ANY); - } else { - addr.s_addr = sock_htonl(INADDR_LOOPBACK); - } - sys_memzero((char*) addrP, sizeof(struct sockaddr_in)); -#ifndef NO_SA_LEN - addrP->sai.sin_len = sizeof(struct sockaddr_in6); -#endif - addrP->sai.sin_family = domain; - addrP->sai.sin_port = sock_htons(port); - addrP->sai.sin_addr.s_addr = addr.s_addr; - *addrLenP = sizeof(struct sockaddr_in); - } - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - { - const struct in6_addr* paddr; - if (any) { - paddr = &in6addr_any; - } else { - paddr = &in6addr_loopback; - } - sys_memzero((char*)addrP, sizeof(struct sockaddr_in6)); -#ifndef NO_SA_LEN - addrP->sai6.sin6_len = sizeof(struct sockaddr_in6); -#endif - addrP->sai6.sin6_family = domain; - addrP->sai6.sin6_port = sock_htons(port); - addrP->sai6.sin6_flowinfo = 0; - addrP->sai6.sin6_addr = *paddr; - *addrLenP = sizeof(struct sockaddr_in6); - } - break; -#endif - - default: - return ESOCK_STR_EINVAL; - break; - } - - return NULL; -} -*/ - -/* -static -BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, BOOLEAN_T* val) -{ - unsigned int len; - char b[16]; // Just in case... - - / * Verify that the value is actually an atom * / - if (!IS_ATOM(env, eVal)) - return FALSE; - - / * Verify that the value is of acceptable length * / - if (!(GET_ATOM_LEN(env, eVal, &len) && - (len > 0) && - (len <= sizeof("false")))) - return FALSE; - - / * And finally try to extract the value * / - if (!GET_ATOM(env, eVal, b, sizeof(b))) - return FALSE; - - if (strncmp(b, "true", len) == 0) - *val = TRUE; - else - *val = FALSE; - - return TRUE; -} -*/ - /* +++ decode the linger value +++ * The (socket) linger option is provided as a two tuple: @@ -8055,88 +7341,40 @@ ErlNifFunc socket_funcs[] = static -BOOLEAN_T extract_item_on_load(ErlNifEnv* env, - ERL_NIF_TERM map, - ERL_NIF_TERM key, - ERL_NIF_TERM* val) +BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, + ERL_NIF_TERM map) { - if (!enif_is_map(env, map)) - return FALSE; - - if (!GET_MAP_VAL(env, map, key, val)) - return FALSE; - - return TRUE; + return extract_bool(env, map, esock_atom_debug, SOCKET_NIF_DEBUG_DEFAULT); } static -BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) +BOOLEAN_T extract_iow_on_load(ErlNifEnv* env, + ERL_NIF_TERM map) { - ERL_NIF_TERM dbgKey = enif_make_atom(env, "debug"); - ERL_NIF_TERM dbgVal; - unsigned int len; - char d[16]; // Just in case... - - /* Extra the value of the debug property */ - if (!extract_item_on_load(env, map, dbgKey, &dbgVal)) - return def; + return extract_bool(env, map, atom_iow, SOCKET_NIF_IOW_DEFAULT); +} - /* Verify that the value is actually an atom */ - if (!enif_is_atom(env, dbgVal)) - return def; +static +BOOLEAN_T extract_bool(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM ekey, + BOOLEAN_T def) +{ + ERL_NIF_TERM eval; - /* Verify that the value is of acceptable length */ - if (!(GET_ATOM_LEN(env, dbgVal, &len) && - (len > 0) && - (len <= sizeof("false")))) + if (!GET_MAP_VAL(env, map, ekey, &eval)) return def; - /* And finally try to extract the value */ - if (!GET_ATOM(env, dbgVal, d, sizeof(d))) + if (!IS_ATOM(env, eval)) return def; - if (strncmp(d, "true", len) == 0) + if (COMPARE(eval, esock_atom_true) == 0) return TRUE; else return FALSE; - } -static -BOOLEAN_T extract_iow_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) -{ - ERL_NIF_TERM iowKey = enif_make_atom(env, "iow"); - ERL_NIF_TERM iowVal; - unsigned int len; - char b[16]; // Just in case... - - /* Extra the value of the debug property */ - if (!extract_item_on_load(env, map, iowKey, &iowVal)) - return def; - - /* Verify that the value is actually an atom */ - if (!enif_is_atom(env, iowVal)) - return def; - - /* Verify that the value is of acceptable length */ - if (!(GET_ATOM_LEN(env, iowVal, &len) && - (len > 0) && - (len <= sizeof("false")))) - return def; - - /* And finally try to extract the value */ - if (!GET_ATOM(env, iowVal, b, sizeof(b))) - return def; - - if (strncmp(b, "true", len) == 0) - return TRUE; - else - return FALSE; - - } - - /* ======================================================================= * load_info - A map of misc info (e.g global debug) @@ -8145,10 +7383,8 @@ BOOLEAN_T extract_iow_on_load(ErlNifEnv* env, ERL_NIF_TERM map, BOOLEAN_T def) static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { - data.dbg = extract_debug_on_load(env, load_info, - SOCKET_NIF_DEBUG_DEFAULT); - data.iow = extract_iow_on_load(env, load_info, - SOCKET_NIF_IOW_DEFAULT); + data.dbg = extract_debug_on_load(env, load_info); + data.iow = extract_iow_on_load(env, load_info); /* +++ Global Counters +++ */ data.cntMtx = MCREATE("socket[gcnt]"); @@ -8168,7 +7404,6 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_close = MKA(env, str_close); atom_closed = MKA(env, str_closed); atom_closing = MKA(env, str_closing); - atom_debug = MKA(env, str_debug); atom_false = MKA(env, str_false); atom_global_counters = MKA(env, str_global_counters); atom_in4_sockaddr = MKA(env, str_in4_sockaddr); @@ -8193,6 +7428,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) /* Global atom(s) */ esock_atom_addr = MKA(env, "addr"); esock_atom_any = MKA(env, "any"); + esock_atom_debug = MKA(env, "debug"); esock_atom_dgram = MKA(env, "dgram"); esock_atom_error = MKA(env, "error"); esock_atom_false = MKA(env, "false"); -- cgit v1.2.3 From 6632cb103336786f3ca12f9d1f1d3338fc76c237 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 20 Jun 2018 12:33:18 +0200 Subject: [net-nif] Changed return type of getaddrinfo The returned address info was supposed to be a map, by was instead (still) a record. OTP-14831 --- erts/emulator/nifs/common/net_nif.c | 72 +++++++++++++++++---------------- erts/emulator/nifs/common/socket_int.h | 2 + erts/emulator/nifs/common/socket_nif.c | 4 ++ erts/emulator/nifs/common/socket_util.c | 43 +++++++++++++++++++- erts/emulator/nifs/common/socket_util.h | 5 +++ 5 files changed, 90 insertions(+), 36 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 572813ac62..6829de509b 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -324,11 +324,12 @@ static ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env, static ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, int proto); -static ERL_NIF_TERM make_address_info(ErlNifEnv* env, - ERL_NIF_TERM fam, - ERL_NIF_TERM sockType, - ERL_NIF_TERM proto, - ERL_NIF_TERM addr); +static char* make_address_info(ErlNifEnv* env, + ERL_NIF_TERM fam, + ERL_NIF_TERM sockType, + ERL_NIF_TERM proto, + ERL_NIF_TERM addr, + ERL_NIF_TERM* ai); static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); @@ -1245,6 +1246,7 @@ BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, BOOLEAN_T result; if (IS_ATOM(env, eString)) { + if (COMPARE(eString, esock_atom_undefined) == 0) { *stringP = NULL; result = TRUE; @@ -1252,28 +1254,11 @@ BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, *stringP = NULL; result = FALSE; } - } else { - unsigned int len; - char* bufP; - if (!GET_LIST_LEN(env, eString, &len) && (len != 0)) { - *stringP = NULL; - result = FALSE; - } + } else { - NDBG( ("NET", "decode_addrinfo_string -> len: %d\r\n", len) ); - - bufP = MALLOC(len + 1); // We shall NULL-terminate + result = esock_decode_string(env, eString, stringP); - if (GET_STR(env, eString, bufP, len+1)) { - NDBG( ("NET", "decode_addrinfo_string -> buf: %s\r\n", bufP) ); - // bufP[len] = '\0'; - *stringP = bufP; - result = TRUE; - } else { - *stringP = NULL; - result = FALSE; - } } return result; @@ -1373,7 +1358,7 @@ static ERL_NIF_TERM encode_address_info(ErlNifEnv* env, struct addrinfo* addrInfoP) { - ERL_NIF_TERM result, fam, type, proto, addr; + ERL_NIF_TERM fam, type, proto, addr, addrInfo; fam = encode_address_info_family(env, addrInfoP->ai_family); type = encode_address_info_type(env, addrInfoP->ai_socktype); @@ -1383,9 +1368,10 @@ ERL_NIF_TERM encode_address_info(ErlNifEnv* env, addrInfoP->ai_addrlen, &addr); - result = make_address_info(env, fam, type, proto, addr); - - return result; + if (make_address_info(env, fam, type, proto, addr, &addrInfo) == NULL) + return addrInfo; + else + return esock_atom_undefined; // We should to better... } @@ -1401,7 +1387,7 @@ ERL_NIF_TERM encode_address_info_family(ErlNifEnv* env, { ERL_NIF_TERM efam; - if (NULL != esock_encode_type(env, family, &efam)) + if (NULL != esock_encode_domain(env, family, &efam)) efam = MKI(env, family); return efam; @@ -1448,13 +1434,29 @@ ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, static -ERL_NIF_TERM make_address_info(ErlNifEnv* env, - ERL_NIF_TERM fam, - ERL_NIF_TERM sockType, - ERL_NIF_TERM proto, - ERL_NIF_TERM addr) +char* make_address_info(ErlNifEnv* env, + ERL_NIF_TERM fam, + ERL_NIF_TERM sockType, + ERL_NIF_TERM proto, + ERL_NIF_TERM addr, + ERL_NIF_TERM* ai) { - return MKT5(env, atom_address_info, fam, sockType, proto, addr); + ERL_NIF_TERM keys[] = {esock_atom_family, + esock_atom_type, + esock_atom_protocol, + esock_atom_addr}; + ERL_NIF_TERM vals[] = {fam, sockType, proto, addr}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, ai)) { + *ai = esock_atom_undefined; + return ESOCK_STR_EINVAL; + } else { + return NULL; + } } diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 878ed513c8..aa10260134 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -119,6 +119,7 @@ extern ERL_NIF_TERM esock_atom_loopback; extern ERL_NIF_TERM esock_atom_ok; extern ERL_NIF_TERM esock_atom_path; extern ERL_NIF_TERM esock_atom_port; +extern ERL_NIF_TERM esock_atom_protocol; extern ERL_NIF_TERM esock_atom_raw; extern ERL_NIF_TERM esock_atom_rdm; extern ERL_NIF_TERM esock_atom_scope_id; @@ -127,6 +128,7 @@ extern ERL_NIF_TERM esock_atom_seqpacket; extern ERL_NIF_TERM esock_atom_stream; extern ERL_NIF_TERM esock_atom_tcp; extern ERL_NIF_TERM esock_atom_true; +extern ERL_NIF_TERM esock_atom_type; extern ERL_NIF_TERM esock_atom_udp; extern ERL_NIF_TERM esock_atom_undefined; diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 1534bd0766..c23f0b021d 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -1373,6 +1373,7 @@ ERL_NIF_TERM esock_atom_local; ERL_NIF_TERM esock_atom_loopback; ERL_NIF_TERM esock_atom_ok; ERL_NIF_TERM esock_atom_path; +ERL_NIF_TERM esock_atom_protocol; ERL_NIF_TERM esock_atom_port; ERL_NIF_TERM esock_atom_raw; ERL_NIF_TERM esock_atom_rdm; @@ -1382,6 +1383,7 @@ ERL_NIF_TERM esock_atom_seqpacket; ERL_NIF_TERM esock_atom_stream; ERL_NIF_TERM esock_atom_tcp; ERL_NIF_TERM esock_atom_true; +ERL_NIF_TERM esock_atom_type; ERL_NIF_TERM esock_atom_udp; ERL_NIF_TERM esock_atom_undefined; ERL_NIF_TERM esock_atom_unknown; @@ -7443,6 +7445,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_ok = MKA(env, "ok"); esock_atom_path = MKA(env, "path"); esock_atom_port = MKA(env, "port"); + esock_atom_protocol = MKA(env, "protocol"); esock_atom_raw = MKA(env, "raw"); esock_atom_rdm = MKA(env, "rdm"); esock_atom_scope_id = MKA(env, "scope_id"); @@ -7451,6 +7454,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_stream = MKA(env, "stream"); esock_atom_tcp = MKA(env, "tcp"); esock_atom_true = MKA(env, "true"); + esock_atom_type = MKA(env, "type"); esock_atom_udp = MKA(env, "udp"); esock_atom_undefined = MKA(env, "undefined"); esock_atom_unknown = MKA(env, "unknown"); diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 397f69f58d..5014688852 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -890,7 +890,7 @@ char* esock_decode_type(ErlNifEnv* env, -/* +++ esock_decode_domain +++ +/* +++ esock_decode_type +++ * * Encode the native type to the Erlang form, that is: * @@ -1038,6 +1038,47 @@ char* esock_decode_protocol(ErlNifEnv* env, +/* *** esock_decode_string *** + * + * Decode a string value. A successful decode results in an + * allocation of the string, which the caller has to free + * once the string has been used. + */ +extern +BOOLEAN_T esock_decode_string(ErlNifEnv* env, + const ERL_NIF_TERM eString, + char** stringP) +{ + BOOLEAN_T result; + unsigned int len; + char* bufP; + + if (!GET_LIST_LEN(env, eString, &len) && (len != 0)) { + *stringP = NULL; + result = FALSE; + } else { + + UDBG( ("SUTIL", "esock_decode_string -> len: %d\r\n", len) ); + + bufP = MALLOC(len + 1); // We shall NULL-terminate + + if (GET_STR(env, eString, bufP, len+1)) { + UDBG( ("SUTIL", "esock_decode_string -> buf: %s\r\n", bufP) ); + // bufP[len] = '\0'; + *stringP = bufP; + result = TRUE; + } else { + *stringP = NULL; + result = FALSE; + FREE(bufP); + } + } + + return result; +} + + + /* *** esock_decode_bool *** * * Decode a boolean value. diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index add2c8f4be..cd8cc7e1fb 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -133,6 +133,11 @@ char* esock_encode_protocol(ErlNifEnv* env, int type, ERL_NIF_TERM* eProtocol); +extern +BOOLEAN_T esock_decode_string(ErlNifEnv* env, + const ERL_NIF_TERM eString, + char** stringP); + extern BOOLEAN_T esock_decode_bool(ERL_NIF_TERM val); extern -- cgit v1.2.3 From d45e3bf3dfeda0e849b07a9a7a19e50a52b04c35 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Jul 2018 14:10:35 +0200 Subject: [socket-nif] Add support for socket (level socket) options domain and protocol Make it possible to *get* the socket options domain and protocol (in addition to type). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 130 +++++++++++++++++++++++++++++++-- 1 file changed, 122 insertions(+), 8 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index c23f0b021d..70a5de912f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -347,14 +347,16 @@ typedef union { #define SOCKET_OPT_OTP_CTRL_PROC 2 #define SOCKET_OPT_SOCK_BROADCAST 4 -#define SOCKET_OPT_SOCK_DONTROUTE 7 -#define SOCKET_OPT_SOCK_KEEPALIVE 9 -#define SOCKET_OPT_SOCK_LINGER 10 -#define SOCKET_OPT_SOCK_PRIORITY 16 -#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_SOCK_DOMAIN 7 +#define SOCKET_OPT_SOCK_DONTROUTE 8 +#define SOCKET_OPT_SOCK_KEEPALIVE 10 +#define SOCKET_OPT_SOCK_LINGER 11 +#define SOCKET_OPT_SOCK_PRIORITY 17 +#define SOCKET_OPT_SOCK_PROTOCOL 18 +#define SOCKET_OPT_SOCK_RCVBUF 19 +#define SOCKET_OPT_SOCK_REUSEADDR 23 +#define SOCKET_OPT_SOCK_SNDBUF 29 +#define SOCKET_OPT_SOCK_TYPE 34 #define SOCKET_OPT_IP_RECVTOS 25 #define SOCKET_OPT_IP_ROUTER_ALERT 28 @@ -931,6 +933,10 @@ static ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_DOMAIN) +static ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_DONTROUTE) static ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, SocketDescriptor* descP); @@ -947,6 +953,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_PROTOCOL) +static ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_RCVBUF) static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, SocketDescriptor* descP); @@ -4715,6 +4725,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_DOMAIN) + case SOCKET_OPT_SOCK_DOMAIN: + result = ngetopt_lvl_sock_domain(env, descP); + break; +#endif + #if defined(SO_DONTROUTE) case SOCKET_OPT_SOCK_DONTROUTE: result = ngetopt_lvl_sock_dontroute(env, descP); @@ -4739,6 +4755,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_PROTOCOL) + case SOCKET_OPT_SOCK_PROTOCOL: + result = ngetopt_lvl_sock_protocol(env, descP); + break; +#endif + #if defined(SO_RCVBUF) case SOCKET_OPT_SOCK_RCVBUF: result = ngetopt_lvl_sock_rcvbuf(env, descP); @@ -4782,6 +4804,51 @@ ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, #endif +#if defined(SO_DOMAIN) +static +ERL_NIF_TERM ngetopt_lvl_sock_domain(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_DOMAIN, + &val, &valSz); + + if (res != 0) { + result = esock_make_error_errno(env, res); + } else { + switch (val) { + case AF_INET: + result = esock_make_ok2(env, esock_atom_inet); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + result = esock_make_ok2(env, esock_atom_inet6); + break; +#endif + +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + result = esock_make_ok2(env, esock_atom_local); + break; +#endif + + default: + result = esock_make_error(env, + MKT2(env, esock_atom_unknown, MKI(env, val))); + break; + } + } + + return result; +} +#endif + + #if defined(SO_DONTROUTE) static ERL_NIF_TERM ngetopt_lvl_sock_dontroute(ErlNifEnv* env, @@ -4842,6 +4909,53 @@ ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, #endif +#if defined(SO_PROTOCOL) +static +ERL_NIF_TERM ngetopt_lvl_sock_protocol(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_PROTOCOL, + &val, &valSz); + + if (res != 0) { + result = esock_make_error_errno(env, res); + } else { + switch (val) { + case IPPROTO_IP: + result = esock_make_ok2(env, esock_atom_ip); + break; + + case IPPROTO_TCP: + result = esock_make_ok2(env, esock_atom_tcp); + break; + + case IPPROTO_UDP: + result = esock_make_ok2(env, esock_atom_udp); + break; + +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + result = esock_make_ok2(env, esock_atom_sctp); + break; +#endif + + default: + result = esock_make_error(env, + MKT2(env, esock_atom_unknown, MKI(env, val))); + break; + } + } + + return result; +} +#endif + + #if defined(SO_RCVBUF) static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, -- cgit v1.2.3 From 7ed2af8dcfd62a051686ac1e1326fbeca0b18334 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Jul 2018 14:54:57 +0200 Subject: [socket-nif] Add support for socket (level socket) option acceptconn The socket (level socket) option acceptconn is now supported (for getopt). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 70a5de912f..73da6455c3 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -346,6 +346,7 @@ typedef union { #define SOCKET_OPT_OTP_IOW 1 #define SOCKET_OPT_OTP_CTRL_PROC 2 +#define SOCKET_OPT_SOCK_ACCEPTCONN 1 #define SOCKET_OPT_SOCK_BROADCAST 4 #define SOCKET_OPT_SOCK_DOMAIN 7 #define SOCKET_OPT_SOCK_DONTROUTE 8 @@ -929,6 +930,10 @@ static ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); +#if defined(SO_ACCEPTCONN) +static ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_BROADCAST) static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, SocketDescriptor* descP); @@ -4719,6 +4724,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(SO_ACCEPTCONN) + case SOCKET_OPT_SOCK_ACCEPTCONN: + result = ngetopt_lvl_sock_acceptconn(env, descP); + break; +#endif + #if defined(SO_BROADCAST) case SOCKET_OPT_SOCK_BROADCAST: result = ngetopt_lvl_sock_broadcast(env, descP); @@ -4794,6 +4805,16 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, } +#if defined(SO_ACCEPTCONN) +static +ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_ACCEPTCONN); +} +#endif + + #if defined(SO_BROADCAST) static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, -- cgit v1.2.3 From 6031321a78e3df5304f555ba356a4482469e7d56 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Jul 2018 15:28:09 +0200 Subject: [socket-nif] Add support for socket (level socket) option debug The socket option (level socket) debug is now supported. To actually set this option, the user must have the proper "authority" (CAP_NET_ADMIN capability or an effective user ID of 0). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 62 ++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 7 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 73da6455c3..c2a8170227 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -348,16 +348,17 @@ typedef union { #define SOCKET_OPT_SOCK_ACCEPTCONN 1 #define SOCKET_OPT_SOCK_BROADCAST 4 +#define SOCKET_OPT_SOCK_DEBUG 6 #define SOCKET_OPT_SOCK_DOMAIN 7 #define SOCKET_OPT_SOCK_DONTROUTE 8 #define SOCKET_OPT_SOCK_KEEPALIVE 10 #define SOCKET_OPT_SOCK_LINGER 11 -#define SOCKET_OPT_SOCK_PRIORITY 17 -#define SOCKET_OPT_SOCK_PROTOCOL 18 -#define SOCKET_OPT_SOCK_RCVBUF 19 -#define SOCKET_OPT_SOCK_REUSEADDR 23 -#define SOCKET_OPT_SOCK_SNDBUF 29 -#define SOCKET_OPT_SOCK_TYPE 34 +#define SOCKET_OPT_SOCK_PRIORITY 18 +#define SOCKET_OPT_SOCK_PROTOCOL 19 +#define SOCKET_OPT_SOCK_RCVBUF 20 +#define SOCKET_OPT_SOCK_REUSEADDR 24 +#define SOCKET_OPT_SOCK_SNDBUF 30 +#define SOCKET_OPT_SOCK_TYPE 35 #define SOCKET_OPT_IP_RECVTOS 25 #define SOCKET_OPT_IP_ROUTER_ALERT 28 @@ -786,6 +787,11 @@ static ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SO_DEBUG) +static ERL_NIF_TERM nsetopt_lvl_sock_debug(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SO_DONTROUTE) static ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env, SocketDescriptor* descP, @@ -938,6 +944,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_DEBUG) +static ERL_NIF_TERM ngetopt_lvl_sock_debug(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_DOMAIN) static ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, SocketDescriptor* descP); @@ -3677,6 +3687,11 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_socket -> entry with" + "\r\n opt: %d" + "\r\n", eOpt) ); + switch (eOpt) { #if defined(SO_BROADCAST) case SOCKET_OPT_SOCK_BROADCAST: @@ -3684,6 +3699,12 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_DEBUG) + case SOCKET_OPT_SOCK_DEBUG: + result = nsetopt_lvl_sock_debug(env, descP, eVal); + break; +#endif + #if defined(SO_DONTROUTE) case SOCKET_OPT_SOCK_DONTROUTE: result = nsetopt_lvl_sock_dontroute(env, descP, eVal); @@ -3746,6 +3767,17 @@ ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, #endif +#if defined(SO_DEBUG) +static +ERL_NIF_TERM nsetopt_lvl_sock_debug(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_DEBUG, eVal); +} +#endif + + #if defined(SO_DONTROUTE) static ERL_NIF_TERM nsetopt_lvl_sock_dontroute(ErlNifEnv* env, @@ -4277,7 +4309,7 @@ ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, opt, &val, sizeof(val)); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; @@ -4736,6 +4768,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_DEBUG) + case SOCKET_OPT_SOCK_DEBUG: + result = ngetopt_lvl_sock_debug(env, descP); + break; +#endif + #if defined(SO_DOMAIN) case SOCKET_OPT_SOCK_DOMAIN: result = ngetopt_lvl_sock_domain(env, descP); @@ -4825,6 +4863,16 @@ ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, #endif +#if defined(SO_DEBUG) +static +ERL_NIF_TERM ngetopt_lvl_sock_debug(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_DEBUG); +} +#endif + + #if defined(SO_DOMAIN) static ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, -- cgit v1.2.3 From 5329726352f8b983e419b6206a85c15cc8836676 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 9 Jul 2018 18:41:07 +0200 Subject: [socket-nif] Add support for socket (level socket) option peek_off The socket option (level socket) peek_off is now supported. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index c2a8170227..4cc953df62 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -353,6 +353,7 @@ typedef union { #define SOCKET_OPT_SOCK_DONTROUTE 8 #define SOCKET_OPT_SOCK_KEEPALIVE 10 #define SOCKET_OPT_SOCK_LINGER 11 +#define SOCKET_OPT_SOCK_PEEK_OFF 16 #define SOCKET_OPT_SOCK_PRIORITY 18 #define SOCKET_OPT_SOCK_PROTOCOL 19 #define SOCKET_OPT_SOCK_RCVBUF 20 @@ -807,6 +808,11 @@ static ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SO_PEEK_OFF) +static ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SO_PRIORITY) static ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, SocketDescriptor* descP, @@ -964,6 +970,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_PEEK_OFF) +static ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_PRIORITY) static ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, SocketDescriptor* descP); @@ -3723,6 +3733,12 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_PEEK_OFF) + case SOCKET_OPT_SOCK_PEEK_OFF: + result = nsetopt_lvl_sock_peek_off(env, descP, eVal); + break; +#endif + #if defined(SO_PRIORITY) case SOCKET_OPT_SOCK_PRIORITY: result = nsetopt_lvl_sock_priority(env, descP, eVal); @@ -3837,6 +3853,17 @@ ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, #endif +#if defined(SO_PEEK_OFF) +static +ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_PEEK_OFF, eVal); +} +#endif + + #if defined(SO_RCVBUF) static ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, @@ -4798,6 +4825,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_PEEK_OFF) + case SOCKET_OPT_SOCK_PEEK_OFF: + result = ngetopt_lvl_sock_peek_off(env, descP); + break; +#endif + #if defined(SO_PRIORITY) case SOCKET_OPT_SOCK_PRIORITY: result = ngetopt_lvl_sock_priority(env, descP); @@ -4968,6 +5001,16 @@ ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, #endif +#if defined(SO_PEEK_OFF) +static +ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_PEEK_OFF); +} +#endif + + #if defined(SO_PRIORITY) static ERL_NIF_TERM ngetopt_lvl_sock_priority(ErlNifEnv* env, -- cgit v1.2.3 From 9110ba256099e6fa55461fc4ca90da5ec4b2966b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 11:28:11 +0200 Subject: [socket-nif] Add support for socket (level socket) option oobinline Added support for socket level socket option oobinline (both get and set). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 67 ++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 12 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 4cc953df62..82468c6e8f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -353,13 +353,14 @@ typedef union { #define SOCKET_OPT_SOCK_DONTROUTE 8 #define SOCKET_OPT_SOCK_KEEPALIVE 10 #define SOCKET_OPT_SOCK_LINGER 11 -#define SOCKET_OPT_SOCK_PEEK_OFF 16 -#define SOCKET_OPT_SOCK_PRIORITY 18 -#define SOCKET_OPT_SOCK_PROTOCOL 19 -#define SOCKET_OPT_SOCK_RCVBUF 20 -#define SOCKET_OPT_SOCK_REUSEADDR 24 -#define SOCKET_OPT_SOCK_SNDBUF 30 -#define SOCKET_OPT_SOCK_TYPE 35 +#define SOCKET_OPT_SOCK_OOBINLINE 13 +#define SOCKET_OPT_SOCK_PEEK_OFF 15 +#define SOCKET_OPT_SOCK_PRIORITY 17 +#define SOCKET_OPT_SOCK_PROTOCOL 18 +#define SOCKET_OPT_SOCK_RCVBUF 19 +#define SOCKET_OPT_SOCK_REUSEADDR 23 +#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 @@ -808,6 +809,11 @@ static ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SO_OOBINLINE) +static ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SO_PEEK_OFF) static ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env, SocketDescriptor* descP, @@ -970,6 +976,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_keepalive(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_OOBINLINE) +static ERL_NIF_TERM ngetopt_lvl_sock_oobinline(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_PEEK_OFF) static ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env, SocketDescriptor* descP); @@ -3739,6 +3749,12 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_OOBINLINE) + case SOCKET_OPT_SOCK_OOBINLINE: + result = nsetopt_lvl_sock_oobinline(env, descP, eVal); + break; +#endif + #if defined(SO_PRIORITY) case SOCKET_OPT_SOCK_PRIORITY: result = nsetopt_lvl_sock_priority(env, descP, eVal); @@ -3842,13 +3858,13 @@ ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, #endif -#if defined(SO_PRIORITY) +#if defined(SO_OOBINLINE) static -ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) +ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) { - return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY, eVal); + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_OOBINLINE, eVal); } #endif @@ -3864,6 +3880,17 @@ ERL_NIF_TERM nsetopt_lvl_sock_peek_off(ErlNifEnv* env, #endif +#if defined(SO_PRIORITY) +static +ERL_NIF_TERM nsetopt_lvl_sock_priority(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY, eVal); +} +#endif + + #if defined(SO_RCVBUF) static ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, @@ -4825,6 +4852,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_OOBINLINE) + case SOCKET_OPT_SOCK_OOBINLINE: + result = ngetopt_lvl_sock_oobinline(env, descP); + break; +#endif + #if defined(SO_PEEK_OFF) case SOCKET_OPT_SOCK_PEEK_OFF: result = ngetopt_lvl_sock_peek_off(env, descP); @@ -5001,6 +5034,16 @@ ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, #endif +#if defined(SO_OOBINLINE) +static +ERL_NIF_TERM ngetopt_lvl_sock_oobinline(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_OOBINLINE); +} +#endif + + #if defined(SO_PEEK_OFF) static ERL_NIF_TERM ngetopt_lvl_sock_peek_off(ErlNifEnv* env, -- cgit v1.2.3 From 8936b71f13b37559600afb5536ff7d7878b9ab0e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 13:42:28 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_loop Added support for the IP option MULTICAST_LOOP. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 89 ++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 15 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 82468c6e8f..95f29f3dd8 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -342,9 +342,9 @@ typedef union { #define SOCKET_OPT_LEVEL_UDP 5 #define SOCKET_OPT_LEVEL_SCTP 6 -#define SOCKET_OPT_OTP_DEBUG 0 -#define SOCKET_OPT_OTP_IOW 1 -#define SOCKET_OPT_OTP_CTRL_PROC 2 +#define SOCKET_OPT_OTP_DEBUG 1 +#define SOCKET_OPT_OTP_IOW 2 +#define SOCKET_OPT_OTP_CTRL_PROC 3 #define SOCKET_OPT_SOCK_ACCEPTCONN 1 #define SOCKET_OPT_SOCK_BROADCAST 4 @@ -362,22 +362,23 @@ typedef union { #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 -#define SOCKET_OPT_IP_TOS 30 -#define SOCKET_OPT_IP_TTL 32 +#define SOCKET_OPT_IP_MULTICAST_LOOP 15 +#define SOCKET_OPT_IP_RECVTOS 25 +#define SOCKET_OPT_IP_ROUTER_ALERT 28 +#define SOCKET_OPT_IP_TOS 30 +#define SOCKET_OPT_IP_TTL 32 #define SOCKET_OPT_IPV6_HOPLIMIT 12 -#define SOCKET_OPT_TCP_CONGESTION 0 -#define SOCKET_OPT_TCP_CORK 1 -#define SOCKET_OPT_TCP_MAXSEG 2 -#define SOCKET_OPT_TCP_NODELAY 3 +#define SOCKET_OPT_TCP_CONGESTION 1 +#define SOCKET_OPT_TCP_CORK 2 +#define SOCKET_OPT_TCP_MAXSEG 3 +#define SOCKET_OPT_TCP_NODELAY 4 -#define SOCKET_OPT_UDP_CORK 0 +#define SOCKET_OPT_UDP_CORK 1 -#define SOCKET_OPT_SCTP_AUTOCLOSE 7 -#define SOCKET_OPT_SCTP_NODELAY 22 +#define SOCKET_OPT_SCTP_AUTOCLOSE 8 +#define SOCKET_OPT_SCTP_NODELAY 23 /* =================================================================== * @@ -843,6 +844,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, ERL_NIF_TERM eVal); +#if defined(IP_MULTICAST_LOOP) +static ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP, @@ -1011,6 +1017,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); +#if defined(IP_MULTICAST_LOOP) +static ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP); @@ -3936,6 +3946,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_MULTICAST_LOOP) + case SOCKET_OPT_IP_MULTICAST_LOOP: + result = nsetopt_lvl_ip_multicast_loop(env, descP, eVal); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = nsetopt_lvl_ip_recvtos(env, descP, eVal); @@ -3969,6 +3985,25 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, } +/* nsetopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option + */ +#if defined(IP_MULTICAST_LOOP) +static +ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_MULTICAST_LOOP, eVal); +} +#endif + + /* nsetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) @@ -5022,7 +5057,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, if (res != 0) { result = esock_make_error_errno(env, res); } else { - ERL_NIF_TERM lOnOff = MKI(env, val.l_onoff); + ERL_NIF_TERM lOnOff = ((val.l_onoff) ? atom_true : atom_false); ERL_NIF_TERM lSecs = MKI(env, val.l_linger); ERL_NIF_TERM linger = MKT2(env, lOnOff, lSecs); @@ -5196,6 +5231,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_MULTICAST_LOOP) + case SOCKET_OPT_IP_MULTICAST_LOOP: + result = ngetopt_lvl_ip_multicast_loop(env, descP); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = ngetopt_lvl_ip_recvtos(env, descP); @@ -5229,6 +5270,24 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, } +/* ngetopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option + */ +#if defined(IP_RECVTOS) +static +ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_MULTICAST_LOOP); +} +#endif + + /* ngetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) -- cgit v1.2.3 From 59586c412340d24188f78730c5b2c45db772c8ca Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 14:01:00 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_ttl Added support for the IP option MULTICAST_TTL. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 61 +++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 95f29f3dd8..f73364f28f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -363,6 +363,7 @@ typedef union { #define SOCKET_OPT_SOCK_TYPE 32 #define SOCKET_OPT_IP_MULTICAST_LOOP 15 +#define SOCKET_OPT_IP_MULTICAST_TTL 16 #define SOCKET_OPT_IP_RECVTOS 25 #define SOCKET_OPT_IP_ROUTER_ALERT 28 #define SOCKET_OPT_IP_TOS 30 @@ -849,6 +850,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_MULTICAST_TTL) +static ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP, @@ -1021,6 +1027,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_MULTICAST_TTL) +static ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP); @@ -3952,6 +3962,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_MULTICAST_TTL) + case SOCKET_OPT_IP_MULTICAST_TTL: + result = nsetopt_lvl_ip_multicast_ttl(env, descP, eVal); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = nsetopt_lvl_ip_recvtos(env, descP, eVal); @@ -4004,6 +4020,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_multicast_ttl - Level IP MULTICAST_TTL option + */ +#if defined(IP_MULTICAST_TTL) +static +ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_int_opt(env, descP, level, IP_MULTICAST_TTL, eVal); +} +#endif + + /* nsetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) @@ -5237,6 +5272,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_MULTICAST_TTL) + case SOCKET_OPT_IP_MULTICAST_TTL: + result = ngetopt_lvl_ip_multicast_ttl(env, descP); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = ngetopt_lvl_ip_recvtos(env, descP); @@ -5272,7 +5313,7 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, /* ngetopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option */ -#if defined(IP_RECVTOS) +#if defined(IP_MULTICAST_LOOP) static ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP) @@ -5288,6 +5329,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_multicast_ttl - Level IP MULTICAST_TTL option + */ +#if defined(IP_MULTICAST_TTL) +static +ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_int_opt(env, descP, level, IP_MULTICAST_TTL); +} +#endif + + /* ngetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) -- cgit v1.2.3 From 8118227d80fc41efac23d30a5601fdfadb75931f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 17:41:40 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_if Added support for the IP option MULTICAST_IF. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 518 +++++++------------------------- erts/emulator/nifs/common/socket_util.c | 83 +++-- erts/emulator/nifs/common/socket_util.h | 18 +- 3 files changed, 172 insertions(+), 447 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f73364f28f..05ba9e55f1 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -362,6 +362,7 @@ typedef union { #define SOCKET_OPT_SOCK_SNDBUF 27 #define SOCKET_OPT_SOCK_TYPE 32 +#define SOCKET_OPT_IP_MULTICAST_IF 14 #define SOCKET_OPT_IP_MULTICAST_LOOP 15 #define SOCKET_OPT_IP_MULTICAST_TTL 16 #define SOCKET_OPT_IP_RECVTOS 25 @@ -845,6 +846,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, ERL_NIF_TERM eVal); +#if defined(IP_MULTICAST_IF) +static ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_MULTICAST_LOOP) static ERL_NIF_TERM nsetopt_lvl_ip_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP, @@ -1023,6 +1029,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); +#if defined(IP_MULTICAST_IF) +static ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_MULTICAST_LOOP) static ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP); @@ -1974,6 +1984,8 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, return esock_make_error_errno(env, sock_errno()); } + SSDBG( descP, ("SOCKET", "nbind -> bound - get port\r\n") ); + port = which_address_port(sockAddrP); SSDBG( descP, ("SOCKET", "nbind -> port: %d\r\n", port) ); if (port == 0) { @@ -3646,7 +3658,7 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, opt, val.data, val.size); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; } else { @@ -3866,7 +3878,7 @@ ERL_NIF_TERM nsetopt_lvl_sock_linger(ErlNifEnv* env, int res = socket_setopt(descP->sock, SOL_SOCKET, SO_LINGER, (void*) &val, optLen); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; } else { @@ -3956,6 +3968,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_MULTICAST_IF) + case SOCKET_OPT_IP_MULTICAST_IF: + result = nsetopt_lvl_ip_multicast_if(env, descP, eVal); + break; +#endif + #if defined(IP_MULTICAST_LOOP) case SOCKET_OPT_IP_MULTICAST_LOOP: result = nsetopt_lvl_ip_multicast_loop(env, descP, eVal); @@ -4001,6 +4019,45 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, } +/* nsetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option + * + * The value is either the atom 'any' or a 4-tuple. + */ +#if defined(IP_MULTICAST_IF) +static +ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + struct in_addr ifAddr; + char* xres; + int res; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + if ((xres = esock_decode_ip4_address(env, eVal, &ifAddr)) != NULL) { + result = esock_make_error_str(env, xres); + } else { + + res = socket_setopt(descP->sock, level, IP_MULTICAST_LOOP, + &ifAddr, sizeof(ifAddr)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + } + + return result; +} +#endif + + /* nsetopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option */ #if defined(IP_MULTICAST_LOOP) @@ -4097,7 +4154,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, IP_TOS, &val, sizeof(val)); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; @@ -4376,7 +4433,7 @@ ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, int res = socket_setopt(descP->sock, level, opt, &val, optLen); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; @@ -4409,7 +4466,7 @@ ERL_NIF_TERM nsetopt_bool_opt(ErlNifEnv* env, res = socket_setopt(descP->sock, level, opt, &ival, sizeof(ival)); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; @@ -4789,7 +4846,7 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, if (valueSz == 0) { res = sock_getopt(descP->sock, level, opt, NULL, NULL); if (res != 0) - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); else result = esock_atom_ok; } else { @@ -4798,7 +4855,7 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, if (ALLOC_BIN(valueSz, &val)) { res = sock_getopt(descP->sock, level, opt, val.data, &valueSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { if (valueSz < val.size) { if (REALLOC_BIN(&val, valueSz)) { @@ -5023,7 +5080,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { switch (val) { case AF_INET: @@ -5090,7 +5147,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_linger(ErlNifEnv* env, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { ERL_NIF_TERM lOnOff = ((val.l_onoff) ? atom_true : atom_false); ERL_NIF_TERM lSecs = MKI(env, val.l_linger); @@ -5148,7 +5205,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { switch (val) { case IPPROTO_IP: @@ -5224,7 +5281,7 @@ ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, res = sock_getopt(descP->sock, SOL_SOCKET, SO_TYPE, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { switch (val) { case SOCK_STREAM: @@ -5266,6 +5323,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_MULTICAST_IF) + case SOCKET_OPT_IP_MULTICAST_IF: + result = ngetopt_lvl_ip_multicast_if(env, descP); + break; +#endif + #if defined(IP_MULTICAST_LOOP) case SOCKET_OPT_IP_MULTICAST_LOOP: result = ngetopt_lvl_ip_multicast_loop(env, descP); @@ -5311,6 +5374,43 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, } +/* ngetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option + */ +#if defined(IP_MULTICAST_IF) +static +ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + ERL_NIF_TERM eAddr; + struct in_addr ifAddr; + SOCKOPTLEN_T ifAddrSz = sizeof(ifAddr); + char* xres; + int res; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + res = sock_getopt(descP->sock, level, IP_MULTICAST_IF, &ifAddr, &ifAddrSz); + + if (res != 0) { + result = esock_make_error_errno(env, sock_errno()); + } else { + if ((xres = esock_encode_ip4_address(env, &ifAddr, &eAddr)) != NULL) { + result = esock_make_error_str(env, xres); + } else { + result = esock_make_ok2(env, eAddr); + } + } + + return result; + +} +#endif + + /* ngetopt_lvl_ip_multicast_loop - Level IP MULTICAST_LOOP option */ #if defined(IP_MULTICAST_LOOP) @@ -5403,7 +5503,7 @@ ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, res = sock_getopt(descP->sock, level, IP_TOS, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { result = encode_ip_tos(env, val); } @@ -5663,7 +5763,7 @@ ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { ERL_NIF_TERM sval = MKSL(env, val, valSz); @@ -5692,7 +5792,7 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { ERL_NIF_TERM bval = ((val) ? atom_true : atom_false); @@ -5719,7 +5819,7 @@ ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { - result = esock_make_error_errno(env, res); + result = esock_make_error_errno(env, sock_errno()); } else { result = esock_make_ok2(env, MKI(env, val)); } @@ -6936,394 +7036,6 @@ BOOLEAN_T ehow2how(unsigned int ehow, int* how) -/* +++ decode_sockaddr +++ - * - * Decode a socket address - sockaddr. In erlang its represented by - * a map, which has a specific set of attributes, depending on one - * mandatory attribute; family. So depending on the value of the family - * attribute: - * - * local - sockaddr_un: path - * inet - sockaddr_in4: port, addr - * inet6 - sockaddr_in6: port, addr, flowinfo, scope_id - */ -/* -static -char* decode_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - SocketAddress* sockAddrP, - unsigned int* addrLen) -{ - ERL_NIF_TERM efam; - int fam; - char* res; - - if (!IS_MAP(env, eSockAddr)) - return ESOCK_STR_EINVAL; - - if (!GET_MAP_VAL(env, eSockAddr, esock_atom_family, &efam)) - return ESOCK_STR_EINVAL; - - if (!decode_domain(env, efam, &fam)) - return ESOCK_STR_EINVAL; - - switch (fam) { - case AF_INET: - res = decode_sockaddr_in4(env, eSockAddr, &sockAddrP->in6, addrLen); - break; - -#if defined(HAVE_IN6) && defined(AF_INET6) - case AF_INET6: - res = decode_sockaddr_in6(env, eSockAddr, &sockAddrP->in6, addrLen); - break; -#endif - -#ifdef HAVE_SYS_UN_H - case AF_UNIX: - res = decode_sockaddr_un(env, eSockAddr, &sockAddrP->un, addrLen); - break; -#endif - - default: - result = ESOCK_STR_EAFNOSUPPORT; - break; - - } - - return res; -} -*/ - - - -/* +++ decode_sockaddr_in4 +++ - * - * Decode a IPv4 socket address - sockaddr_in4. In erlang its represented by - * a map, which has a specific set of attributes: - * - * port :: port_numbber() - * addr :: ip4_address() - * - * The erlang module ensures that both of these has values exist, so there - * is no need for any elaborate error handling. - */ -/* -static -char* decode_sockaddr_in4(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - struct sockaddr_in* sockAddrP, - unsigned int* addrLen) -{ - ERL_NIF_TERM eport, eaddr; - short port; - - / * Basic init * / - sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); - -#ifndef NO_SA_LEN - sockAddrP->sin_len = sizeof(struct sockaddr_in); -#endif - - sockAddrP->sin_family = AF_INET; - - / * Extract (e) port number from map * / - if (!GET_MAP_VAL(env, eSockAddr, atom_port, &eport)) - return ESOCK_STR_EINVAL; - - / * Decode port number * / - if (!GET_INT(env, eport, &port)) - return ESOCK_STR_EINVAL; - sockAddrP->sin_port = sock_htons(port); - - / * Extract (e) address from map * / - if (!GET_MAP_VAL(env, eSockAddr, atom_addr, &eaddr)) - return ESOCK_STR_EINVAL; - - / * Decode address * / - if (!decode_ip4_address(env, eaddr, sockAddrP, addrLen)) - return ESOCK_STR_EINVAL; - - return NULL; -} -*/ - - - -/* +++ decode_sockaddr_in6 +++ - * - * Decode a IPv6 socket address - sockaddr_in6. In erlang its represented by - * a map, which has a specific set of attributes: - * - * port :: port_numbber() (integer) - * addr :: ip6_address() (tuple) - * flowinfo :: in6_flow_info() (integer) - * scope_id :: in6_scope_id() (integer) - * - * The erlang module ensures that all of these has values exist, so there - * is no need for any elaborate error handling. - */ -/* -#if defined(HAVE_IN6) && defined(AF_INET6) -static -char* decode_sockaddr_in6(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - struct sockaddr_in6* sockAddrP, - unsigned int* addrLen) -{ - ERL_NIF_TERM eport, eaddr; - short port; - - / * Basic init * / - sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); -#ifndef NO_SA_LEN - sockAddrP->sin6_len = sizeof(struct sockaddr_in); -#endif - - sockAddrP->sin6_family = AF_INET6; - - / * *** Extract (e) port number from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_port, &eport)) - return ESOCK_STR_EINVAL; - - / * Decode port number * / - if (!GET_INT(env, eport, &port)) - return ESOCK_STR_EINVAL; - - sockAddrP->sin6_port = sock_htons(port); - - / * *** Extract (e) flowinfo from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_flowinfo, &eflowInfo)) - return ESOCK_STR_EINVAL; - - / * 4: Get the flowinfo * / - if (!GET_UINT(env, eflowInfo, &flowInfo)) - return ESOCK_STR_EINVAL; - - sockAddrP->sin6_flowinfo = flowInfo; - - / * *** Extract (e) scope_id from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_scope_id, &escopeId)) - return ESOCK_STR_EINVAL; - - / * *** Get the scope_id *** * / - if (!GET_UINT(env, escopeId, &scopeId)) - return ESOCK_STR_EINVAL; - - sockAddrP->sin6_scope_id = scopeId; - - / * *** Extract (e) address from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_addr, &eaddr)) - return ESOCK_STR_EINVAL; - - / * Decode address * / - if (!decode_ip6_address(env, eaddr, sockAddrP, addrLen)) - return ESOCK_STR_EINVAL; - - return NULL; -} -#endif -*/ - - - - -/* +++ decode_sockaddr_un +++ - * - * Decode a Unix Domain socket address - sockaddr_un. In erlang its represented by - * a map, which has a specific set of attributes: - * - * path :: binary() - * - * The erlang module ensures that this value exist, so there - * is no need for any elaborate error handling. - */ -/* -#ifdef HAVE_SYS_UN_H -static -char* decode_sockaddr_un(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - struct sockaddr_un* sockAddrP, - unsigned int* addrLen) -{ - ErlNifBinary bin; - ERL_NIF_TERM epath; - unsigned int len; - - / * *** Extract (e) path (a binary) from map *** * / - if (!GET_MAP_VAL(env, eSockAddr, atom_port, &epath)) - return ESOCK_STR_EINVAL; - - / * Get the path * / - if (!GET_BIN(env, epath, &bin)) - return ESOCK_STR_EINVAL; - - if ((bin.size + -#ifdef __linux__ - / * Make sure the address gets zero terminated - * except when the first byte is \0 because then it is - * sort of zero terminated although the zero termination - * comes before the address... - * This fix handles Linux's nonportable - * abstract socket address extension. - * / - (bin.data[0] == '\0' ? 0 : 1) -#else - 1 -#endif - ) > sizeof(sockaAddrP->sun_path)) - return ESOCK_STR_EINVAL; - - - sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_un)); - sockAddrP->sun_family = AF_UNIX; - - sys_memcpy(sockAddrP->sun_path, bin.data, bin.size); - len = offsetof(struct sockaddr_un, sun_path) + bin.size; - -#ifndef NO_SA_LEN - sockAddrP->sun_len = len; -#endif - *addrLen = len: - - return NULL; -} -#endif -*/ - - - -/* +++ decode_ip4_address +++ - * - * Decode a IPv4 address. This can be three things: - * - * + Then atom 'any' - * + Then atom 'loopback' - * + An ip4_address() (4 tuple) - * - * Note that this *only* decodes the "address" part of a - * (IPv4) socket address. There are several other things (port). - */ -/* -static -char* decode_ip4_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in* sockAddrP, - unsigned int* addrLen) -{ - if (IS_ATOM(env, eAddr)) { - / * This is either 'any' or 'loopback' * / - struct in_addr addr; - - if (COMPARE(esock_atom_loopback, eAddr) == 0) { - addr.s_addr = sock_htonl(INADDR_LOOPBACK); - } else if (COMPARE(esock_atom_any, eAddr) == 0) { - addr.s_addr = sock_htonl(INADDR_ANY); - } else { - return ESOCK_STR_EINVAL; - } - - sockAddrP->sin_addr.s_addr = addr.s_addr; - *addrLen = sizeof(struct sockaddr_in); - - } else { - / * This is a 4-tuple * / - - const ERL_NIF_TERM* addrt; - int addrtSz; - int a, v; - char addr[4]; - - if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) - return ESOCK_STR_EINVAL; - - if (addrtSz != 4) - return ESOCK_STR_EINVAL; - - for (a = 0; a < 4; a++) { - if (!GET_INT(env, addrt[a], &v)) - return ESOCK_STR_EINVAL; - addr[a] = v; - } - - sys_memcpy(&sockAddrP->sin_addr, &addr, sizeof(addr)); - *addrLenP = sizeof(struct sockaddr_in); - - } - - return NULL; -} -*/ - - - -/* +++ decode_ip6_address +++ - * - * Decode a IPv6 address. This can be three things: - * - * + Then atom 'any' - * + Then atom 'loopback' - * + An ip6_address() (8 tuple) - * - * Note that this *only* decodes the "address" part of a - * (IPv6) socket address. There are several other things - * (port, flowinfo and scope_id) that are handled elsewhere). - */ -/* -#if defined(HAVE_IN6) && defined(AF_INET6) -static -char* decode_ip6_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in6* sockAddrP, - unsigned int* addrLen) -{ - if (IS_ATOM(env, eAddr)) { - / * This is either 'any' or 'loopback' * / - const struct in6_addr* addr; - - if (COMPARE(esock_atom_loopback, eAddr) == 0) { - addr = &in6addr_loopback; - } else if (COMPARE(esock_atom_any, eAddr) == 0) { - addr = &in6addr_any; - } else { - return ESOCK_STR_EINVAL; - } - - sockAddrP->sin6_addr = *addr; - *addrLen = sizeof(struct sockaddr_in6); - - } else { - / * This is a 8-tuple * / - - const ERL_NIF_TERM* addrt; - int addrtSz; - int a, v; - char addr[16]; - - if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) - return ESOCK_STR_EINVAL; - - if (addrtSz != 8) - return ESOCK_STR_EINVAL; - - for (a = 0; a < 8; a++) { - if (!GET_INT(env, addrt[a], &v)) - return ESOCK_STR_EINVAL; - addr[a*2 ] = ((v >> 8) & 0xFF); - addr[a*2+1] = (v & 0xFF); - } - - sys_memcpy(&sockAddrP->sin6_addr, &addr, sizeof(addr)); - *addrLen = sizeof(struct sockaddr_in6); - - } - - return NULL; -} -#endif -*/ - - - #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) /* strnlen doesn't exist everywhere */ /* diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 5014688852..512c1d38e0 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -237,9 +237,13 @@ char* esock_decode_sockaddr_in4(ErlNifEnv* env, /* Decode address */ UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try decode (ip) address\r\n") ); - if ((xres = esock_decode_ip4_address(env, eaddr, sockAddrP, addrLen)) != NULL) + if ((xres = esock_decode_ip4_address(env, + eaddr, + &sockAddrP->sin_addr)) != NULL) return xres; + *addrLen = sizeof(struct sockaddr_in); + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> done\r\n") ); return NULL; @@ -330,6 +334,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, unsigned int flowInfo, scopeId; char* xres; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> entry\r\n") ); + /* Basic init */ sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN @@ -346,6 +352,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, if (!GET_INT(env, eport, &port)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> port: %d\r\n", port) ); + sockAddrP->sin6_port = htons(port); /* *** Extract (e) flowinfo from map *** */ @@ -356,6 +364,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, if (!GET_UINT(env, eflowInfo, &flowInfo)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> flowinfo: %d\r\n", flowInfo) ); + sockAddrP->sin6_flowinfo = flowInfo; /* *** Extract (e) scope_id from map *** */ @@ -366,6 +376,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, if (!GET_UINT(env, escopeId, &scopeId)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> scopeId: %d\r\n", scopeId) ); + sockAddrP->sin6_scope_id = scopeId; /* *** Extract (e) address from map *** */ @@ -373,9 +385,15 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, return ESOCK_STR_EINVAL; /* Decode address */ - if ((xres = esock_decode_ip6_address(env, eaddr, sockAddrP, addrLen)) != NULL) + if ((xres = esock_decode_ip6_address(env, + eaddr, + &sockAddrP->sin6_addr)) != NULL) return xres; + *addrLen = sizeof(struct sockaddr_in6); + + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> done\r\n") ); + return NULL; } #endif @@ -570,22 +588,22 @@ char* esock_encode_sockaddr_un(ErlNifEnv* env, * + An ip4_address() (4 tuple) * * Note that this *only* decodes the "address" part of a - * (IPv4) socket address. There are several other things (port). + * (IPv4) socket address. */ extern -char* esock_decode_ip4_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in* sockAddrP, - unsigned int* addrLen) +char* esock_decode_ip4_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in_addr* inAddrP) { + struct in_addr addr; + UDBG( ("SUTIL", "esock_decode_ip4_address -> entry with" "\r\n eAddr: %T" "\r\n", eAddr) ); if (IS_ATOM(env, eAddr)) { /* This is either 'any' or 'loopback' */ - struct in_addr addr; if (COMPARE(esock_atom_loopback, eAddr) == 0) { UDBG( ("SUTIL", "esock_decode_ip4_address -> address: lookback\r\n") ); @@ -598,8 +616,7 @@ char* esock_decode_ip4_address(ErlNifEnv* env, return ESOCK_STR_EINVAL; } - sockAddrP->sin_addr.s_addr = addr.s_addr; - *addrLen = sizeof(struct sockaddr_in); + inAddrP->s_addr = addr.s_addr; } else { /* This is a 4-tuple */ @@ -621,8 +638,7 @@ char* esock_decode_ip4_address(ErlNifEnv* env, addr[a] = v; } - sys_memcpy(&sockAddrP->sin_addr, &addr, sizeof(addr)); - *addrLen = sizeof(struct sockaddr_in); + sys_memcpy(inAddrP, &addr, sizeof(addr)); } @@ -682,11 +698,14 @@ char* esock_encode_ip4_address(ErlNifEnv* env, #if defined(HAVE_IN6) && defined(AF_INET6) extern -char* esock_decode_ip6_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in6* sockAddrP, - unsigned int* addrLen) +char* esock_decode_ip6_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in6_addr* inAddrP) { + UDBG( ("SUTIL", "esock_decode_ip6_address -> entry with" + "\r\n eAddr: %T" + "\r\n", eAddr) ); + if (IS_ATOM(env, eAddr)) { /* This is either 'any' or 'loopback' */ const struct in6_addr* addr; @@ -698,34 +717,34 @@ char* esock_decode_ip6_address(ErlNifEnv* env, } else { return ESOCK_STR_EINVAL; } - - sockAddrP->sin6_addr = *addr; - *addrLen = sizeof(struct sockaddr_in6); - + + *inAddrP = *addr; + } else { /* This is a 8-tuple */ - + const ERL_NIF_TERM* addrt; int addrtSz; - int a, v; - char addr[16]; + int ai, v; + unsigned char addr[16]; + unsigned char* a = addr; + unsigned int addrLen = sizeof(addr) / sizeof(unsigned char); if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) return ESOCK_STR_EINVAL; if (addrtSz != 8) return ESOCK_STR_EINVAL; - - for (a = 0; a < 8; a++) { - if (!GET_INT(env, addrt[a], &v)) + + for (ai = 0; ai < 8; ai++) { + if (!GET_INT(env, addrt[ai], &v)) return ESOCK_STR_EINVAL; - addr[a*2 ] = ((v >> 8) & 0xFF); - addr[a*2+1] = (v & 0xFF); + put_int16(v, a); + a += 2; } - - sys_memcpy(&sockAddrP->sin6_addr, &addr, sizeof(addr)); - *addrLen = sizeof(struct sockaddr_in6); + sys_memcpy(inAddrP, &addr, addrLen); + } return NULL; @@ -754,7 +773,7 @@ char* esock_encode_ip6_address(ErlNifEnv* env, unsigned int i; ERL_NIF_TERM at[8]; unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); - unsigned char* a = (unsigned char*) &addrP; + unsigned char* a = (unsigned char*) addrP; /* The address */ for (i = 0; i < atLen; i++) { diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index cd8cc7e1fb..f1c122e281 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -32,10 +32,6 @@ #define ESOCK_ABORT(E) esock_abort(E, __func__, __FILE__, __LINE__) #define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0))) -/* Two byte integer decoding */ -#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ - (((unsigned char*) (s))[1])) - extern char* esock_decode_sockaddr(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, @@ -85,10 +81,9 @@ char* esock_encode_sockaddr_un(ErlNifEnv* env, #endif extern -char* esock_decode_ip4_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in* sockAddrP, - unsigned int* addrLen); +char* esock_decode_ip4_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in_addr* inAddrP); extern char* esock_encode_ip4_address(ErlNifEnv* env, struct in_addr* addrP, @@ -96,10 +91,9 @@ char* esock_encode_ip4_address(ErlNifEnv* env, #if defined(HAVE_IN6) && defined(AF_INET6) extern -char* esock_decode_ip6_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in6* sockAddrP, - unsigned int* addrLen); +char* esock_decode_ip6_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in6_addr* inAddrP); extern char* esock_encode_ip6_address(ErlNifEnv* env, struct in6_addr* addrP, -- cgit v1.2.3 From 04524794d5e1f80a33f48201e5c14de8c396c30e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 Jul 2018 11:20:46 +0200 Subject: [socket-nif] Add support for socket (level ip) options [add|drop]_membership Added support for the socket options (level ip) add_membership and drop_membership. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 146 +++++++++++++++++++++++++++++++-- 1 file changed, 139 insertions(+), 7 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 05ba9e55f1..267151083f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -362,13 +362,15 @@ typedef union { #define SOCKET_OPT_SOCK_SNDBUF 27 #define SOCKET_OPT_SOCK_TYPE 32 -#define SOCKET_OPT_IP_MULTICAST_IF 14 -#define SOCKET_OPT_IP_MULTICAST_LOOP 15 -#define SOCKET_OPT_IP_MULTICAST_TTL 16 -#define SOCKET_OPT_IP_RECVTOS 25 -#define SOCKET_OPT_IP_ROUTER_ALERT 28 -#define SOCKET_OPT_IP_TOS 30 -#define SOCKET_OPT_IP_TTL 32 +#define SOCKET_OPT_IP_ADD_MEMBERSHIP 1 +#define SOCKET_OPT_IP_DROP_MEMBERSHIP 5 +#define SOCKET_OPT_IP_MULTICAST_IF 14 +#define SOCKET_OPT_IP_MULTICAST_LOOP 15 +#define SOCKET_OPT_IP_MULTICAST_TTL 16 +#define SOCKET_OPT_IP_RECVTOS 25 +#define SOCKET_OPT_IP_ROUTER_ALERT 28 +#define SOCKET_OPT_IP_TOS 30 +#define SOCKET_OPT_IP_TTL 32 #define SOCKET_OPT_IPV6_HOPLIMIT 12 @@ -846,6 +848,23 @@ static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, ERL_NIF_TERM eVal); +#if defined(IP_ADD_MEMBERSHIP) +static ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(IP_DROP_MEMBERSHIP) +static ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(IP_DROP_MEMBERSHIP) || defined(IP_ADD_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal, + int opt); +#endif #if defined(IP_MULTICAST_IF) static ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, SocketDescriptor* descP, @@ -1397,7 +1416,9 @@ static char str_global_counters[] = "global_counters"; static char str_in4_sockaddr[] = "in4_sockaddr"; static char str_in6_sockaddr[] = "in6_sockaddr"; static char str_iow[] = "iow"; +static char str_interface[] = "interface"; // static char str_loopback[] = "loopback"; +static char str_multiaddr[] = "multiaddr"; static char str_nif_abort[] = "nif_abort"; static char str_select[] = "select"; static char str_num_dlocal[] = "num_domain_local"; @@ -1477,6 +1498,8 @@ static ERL_NIF_TERM atom_global_counters; static ERL_NIF_TERM atom_in4_sockaddr; static ERL_NIF_TERM atom_in6_sockaddr; static ERL_NIF_TERM atom_iow; +static ERL_NIF_TERM atom_interface; +static ERL_NIF_TERM atom_multiaddr; static ERL_NIF_TERM atom_nif_abort; static ERL_NIF_TERM atom_num_dinet; static ERL_NIF_TERM atom_num_dinet6; @@ -3968,6 +3991,18 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_ADD_MEMBERSHIP) + case SOCKET_OPT_IP_ADD_MEMBERSHIP: + result = nsetopt_lvl_ip_add_membership(env, descP, eVal); + break; +#endif + +#if defined(IP_DROP_MEMBERSHIP) + case SOCKET_OPT_IP_DROP_MEMBERSHIP: + result = nsetopt_lvl_ip_drop_membership(env, descP, eVal); + break; +#endif + #if defined(IP_MULTICAST_IF) case SOCKET_OPT_IP_MULTICAST_IF: result = nsetopt_lvl_ip_multicast_if(env, descP, eVal); @@ -4019,6 +4054,101 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, } +/* nsetopt_lvl_ip_add_membership - Level IP ADD_MEMBERSHIP option + * + * The value is a map with two attributes: multiaddr and interface. + * The attribute 'multiaddr' is always a 4-tuple (IPv4 address). + * The attribute 'interface' is either the atom 'any' or a 4-tuple + * (IPv4 address). + */ +#if defined(IP_ADD_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ip_update_membership(env, descP, eVal, IP_ADD_MEMBERSHIP); +} +#endif + + +/* nsetopt_lvl_ip_drop_membership - Level IP DROP_MEMBERSHIP option + * + * The value is a map with two attributes: multiaddr and interface. + * The attribute 'multiaddr' is always a 4-tuple (IPv4 address). + * The attribute 'interface' is either the atom 'any' or a 4-tuple + * (IPv4 address). + * + * We should really have a common function with add_membership, + * since the code is virtually identical (except for the option + * value). + */ +#if defined(IP_DROP_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ip_update_membership(env, descP, eVal, IP_DROP_MEMBERSHIP); +} +#endif + + + +#if defined(IP_DROP_MEMBERSHIP) || defined(IP_ADD_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal, + int opt) +{ + ERL_NIF_TERM result, eMultiAddr, eInterface; + struct ip_mreq mreq; + char* xres; + int res; + size_t sz; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + // It must be a map + if (!IS_MAP(env, eVal)) + return enif_make_badarg(env); + + // It must have exactly two attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz >= 2)) + return enif_make_badarg(env); + + if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) + return enif_make_badarg(env); + + if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) + return enif_make_badarg(env); + + if ((xres = esock_decode_ip4_address(env, + eMultiAddr, + &mreq.imr_multiaddr)) != NULL) + return esock_make_error_str(env, xres); + + if ((xres = esock_decode_ip4_address(env, + eInterface, + &mreq.imr_interface)) != NULL) + return esock_make_error_str(env, xres); + + res = socket_setopt(descP->sock, level, opt, &mreq, sizeof(mreq)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + return result; +} +#endif + + /* nsetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option * * The value is either the atom 'any' or a 4-tuple. @@ -7510,6 +7640,8 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_in4_sockaddr = MKA(env, str_in4_sockaddr); atom_in6_sockaddr = MKA(env, str_in6_sockaddr); atom_iow = MKA(env, str_iow); + atom_interface = MKA(env, str_interface); + atom_multiaddr = MKA(env, str_multiaddr); atom_nif_abort = MKA(env, str_nif_abort); atom_num_dinet = MKA(env, str_num_dinet); atom_num_dinet6 = MKA(env, str_num_dinet6); -- cgit v1.2.3 From e5a5cb1025270c265baeda89dd4cd13a1417a262 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 Jul 2018 18:20:06 +0200 Subject: [socket-nif] Add support for socket (level ip) option mtu Added support for the IP option MTU. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 267151083f..6b9f27aa3f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -364,6 +364,7 @@ typedef union { #define SOCKET_OPT_IP_ADD_MEMBERSHIP 1 #define SOCKET_OPT_IP_DROP_MEMBERSHIP 5 +#define SOCKET_OPT_IP_MTU 11 #define SOCKET_OPT_IP_MULTICAST_IF 14 #define SOCKET_OPT_IP_MULTICAST_LOOP 15 #define SOCKET_OPT_IP_MULTICAST_TTL 16 @@ -1048,6 +1049,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); +#if defined(IP_MTU) +static ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_MULTICAST_IF) static ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, SocketDescriptor* descP); @@ -5453,6 +5458,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_MTU) + case SOCKET_OPT_IP_MTU: + result = ngetopt_lvl_ip_mtu(env, descP); + break; +#endif + #if defined(IP_MULTICAST_IF) case SOCKET_OPT_IP_MULTICAST_IF: result = ngetopt_lvl_ip_multicast_if(env, descP); @@ -5504,6 +5515,24 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, } +/* ngetopt_lvl_ip_mtu - Level IP MTU option + */ +#if defined(IP_MTU) +static +ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_int_opt(env, descP, level, IP_MTU); +} +#endif + + /* ngetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option */ #if defined(IP_MULTICAST_IF) -- cgit v1.2.3 From e0f27afac1cb3e3a5567a05081d0cf307c154b0d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 10:24:44 +0200 Subject: [socket-nif] Add support for socket (level ip) option mtu_discover Added support for the IP option MTU_DISCOVER. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 225 ++++++++++++++++++++++++++++++++- 1 file changed, 222 insertions(+), 3 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 6b9f27aa3f..65e530adee 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -365,6 +365,7 @@ typedef union { #define SOCKET_OPT_IP_ADD_MEMBERSHIP 1 #define SOCKET_OPT_IP_DROP_MEMBERSHIP 5 #define SOCKET_OPT_IP_MTU 11 +#define SOCKET_OPT_IP_MTU_DISCOVER 12 #define SOCKET_OPT_IP_MULTICAST_IF 14 #define SOCKET_OPT_IP_MULTICAST_LOOP 15 #define SOCKET_OPT_IP_MULTICAST_TTL 16 @@ -866,6 +867,11 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, ERL_NIF_TERM eVal, int opt); #endif +#if defined(IP_MTU_DISCOVER) +static ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_MULTICAST_IF) static ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, SocketDescriptor* descP, @@ -1053,6 +1059,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_MTU_DISCOVER) +static ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_MULTICAST_IF) static ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, SocketDescriptor* descP); @@ -1282,9 +1292,22 @@ static void encode_address(ErlNifEnv* env, static BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* valP); +#if defined(IP_TOS) static BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val); +#endif +#if defined(IP_MTU_DISCOVER) +static char* decode_ip_pmtudisc(ErlNifEnv* env, + ERL_NIF_TERM eVal, + int* val); +#endif +#if defined(IP_MTU_DISCOVER) +static void encode_ip_pmtudisc(ErlNifEnv* env, + int val, + ERL_NIF_TERM* eVal); +#endif + /* static BOOLEAN_T decode_bool(ErlNifEnv* env, ERL_NIF_TERM eVal, @@ -1416,6 +1439,8 @@ static const struct in6_addr in6addr_loopback = static char str_close[] = "close"; static char str_closed[] = "closed"; static char str_closing[] = "closing"; +static char str_do[] = "do"; +static char str_dont[] = "dont"; static char str_false[] = "false"; static char str_global_counters[] = "global_counters"; static char str_in4_sockaddr[] = "in4_sockaddr"; @@ -1425,7 +1450,6 @@ static char str_interface[] = "interface"; // static char str_loopback[] = "loopback"; static char str_multiaddr[] = "multiaddr"; static char str_nif_abort[] = "nif_abort"; -static char str_select[] = "select"; static char str_num_dlocal[] = "num_domain_local"; static char str_num_dinet[] = "num_domain_inet"; static char str_num_dinet6[] = "num_domain_inet6"; @@ -1437,8 +1461,11 @@ static char str_num_sockets[] = "num_sockets"; static char str_num_tdgrams[] = "num_type_dgram"; static char str_num_tseqpkgs[] = "num_type_seqpacket"; static char str_num_tstreams[] = "num_type_stream"; +static char str_probe[] = "probe"; +static char str_select[] = "select"; static char str_timeout[] = "timeout"; static char str_true[] = "true"; +static char str_want[] = "want"; static char str_lowdelay[] = "lowdelay"; static char str_throughput[] = "throughput"; @@ -1498,6 +1525,8 @@ ERL_NIF_TERM esock_atom_einval; static ERL_NIF_TERM atom_close; static ERL_NIF_TERM atom_closed; static ERL_NIF_TERM atom_closing; +static ERL_NIF_TERM atom_do; +static ERL_NIF_TERM atom_dont; static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_global_counters; static ERL_NIF_TERM atom_in4_sockaddr; @@ -1517,9 +1546,11 @@ static ERL_NIF_TERM atom_num_sockets; static ERL_NIF_TERM atom_num_tdgrams; static ERL_NIF_TERM atom_num_tseqpkgs; static ERL_NIF_TERM atom_num_tstreams; +static ERL_NIF_TERM atom_probe; static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_want; static ERL_NIF_TERM atom_lowdelay; static ERL_NIF_TERM atom_throughput; @@ -4008,6 +4039,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_MTU_DISCOVER) + case SOCKET_OPT_IP_MTU_DISCOVER: + result = nsetopt_lvl_ip_mtu_discover(env, descP, eVal); + break; +#endif + #if defined(IP_MULTICAST_IF) case SOCKET_OPT_IP_MULTICAST_IF: result = nsetopt_lvl_ip_multicast_if(env, descP, eVal); @@ -4154,6 +4191,47 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option + * + * The value is an atom of the type ip_pmtudisc(). + */ +#if defined(IP_MTU_DISCOVER) +static +ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + int val; + char* xres; + int res; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + if ((xres = decode_ip_pmtudisc(env, eVal, &val)) != NULL) { + + result = esock_make_error_str(env, xres); + + } else { + + res = socket_setopt(descP->sock, level, IP_MTU_DISCOVER, + &val, sizeof(val)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + } + + return result; +} +#endif + + /* nsetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option * * The value is either the atom 'any' or a 4-tuple. @@ -5236,7 +5314,9 @@ ERL_NIF_TERM ngetopt_lvl_sock_domain(ErlNifEnv* env, default: result = esock_make_error(env, - MKT2(env, esock_atom_unknown, MKI(env, val))); + MKT2(env, + esock_atom_unknown, + MKI(env, val))); break; } } @@ -5464,6 +5544,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_MTU_DISCOVER) + case SOCKET_OPT_IP_MTU_DISCOVER: + result = ngetopt_lvl_ip_mtu_discover(env, descP); + break; +#endif + #if defined(IP_MULTICAST_IF) case SOCKET_OPT_IP_MULTICAST_IF: result = ngetopt_lvl_ip_multicast_if(env, descP); @@ -5533,6 +5619,40 @@ ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option + */ +#if defined(IP_MTU_DISCOVER) +static +ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + ERL_NIF_TERM eMtuDisc; + int mtuDisc; + SOCKOPTLEN_T mtuDiscSz = sizeof(mtuDisc); + int res; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + res = sock_getopt(descP->sock, level, IP_MTU_DISCOVER, + &mtuDisc, &mtuDiscSz); + + if (res != 0) { + result = esock_make_error_errno(env, sock_errno()); + } else { + encode_ip_pmtudisc(env, mtuDisc, &eMtuDisc); + result = esock_make_ok2(env, eMtuDisc); + } + + return result; + +} +#endif + + /* ngetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option */ #if defined(IP_MULTICAST_IF) @@ -6532,7 +6652,7 @@ BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* v -/* +++ decode the ip socket option tos +++ +/* +++ decode the ip socket option TOS +++ * The (ip) option can be provide in two ways: * * atom() | integer() @@ -6542,6 +6662,7 @@ BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* v * lowdelay | throughput | reliability | mincost * */ +#if defined(IP_TOS) static BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) { @@ -6596,6 +6717,100 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) return result; } +#endif + + + +/* +++ decode the ip socket option MTU_DISCOVER +++ + * The (ip) option can be provide in two ways: + * + * atom() | integer() + * + * When its an atom it can have the values: + * + * want | dont | do | probe + * + */ +#if defined(IP_MTU_DISCOVER) +static +char* decode_ip_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) +{ + char* res = NULL; + + if (IS_ATOM(env, eVal)) { + + if (COMPARE(eVal, atom_want) == 0) { + *val = IP_PMTUDISC_WANT; + } else if (COMPARE(eVal, atom_dont) == 0) { + *val = IP_PMTUDISC_DONT; + } else if (COMPARE(eVal, atom_do) == 0) { + *val = IP_PMTUDISC_DO; + } else if (COMPARE(eVal, atom_probe) == 0) { + *val = IP_PMTUDISC_PROBE; + } else { + *val = -1; + res = ESOCK_STR_EINVAL; + } + + } else if (IS_NUM(env, eVal)) { + + if (!GET_INT(env, eVal, val)) { + *val = -1; + res = ESOCK_STR_EINVAL; + } + + } else { + + *val = -1; + res = ESOCK_STR_EINVAL; + + } + + return res; +} +#endif + + + +/* +++ encode the ip socket option MTU_DISCOVER +++ + * The (ip) option can be provide in two ways: + * + * atom() | integer() + * + * If its one of the "known" values, it will be an atom: + * + * want | dont | do | probe + * + */ +#if defined(IP_MTU_DISCOVER) +static +void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) +{ + switch (val) { + case IP_PMTUDISC_WANT: + *eVal = atom_want; + break; + + case IP_PMTUDISC_DONT: + *eVal = atom_dont; + break; + + case IP_PMTUDISC_DO: + *eVal = atom_do; + break; + + case IP_PMTUDISC_PROBE: + *eVal = atom_probe; + break; + + default: + *eVal = MKI(env, val); + break; + } + + return; +} +#endif @@ -7664,6 +7879,8 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_close = MKA(env, str_close); atom_closed = MKA(env, str_closed); atom_closing = MKA(env, str_closing); + atom_do = MKA(env, str_do); + atom_dont = MKA(env, str_dont); atom_false = MKA(env, str_false); atom_global_counters = MKA(env, str_global_counters); atom_in4_sockaddr = MKA(env, str_in4_sockaddr); @@ -7683,9 +7900,11 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_num_tdgrams = MKA(env, str_num_tdgrams); atom_num_tseqpkgs = MKA(env, str_num_tseqpkgs); atom_num_tstreams = MKA(env, str_num_tstreams); + atom_probe = MKA(env, str_probe); atom_select = MKA(env, str_select); atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); + atom_want = MKA(env, str_want); /* Global atom(s) */ esock_atom_addr = MKA(env, "addr"); -- cgit v1.2.3 From ea680e88b967440b2ecd925321ebb5dd48461608 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 10:42:19 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_all Added support for the IP option MULTICAST_ALL. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 53 ++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 65e530adee..a2bebcd621 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -366,6 +366,7 @@ typedef union { #define SOCKET_OPT_IP_DROP_MEMBERSHIP 5 #define SOCKET_OPT_IP_MTU 11 #define SOCKET_OPT_IP_MTU_DISCOVER 12 +#define SOCKET_OPT_IP_MULTICAST_ALL 13 #define SOCKET_OPT_IP_MULTICAST_IF 14 #define SOCKET_OPT_IP_MULTICAST_LOOP 15 #define SOCKET_OPT_IP_MULTICAST_TTL 16 @@ -872,6 +873,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_MULTICAST_ALL) +static ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_MULTICAST_IF) static ERL_NIF_TERM nsetopt_lvl_ip_multicast_if(ErlNifEnv* env, SocketDescriptor* descP, @@ -1063,6 +1069,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_MULTICAST_ALL) +static ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_MULTICAST_IF) static ERL_NIF_TERM ngetopt_lvl_ip_multicast_if(ErlNifEnv* env, SocketDescriptor* descP); @@ -4232,6 +4242,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_multicast_all - Level IP MULTICAST_ALL option + */ +#if defined(IP_MULTICAST_ALL) +static +ERL_NIF_TERM nsetopt_lvl_ip_multicast_all(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_MULTICAST_ALL, eVal); +} +#endif + + /* nsetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option * * The value is either the atom 'any' or a 4-tuple. @@ -5550,6 +5579,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_MULTICAST_ALL) + case SOCKET_OPT_IP_MULTICAST_ALL: + result = ngetopt_lvl_ip_multicast_all(env, descP); + break; +#endif + #if defined(IP_MULTICAST_IF) case SOCKET_OPT_IP_MULTICAST_IF: result = ngetopt_lvl_ip_multicast_if(env, descP); @@ -5653,6 +5688,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_mtu_discover(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_multicast_all - Level IP MULTICAST_ALL option + */ +#if defined(IP_MULTICAST_ALL) +static +ERL_NIF_TERM ngetopt_lvl_ip_multicast_all(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_MULTICAST_ALL); +} +#endif + + /* ngetopt_lvl_ip_multicast_if - Level IP MULTICAST_IF option */ #if defined(IP_MULTICAST_IF) -- cgit v1.2.3 From 0122d2cafdb6f44c221796f1a6b2a5188dfb153d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 11:04:38 +0200 Subject: [socket-nif] Add support for socket (level ip) option nodefrag Added support for the IP option NODEFRAG. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index a2bebcd621..0972834cb2 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -370,6 +370,7 @@ typedef union { #define SOCKET_OPT_IP_MULTICAST_IF 14 #define SOCKET_OPT_IP_MULTICAST_LOOP 15 #define SOCKET_OPT_IP_MULTICAST_TTL 16 +#define SOCKET_OPT_IP_NODEFRAG 17 #define SOCKET_OPT_IP_RECVTOS 25 #define SOCKET_OPT_IP_ROUTER_ALERT 28 #define SOCKET_OPT_IP_TOS 30 @@ -893,6 +894,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_NODEFRAG) +static ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP, @@ -1085,6 +1091,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_multicast_loop(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_NODEFRAG) +static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP); @@ -4055,6 +4065,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_MULTICAST_ALL) + case SOCKET_OPT_IP_MULTICAST_ALL: + result = nsetopt_lvl_ip_multicast_all(env, descP, eVal); + break; +#endif + #if defined(IP_MULTICAST_IF) case SOCKET_OPT_IP_MULTICAST_IF: result = nsetopt_lvl_ip_multicast_if(env, descP, eVal); @@ -4073,6 +4089,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_NODEFRAG) + case SOCKET_OPT_IP_NODEFRAG: + result = nsetopt_lvl_ip_nodefrag(env, descP, eVal); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = nsetopt_lvl_ip_recvtos(env, descP, eVal); @@ -4338,6 +4360,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_nodefrag - Level IP NODEFRAG option + */ +#if defined(IP_NODEFRAG) +static +ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_NODEFRAG, eVal); +} +#endif + + /* nsetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) @@ -5603,6 +5644,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_NODEFRAG) + case SOCKET_OPT_IP_NODEFRAG: + result = ngetopt_lvl_ip_nodefrag(env, descP); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = ngetopt_lvl_ip_recvtos(env, descP); @@ -5779,6 +5826,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_nodefrag - Level IP NODEFRAG option + */ +#if defined(IP_NODEFRAG) +static +ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_NODEFRAG); +} +#endif + + /* ngetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) -- cgit v1.2.3 From ca81f1d1602cf994fca9fcd61e892c76e4e2742c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 11:22:53 +0200 Subject: [socket-nif] Add support for socket (level ip) option recvttl Added support for the IP option RECVTTL. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 0972834cb2..22ac124802 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -372,6 +372,7 @@ typedef union { #define SOCKET_OPT_IP_MULTICAST_TTL 16 #define SOCKET_OPT_IP_NODEFRAG 17 #define SOCKET_OPT_IP_RECVTOS 25 +#define SOCKET_OPT_IP_RECVTTL 26 #define SOCKET_OPT_IP_ROUTER_ALERT 28 #define SOCKET_OPT_IP_TOS 30 #define SOCKET_OPT_IP_TTL 32 @@ -904,6 +905,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_RECVTTL) +static ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_ROUTER_ALERT) static ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, SocketDescriptor* descP, @@ -1099,6 +1105,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_RECVTTL) +static ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_ROUTER_ALERT) static ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, SocketDescriptor* descP); @@ -4101,6 +4111,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVTTL) + case SOCKET_OPT_IP_RECVTTL: + result = nsetopt_lvl_ip_recvttl(env, descP, eVal); + break; +#endif + #if defined(IP_ROUTER_ALERT) case SOCKET_OPT_IP_ROUTER_ALERT: result = nsetopt_lvl_ip_router_alert(env, descP, eVal); @@ -4398,6 +4414,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_recvttl - Level IP RECVTTL option + */ +#if defined(IP_RECVTTL) +static +ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_RECVTTL, eVal); +} +#endif + + /* nsetopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option */ #if defined(IP_ROUTER_ALERT) @@ -5656,6 +5691,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVTTL) + case SOCKET_OPT_IP_RECVTTL: + result = ngetopt_lvl_ip_recvttl(env, descP); + break; +#endif + #if defined(IP_ROUTER_ALERT) case SOCKET_OPT_IP_ROUTER_ALERT: result = ngetopt_lvl_ip_router_alert(env, descP); @@ -5862,6 +5903,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_recvttl - Level IP RECVTTL option + */ +#if defined(IP_RECVTTL) +static +ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_RECVTTL); +} +#endif + + /* ngetopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option */ #if defined(IP_ROUTER_ALERT) -- cgit v1.2.3 From 3d045788e8033e2e76396900358976ac92fb184a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 12:43:41 +0200 Subject: [socket-nif] Add support for socket (level ip) option(s) sources Added support for the IP options: ADD_SOURCE_MEMBERSHIP, DROP_SOURCE_MEMBERSHIP, BLOCK_SOURCE and UNBLOCK_SOURCE. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 353 ++++++++++++++++++++++++++------- 1 file changed, 284 insertions(+), 69 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 22ac124802..109d1a2d5c 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -362,20 +362,24 @@ typedef union { #define SOCKET_OPT_SOCK_SNDBUF 27 #define SOCKET_OPT_SOCK_TYPE 32 -#define SOCKET_OPT_IP_ADD_MEMBERSHIP 1 -#define SOCKET_OPT_IP_DROP_MEMBERSHIP 5 -#define SOCKET_OPT_IP_MTU 11 -#define SOCKET_OPT_IP_MTU_DISCOVER 12 -#define SOCKET_OPT_IP_MULTICAST_ALL 13 -#define SOCKET_OPT_IP_MULTICAST_IF 14 -#define SOCKET_OPT_IP_MULTICAST_LOOP 15 -#define SOCKET_OPT_IP_MULTICAST_TTL 16 -#define SOCKET_OPT_IP_NODEFRAG 17 -#define SOCKET_OPT_IP_RECVTOS 25 -#define SOCKET_OPT_IP_RECVTTL 26 -#define SOCKET_OPT_IP_ROUTER_ALERT 28 -#define SOCKET_OPT_IP_TOS 30 -#define SOCKET_OPT_IP_TTL 32 +#define SOCKET_OPT_IP_ADD_MEMBERSHIP 1 +#define SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP 2 +#define SOCKET_OPT_IP_BLOCK_SOURCE 3 +#define SOCKET_OPT_IP_DROP_MEMBERSHIP 5 +#define SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP 6 +#define SOCKET_OPT_IP_MTU 11 +#define SOCKET_OPT_IP_MTU_DISCOVER 12 +#define SOCKET_OPT_IP_MULTICAST_ALL 13 +#define SOCKET_OPT_IP_MULTICAST_IF 14 +#define SOCKET_OPT_IP_MULTICAST_LOOP 15 +#define SOCKET_OPT_IP_MULTICAST_TTL 16 +#define SOCKET_OPT_IP_NODEFRAG 17 +#define SOCKET_OPT_IP_RECVTOS 25 +#define SOCKET_OPT_IP_RECVTTL 26 +#define SOCKET_OPT_IP_ROUTER_ALERT 28 +#define SOCKET_OPT_IP_TOS 30 +#define SOCKET_OPT_IP_TTL 32 +#define SOCKET_OPT_IP_UNBLOCK_SOURCE 33 #define SOCKET_OPT_IPV6_HOPLIMIT 12 @@ -794,6 +798,8 @@ static ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, ERL_NIF_TERM eVal); + +/* *** Handling set of socket options for level = socket *** */ #if defined(SO_BROADCAST) static ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, SocketDescriptor* descP, @@ -853,22 +859,32 @@ static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, ERL_NIF_TERM eVal); + +/* *** Handling set of socket options for level = ip *** */ #if defined(IP_ADD_MEMBERSHIP) static ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_ADD_SOURCE_MEMBERSHIP) +static ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(IP_BLOCK_SOURCE) +static ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_DROP_MEMBERSHIP) static ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif -#if defined(IP_DROP_MEMBERSHIP) || defined(IP_ADD_MEMBERSHIP) -static -ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal, - int opt); +#if defined(IP_DROP_SOURCE_MEMBERSHIP) +static ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); #endif #if defined(IP_MTU_DISCOVER) static ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, @@ -925,6 +941,29 @@ static ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_UNBLOCK_SOURCE) +static ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif + +#if defined(IP_DROP_MEMBERSHIP) || defined(IP_ADD_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal, + int opt); +#endif +#if defined(IP_ADD_SOURCE_MEMBERSHIP) || defined(IP_DROP_SOURCE_MEMBERSHIP) || defined(IP_BLOCK_SOURCE) || defined(IP_UNBLOCK_SOURCE) +static +ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal, + int opt); +#endif + + +/* *** Handling set of socket options for level = ipv6 *** */ #if defined(SOL_IPV6) static ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, SocketDescriptor* descP, @@ -1493,6 +1532,7 @@ static char str_num_tseqpkgs[] = "num_type_seqpacket"; static char str_num_tstreams[] = "num_type_stream"; static char str_probe[] = "probe"; static char str_select[] = "select"; +static char str_sourceaddr[] = "sourceaddr"; static char str_timeout[] = "timeout"; static char str_true[] = "true"; static char str_want[] = "want"; @@ -1578,6 +1618,7 @@ static ERL_NIF_TERM atom_num_tseqpkgs; static ERL_NIF_TERM atom_num_tstreams; static ERL_NIF_TERM atom_probe; static ERL_NIF_TERM atom_select; +static ERL_NIF_TERM atom_sourceaddr; static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_want; @@ -4063,12 +4104,30 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_ADD_SOURCE_MEMBERSHIP) + case SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP: + result = nsetopt_lvl_ip_add_source_membership(env, descP, eVal); + break; +#endif + +#if defined(IP_BLOCK_SOURCE) + case SOCKET_OPT_IP_BLOCK_SOURCE: + result = nsetopt_lvl_ip_block_source(env, descP, eVal); + break; +#endif + #if defined(IP_DROP_MEMBERSHIP) case SOCKET_OPT_IP_DROP_MEMBERSHIP: result = nsetopt_lvl_ip_drop_membership(env, descP, eVal); break; #endif +#if defined(IP_DROP_SOURCE_MEMBERSHIP) + case SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP: + result = nsetopt_lvl_ip_drop_source_membership(env, descP, eVal); + break; +#endif + #if defined(IP_MTU_DISCOVER) case SOCKET_OPT_IP_MTU_DISCOVER: result = nsetopt_lvl_ip_mtu_discover(env, descP, eVal); @@ -4135,6 +4194,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_UNBLOCK_SOURCE) + case SOCKET_OPT_IP_UNBLOCK_SOURCE: + result = nsetopt_lvl_ip_unblock_source(env, descP, eVal); + break; +#endif + default: result = esock_make_error(env, esock_atom_einval); break; @@ -4162,6 +4227,47 @@ ERL_NIF_TERM nsetopt_lvl_ip_add_membership(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_add_source_membership - Level IP ADD_SOURCE_MEMBERSHIP option + * + * The value is a map with three attributes: multiaddr, interface and + * sourceaddr. + * The attribute 'multiaddr' is always a 4-tuple (IPv4 address). + * The attribute 'interface' is always a 4-tuple (IPv4 address). + * The attribute 'sourceaddr' is always a 4-tuple (IPv4 address). + * (IPv4 address). + */ +#if defined(IP_ADD_SOURCE_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ip_add_source_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ip_update_source(env, descP, eVal, + IP_ADD_SOURCE_MEMBERSHIP); +} +#endif + + +/* nsetopt_lvl_ip_block_source - Level IP BLOCK_SOURCE option + * + * The value is a map with three attributes: multiaddr, interface and + * sourceaddr. + * The attribute 'multiaddr' is always a 4-tuple (IPv4 address). + * The attribute 'interface' is always a 4-tuple (IPv4 address). + * The attribute 'sourceaddr' is always a 4-tuple (IPv4 address). + * (IPv4 address). + */ +#if defined(IP_BLOCK_SOURCE) +static +ERL_NIF_TERM nsetopt_lvl_ip_block_source(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ip_update_source(env, descP, eVal, IP_BLOCK_SOURCE); +} +#endif + + /* nsetopt_lvl_ip_drop_membership - Level IP DROP_MEMBERSHIP option * * The value is a map with two attributes: multiaddr and interface. @@ -4179,66 +4285,35 @@ ERL_NIF_TERM nsetopt_lvl_ip_drop_membership(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_lvl_ip_update_membership(env, descP, eVal, IP_DROP_MEMBERSHIP); + return nsetopt_lvl_ip_update_membership(env, descP, eVal, + IP_DROP_MEMBERSHIP); } #endif -#if defined(IP_DROP_MEMBERSHIP) || defined(IP_ADD_MEMBERSHIP) +/* nsetopt_lvl_ip_drop_source_membership - Level IP DROP_SOURCE_MEMBERSHIP option + * + * The value is a map with three attributes: multiaddr, interface and + * sourceaddr. + * The attribute 'multiaddr' is always a 4-tuple (IPv4 address). + * The attribute 'interface' is always a 4-tuple (IPv4 address). + * The attribute 'sourceaddr' is always a 4-tuple (IPv4 address). + * (IPv4 address). + */ +#if defined(IP_DROP_SOURCE_MEMBERSHIP) static -ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal, - int opt) +ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) { - ERL_NIF_TERM result, eMultiAddr, eInterface; - struct ip_mreq mreq; - char* xres; - int res; - size_t sz; -#if defined(SOL_IP) - int level = SOL_IP; -#else - int level = IPPROTO_IP; -#endif - - // It must be a map - if (!IS_MAP(env, eVal)) - return enif_make_badarg(env); - - // It must have exactly two attributes - if (!enif_get_map_size(env, eVal, &sz) || (sz >= 2)) - return enif_make_badarg(env); - - if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) - return enif_make_badarg(env); - - if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) - return enif_make_badarg(env); - - if ((xres = esock_decode_ip4_address(env, - eMultiAddr, - &mreq.imr_multiaddr)) != NULL) - return esock_make_error_str(env, xres); - - if ((xres = esock_decode_ip4_address(env, - eInterface, - &mreq.imr_interface)) != NULL) - return esock_make_error_str(env, xres); - - res = socket_setopt(descP->sock, level, opt, &mreq, sizeof(mreq)); - - if (res != 0) - result = esock_make_error_errno(env, sock_errno()); - else - result = esock_atom_ok; - - return result; + return nsetopt_lvl_ip_update_source(env, descP, eVal, + IP_DROP_SOURCE_MEMBERSHIP); } #endif + /* nsetopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option * * The value is an atom of the type ip_pmtudisc(). @@ -4505,6 +4580,145 @@ ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, +/* nsetopt_lvl_ip_unblock_source - Level IP UNBLOCK_SOURCE option + * + * The value is a map with three attributes: multiaddr, interface and + * sourceaddr. + * The attribute 'multiaddr' is always a 4-tuple (IPv4 address). + * The attribute 'interface' is always a 4-tuple (IPv4 address). + * The attribute 'sourceaddr' is always a 4-tuple (IPv4 address). + * (IPv4 address). + */ +#if defined(IP_UNBLOCK_SOURCE) +static +ERL_NIF_TERM nsetopt_lvl_ip_unblock_source(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ip_update_source(env, descP, eVal, IP_UNBLOCK_SOURCE); +} +#endif + + + +#if defined(IP_ADD_MEMBERSHIP) || defined(IP_DROP_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ip_update_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal, + int opt) +{ + ERL_NIF_TERM result, eMultiAddr, eInterface; + struct ip_mreq mreq; + char* xres; + int res; + size_t sz; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + // It must be a map + if (!IS_MAP(env, eVal)) + return enif_make_badarg(env); + + // It must have atleast two attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz >= 2)) + return enif_make_badarg(env); + + if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) + return enif_make_badarg(env); + + if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) + return enif_make_badarg(env); + + if ((xres = esock_decode_ip4_address(env, + eMultiAddr, + &mreq.imr_multiaddr)) != NULL) + return esock_make_error_str(env, xres); + + if ((xres = esock_decode_ip4_address(env, + eInterface, + &mreq.imr_interface)) != NULL) + return esock_make_error_str(env, xres); + + res = socket_setopt(descP->sock, level, opt, &mreq, sizeof(mreq)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + return result; +} +#endif + + +#if defined(IP_ADD_SOURCE_MEMBERSHIP) || defined(IP_DROP_SOURCE_MEMBERSHIP) || defined(IP_BLOCK_SOURCE) || defined(IP_UNBLOCK_SOURCE) +static +ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal, + int opt) +{ + ERL_NIF_TERM result, eMultiAddr, eInterface, eSourceAddr; + struct ip_mreq_source mreq; + char* xres; + int res; + size_t sz; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + // It must be a map + if (!IS_MAP(env, eVal)) + return enif_make_badarg(env); + + // It must have atleast three attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz >= 3)) + return enif_make_badarg(env); + + if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) + return enif_make_badarg(env); + + if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) + return enif_make_badarg(env); + + if (!GET_MAP_VAL(env, eVal, atom_sourceaddr, &eSourceAddr)) + return enif_make_badarg(env); + + if ((xres = esock_decode_ip4_address(env, + eMultiAddr, + &mreq.imr_multiaddr)) != NULL) + return esock_make_error_str(env, xres); + + if ((xres = esock_decode_ip4_address(env, + eInterface, + &mreq.imr_interface)) != NULL) + return esock_make_error_str(env, xres); + + if ((xres = esock_decode_ip4_address(env, + eSourceAddr, + &mreq.imr_sourceaddr)) != NULL) + return esock_make_error_str(env, xres); + + res = socket_setopt(descP->sock, level, opt, &mreq, sizeof(mreq)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + return result; +} +#endif + + +/* *** Handling set of socket options for level = ipv6 *** */ + /* nsetopt_lvl_ipv6 - Level *IPv6* option(s) */ #if defined(SOL_IPV6) @@ -8079,6 +8293,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_num_tstreams = MKA(env, str_num_tstreams); atom_probe = MKA(env, str_probe); atom_select = MKA(env, str_select); + atom_sourceaddr = MKA(env, str_sourceaddr); atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); atom_want = MKA(env, str_want); -- cgit v1.2.3 From 31ed3b1d9cff6171432459bf2de2b761e987ac97 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 17:00:34 +0200 Subject: [socket-nif] Add support for socket (level ip) open recvif Added support for the IP option RECVIF. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 50 ++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 109d1a2d5c..a851dc94b4 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -374,6 +374,7 @@ typedef union { #define SOCKET_OPT_IP_MULTICAST_LOOP 15 #define SOCKET_OPT_IP_MULTICAST_TTL 16 #define SOCKET_OPT_IP_NODEFRAG 17 +#define SOCKET_OPT_IP_RECVIF 21 #define SOCKET_OPT_IP_RECVTOS 25 #define SOCKET_OPT_IP_RECVTTL 26 #define SOCKET_OPT_IP_ROUTER_ALERT 28 @@ -4164,6 +4165,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVIF) + case SOCKET_OPT_IP_RECVIF: + result = nsetopt_lvl_ip_recvif(env, descP, eVal); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = nsetopt_lvl_ip_recvtos(env, descP, eVal); @@ -4470,6 +4477,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_recvif - Level IP RECVIFxs option + */ +#if defined(IP_RECVIF) +static +ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_RECVIF, eVal); +} +#endif + + /* nsetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) @@ -5899,6 +5925,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVIF) + case SOCKET_OPT_IP_RECVIF: + result = ngetopt_lvl_ip_recvif(env, descP); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = ngetopt_lvl_ip_recvtos(env, descP); @@ -6117,6 +6149,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_recvif - Level IP RECVIF option + */ +#if defined(IP_RECVIF) +static +ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_RECVIF); +} +#endif + + /* ngetopt_lvl_ip_recvttl - Level IP RECVTTL option */ #if defined(IP_RECVTTL) -- cgit v1.2.3 From 2151895b4eb4cb3172ba7597477fd83eb85b444a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 17:30:59 +0200 Subject: [socket-nif] Add support for socket (level ip) option minttl Added support for the IP option MINTTL. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index a851dc94b4..37bb307e2b 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -367,6 +367,7 @@ typedef union { #define SOCKET_OPT_IP_BLOCK_SOURCE 3 #define SOCKET_OPT_IP_DROP_MEMBERSHIP 5 #define SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP 6 +#define SOCKET_OPT_IP_MINTTL 9 #define SOCKET_OPT_IP_MTU 11 #define SOCKET_OPT_IP_MTU_DISCOVER 12 #define SOCKET_OPT_IP_MULTICAST_ALL 13 @@ -887,6 +888,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_MINTTL) +static ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_MTU_DISCOVER) static ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, SocketDescriptor* descP, @@ -1113,6 +1119,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); +#if defined(IP_MINTTL) +static ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_MTU) static ERL_NIF_TERM ngetopt_lvl_ip_mtu(ErlNifEnv* env, SocketDescriptor* descP); @@ -4129,6 +4139,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_MINTTL) + case SOCKET_OPT_IP_MINTTL: + result = nsetopt_lvl_ip_minttl(env, descP, eVal); + break; +#endif + #if defined(IP_MTU_DISCOVER) case SOCKET_OPT_IP_MTU_DISCOVER: result = nsetopt_lvl_ip_mtu_discover(env, descP, eVal); @@ -4321,6 +4337,26 @@ ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, +/* nsetopt_lvl_ip_minttl - Level IP MINTTL option + */ +#if defined(IP_MINTTL) +static +ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_int_opt(env, descP, level, IP_MINTTL, eVal); +} +#endif + + + /* nsetopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option * * The value is an atom of the type ip_pmtudisc(). @@ -5883,6 +5919,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_MINTTL) + case SOCKET_OPT_IP_MINTTL: + result = ngetopt_lvl_ip_minttl(env, descP); + break; +#endif + #if defined(IP_MTU) case SOCKET_OPT_IP_MTU: result = ngetopt_lvl_ip_mtu(env, descP); @@ -5970,6 +6012,24 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, } +/* ngetopt_lvl_ip_minttl - Level IP MINTTL option + */ +#if defined(IP_MINTTL) +static +ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_int_opt(env, descP, level, IP_MINTTL); +} +#endif + + /* ngetopt_lvl_ip_mtu - Level IP MTU option */ #if defined(IP_MTU) -- cgit v1.2.3 From 3a2c48b2740f08d3582028fd2f8d626009aa911e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 Jul 2018 21:17:14 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option(s) [add|drop]_membership Added support for the IPv6 options ADD_MEMBERSHIP, DROP_MEMBERSHIP OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 108 ++++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 37bb307e2b..1f9234016c 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -383,7 +383,9 @@ typedef union { #define SOCKET_OPT_IP_TTL 32 #define SOCKET_OPT_IP_UNBLOCK_SOURCE 33 -#define SOCKET_OPT_IPV6_HOPLIMIT 12 +#define SOCKET_OPT_IPV6_ADD_MEMBERSHIP 2 +#define SOCKET_OPT_IPV6_DROP_MEMBERSHIP 6 +#define SOCKET_OPT_IPV6_HOPLIMIT 12 #define SOCKET_OPT_TCP_CONGESTION 1 #define SOCKET_OPT_TCP_CORK 2 @@ -976,11 +978,29 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, ERL_NIF_TERM eVal); +#if defined(IPV6_ADD_MEMBERSHIP) +static ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(IPV6_DROP_MEMBERSHIP) +static ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif + +#if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP) +static ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal, + int opt); +#endif + #endif // defined(SOL_IPV6) static ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, SocketDescriptor* descP, @@ -4779,6 +4799,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, #endif + /* *** Handling set of socket options for level = ipv6 *** */ /* nsetopt_lvl_ipv6 - Level *IPv6* option(s) @@ -4793,6 +4814,18 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IPV6_ADD_MEMBERSHIP) + case SOCKET_OPT_IPV6_ADD_MEMBERSHIP: + result = nsetopt_lvl_ipv6_add_membership(env, descP, eVal); + break; +#endif + +#if defined(IPV6_DROP_MEMBERSHIP) + case SOCKET_OPT_IPV6_DROP_MEMBERSHIP: + result = nsetopt_lvl_ipv6_drop_membership(env, descP, eVal); + break; +#endif + #if defined(IPV6_HOPLIMIT) case SOCKET_OPT_IPV6_HOPLIMIT: result = nsetopt_lvl_ipv6_hoplimit(env, descP, eVal); @@ -4808,6 +4841,30 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, } +#if defined(IPV6_ADD_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ipv6_update_membership(env, descP, eVal, + IPV6_ADD_MEMBERSHIP); +} +#endif + + +#if defined(IPV6_DROP_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ipv6_update_membership(env, descP, eVal, + IPV6_DROP_MEMBERSHIP); +} +#endif + + #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, @@ -4819,6 +4876,55 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, #endif +#if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal, + int opt) +{ + ERL_NIF_TERM result, eMultiAddr, eInterface; + struct ipv6_mreq mreq; + char* xres; + int res; + size_t sz; + int level = IPPROTO_IPV6; + + // It must be a map + if (!IS_MAP(env, eVal)) + return enif_make_badarg(env); + + // It must have atleast two attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz >= 2)) + return enif_make_badarg(env); + + if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) + return enif_make_badarg(env); + + if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) + return enif_make_badarg(env); + + if ((xres = esock_decode_ip6_address(env, + eMultiAddr, + &mreq.ipv6mr_multiaddr)) != NULL) + return esock_make_error_str(env, xres); + + if (!GET_UINT(env, eInterface, &mreq.ipv6mr_interface)) + return esock_make_error(env, esock_atom_einval); + + res = socket_setopt(descP->sock, level, opt, &mreq, sizeof(mreq)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + return result; +} +#endif + + + #endif // defined(SOL_IPV6) -- cgit v1.2.3 From 416644989e26ac76038523511d81ebf9e0b8fc4f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 Jul 2018 12:34:06 +0200 Subject: [net+socket-nif] Updated on_load stuff The net nif-module had debug on by default. Fixed this and also made it an option, just as for the socket nif-module. OTP-14831 --- erts/emulator/nifs/common/net_nif.c | 20 ++++++++++- erts/emulator/nifs/common/socket_nif.c | 60 +++++++++++++-------------------- erts/emulator/nifs/common/socket_util.c | 27 +++++++++++++++ erts/emulator/nifs/common/socket_util.h | 5 +++ 4 files changed, 75 insertions(+), 37 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 6829de509b..309ad05a36 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -209,6 +209,9 @@ # define SOCKLEN_T size_t #endif +/* Debug stuff... */ +#define NET_NIF_DEBUG_DEFAULT FALSE + #define NDBG( proto ) ESOCK_DBG_PRINTF( data.debug , proto ) @@ -331,6 +334,8 @@ static char* make_address_info(ErlNifEnv* env, ERL_NIF_TERM addr, ERL_NIF_TERM* ai); +static BOOLEAN_T extract_debug(ErlNifEnv* env, + ERL_NIF_TERM map); static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); @@ -1535,6 +1540,19 @@ ErlNifFunc net_funcs[] = }; +static +BOOLEAN_T extract_debug(ErlNifEnv* env, + ERL_NIF_TERM map) +{ + /* + * We need to do this here since the "proper" atom has not been + * created when this function is called. + */ + ERL_NIF_TERM debug = MKA(env, "debug"); + + return esock_extract_bool_from_map(env, map, debug, NET_NIF_DEBUG_DEFAULT); +} + /* ======================================================================= * load_info - A map of misc info (e.g global debug) @@ -1544,7 +1562,7 @@ static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { // We should make it possible to use load_info to get default values - data.debug = TRUE; + data.debug = extract_debug(env, load_info); NDBG( ("NET", "on_load -> entry\r\n") ); diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 1f9234016c..d973633606 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -1499,14 +1499,10 @@ static char* send_msg(ErlNifEnv* env, ERL_NIF_TERM msg, ErlNifPid* pid); -static BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, - ERL_NIF_TERM map); -static BOOLEAN_T extract_iow_on_load(ErlNifEnv* env, - ERL_NIF_TERM map); -static BOOLEAN_T extract_bool(ErlNifEnv* env, - ERL_NIF_TERM map, - ERL_NIF_TERM ekey, - BOOLEAN_T def); +static BOOLEAN_T extract_debug(ErlNifEnv* env, + ERL_NIF_TERM map); +static BOOLEAN_T extract_iow(ErlNifEnv* env, + ERL_NIF_TERM map); static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); @@ -8423,37 +8419,29 @@ ErlNifFunc socket_funcs[] = static -BOOLEAN_T extract_debug_on_load(ErlNifEnv* env, - ERL_NIF_TERM map) +BOOLEAN_T extract_debug(ErlNifEnv* env, + ERL_NIF_TERM map) { - return extract_bool(env, map, esock_atom_debug, SOCKET_NIF_DEBUG_DEFAULT); -} - -static -BOOLEAN_T extract_iow_on_load(ErlNifEnv* env, - ERL_NIF_TERM map) -{ - return extract_bool(env, map, atom_iow, SOCKET_NIF_IOW_DEFAULT); + /* + * We need to do this here since the "proper" atom has not been + * created when this function is called. + */ + ERL_NIF_TERM debug = MKA(env, "debug"); + + return esock_extract_bool_from_map(env, map, debug, SOCKET_NIF_DEBUG_DEFAULT); } static -BOOLEAN_T extract_bool(ErlNifEnv* env, - ERL_NIF_TERM map, - ERL_NIF_TERM ekey, - BOOLEAN_T def) +BOOLEAN_T extract_iow(ErlNifEnv* env, + ERL_NIF_TERM map) { - ERL_NIF_TERM eval; - - if (!GET_MAP_VAL(env, map, ekey, &eval)) - return def; - - if (!IS_ATOM(env, eval)) - return def; - - if (COMPARE(eval, esock_atom_true) == 0) - return TRUE; - else - return FALSE; + /* + * We need to do this here since the "proper" atom has not been + * created when this function is called. + */ + ERL_NIF_TERM iow = MKA(env, "iow"); + + return esock_extract_bool_from_map(env, map, iow, SOCKET_NIF_IOW_DEFAULT); } @@ -8465,8 +8453,8 @@ BOOLEAN_T extract_bool(ErlNifEnv* env, static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { - data.dbg = extract_debug_on_load(env, load_info); - data.iow = extract_iow_on_load(env, load_info); + data.dbg = extract_debug(env, load_info); + data.iow = extract_iow(env, load_info); /* +++ Global Counters +++ */ data.cntMtx = MCREATE("socket[gcnt]"); diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 512c1d38e0..e6eb21adcf 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1098,6 +1098,33 @@ BOOLEAN_T esock_decode_string(ErlNifEnv* env, +/* *** esock_extract_bool_from_map *** + * + * Extract an boolean item from a map. + * + */ +extern +BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + BOOLEAN_T def) +{ + ERL_NIF_TERM val; + + if (!GET_MAP_VAL(env, map, key, &val)) + return def; + + if (!IS_ATOM(env, val)) + return def; + + if (COMPARE(val, esock_atom_true) == 0) + return TRUE; + else + return FALSE; +} + + + /* *** esock_decode_bool *** * * Decode a boolean value. diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index f1c122e281..686ce0bac6 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -133,6 +133,11 @@ BOOLEAN_T esock_decode_string(ErlNifEnv* env, char** stringP); extern +BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + BOOLEAN_T def); +extern BOOLEAN_T esock_decode_bool(ERL_NIF_TERM val); extern ERL_NIF_TERM esock_encode_bool(BOOLEAN_T val); -- cgit v1.2.3 From 5e0a36abaa984358f617541b102b4e4cbb112956 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 Jul 2018 14:42:34 +0200 Subject: [socket-nif] Add support for socket (level socket) option bindtodevice Added support for socket level socket option BINDTODEVICE. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 246 +++++++++++++++++++++++---------- 1 file changed, 171 insertions(+), 75 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index d973633606..141b42a3cd 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -346,21 +346,22 @@ typedef union { #define SOCKET_OPT_OTP_IOW 2 #define SOCKET_OPT_OTP_CTRL_PROC 3 -#define SOCKET_OPT_SOCK_ACCEPTCONN 1 -#define SOCKET_OPT_SOCK_BROADCAST 4 -#define SOCKET_OPT_SOCK_DEBUG 6 -#define SOCKET_OPT_SOCK_DOMAIN 7 -#define SOCKET_OPT_SOCK_DONTROUTE 8 -#define SOCKET_OPT_SOCK_KEEPALIVE 10 -#define SOCKET_OPT_SOCK_LINGER 11 -#define SOCKET_OPT_SOCK_OOBINLINE 13 -#define SOCKET_OPT_SOCK_PEEK_OFF 15 -#define SOCKET_OPT_SOCK_PRIORITY 17 -#define SOCKET_OPT_SOCK_PROTOCOL 18 -#define SOCKET_OPT_SOCK_RCVBUF 19 -#define SOCKET_OPT_SOCK_REUSEADDR 23 -#define SOCKET_OPT_SOCK_SNDBUF 27 -#define SOCKET_OPT_SOCK_TYPE 32 +#define SOCKET_OPT_SOCK_ACCEPTCONN 1 +#define SOCKET_OPT_SOCK_BINDTODEVICE 3 +#define SOCKET_OPT_SOCK_BROADCAST 4 +#define SOCKET_OPT_SOCK_DEBUG 6 +#define SOCKET_OPT_SOCK_DOMAIN 7 +#define SOCKET_OPT_SOCK_DONTROUTE 8 +#define SOCKET_OPT_SOCK_KEEPALIVE 10 +#define SOCKET_OPT_SOCK_LINGER 11 +#define SOCKET_OPT_SOCK_OOBINLINE 13 +#define SOCKET_OPT_SOCK_PEEK_OFF 15 +#define SOCKET_OPT_SOCK_PRIORITY 17 +#define SOCKET_OPT_SOCK_PROTOCOL 18 +#define SOCKET_OPT_SOCK_RCVBUF 19 +#define SOCKET_OPT_SOCK_REUSEADDR 23 +#define SOCKET_OPT_SOCK_SNDBUF 27 +#define SOCKET_OPT_SOCK_TYPE 32 #define SOCKET_OPT_IP_ADD_MEMBERSHIP 1 #define SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP 2 @@ -803,7 +804,14 @@ static ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, int eOpt, ERL_NIF_TERM eVal); + /* *** Handling set of socket options for level = socket *** */ + +#if defined(SO_BINDTODEVICE) +static ERL_NIF_TERM nsetopt_lvl_sock_bindtodevice(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SO_BROADCAST) static ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, SocketDescriptor* descP, @@ -1080,6 +1088,10 @@ static ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_BINDTODEVICE) +static ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_BROADCAST) static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, SocketDescriptor* descP); @@ -3902,6 +3914,12 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, "\r\n", eOpt) ); switch (eOpt) { +#if defined(SO_BINDTODEVICE) + case SOCKET_OPT_SOCK_BINDTODEVICE: + result = nsetopt_lvl_sock_bindtodevice(env, descP, eVal); + break; +#endif + #if defined(SO_BROADCAST) case SOCKET_OPT_SOCK_BROADCAST: result = nsetopt_lvl_sock_broadcast(env, descP, eVal); @@ -3977,6 +3995,19 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, } +#if defined(SO_BINDTODEVICE) +static +ERL_NIF_TERM nsetopt_lvl_sock_bindtodevice(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_str_opt(env, descP, + SOL_SOCKET, SO_BROADCAST, + IFNAMSIZ, eVal); +} +#endif + + #if defined(SO_BROADCAST) static ERL_NIF_TERM nsetopt_lvl_sock_broadcast(ErlNifEnv* env, @@ -5111,38 +5142,6 @@ ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, -/* nsetopt_str_opt - set an option that has an string value - */ -static -ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - int max, - ERL_NIF_TERM eVal) -{ - ERL_NIF_TERM result; - char* val = MALLOC(max); - - if (GET_STR(env, eVal, val, max) > 0) { - int optLen = strlen(val); - int res = socket_setopt(descP->sock, level, opt, &val, optLen); - - if (res != 0) - result = esock_make_error_errno(env, sock_errno()); - else - result = esock_atom_ok; - - } else { - result = esock_make_error(env, esock_atom_einval); - } - - FREE(val); - - return result; -} - - /* nsetopt_bool_opt - set an option that has an (integer) bool value */ static @@ -5198,6 +5197,38 @@ ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, } +/* nsetopt_str_opt - set an option that has an string value + */ +static +ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + int max, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + char* val = MALLOC(max); + + if (GET_STR(env, eVal, val, max) > 0) { + int optLen = strlen(val); + int res = socket_setopt(descP->sock, level, opt, &val, optLen); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + } else { + result = esock_make_error(env, esock_atom_einval); + } + + FREE(val); + + return result; +} + + static BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, int eLevel, @@ -5389,6 +5420,8 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, ERL_NIF_TERM eIsEncoded; BOOLEAN_T isEncoded, isOTP; + SGDBG( ("SOCKET", "nif_getopt -> entry with argc: %d\r\n", argc) ); + if ((argc != 4) || !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !GET_INT(env, argv[2], &eLevel) || @@ -5397,6 +5430,14 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, } eIsEncoded = argv[1]; + SSDBG( descP, + ("SOCKET", "nif_getopt -> args when sock = %d:" + "\r\n Socket: %T" + "\r\n eIsEncoded: %T" + "\r\n eLevel: %d" + "\r\n eOpt: %d" + "\r\n", descP->sock, argv[0], eIsEncoded, eLevel, eOpt) ); + isEncoded = esock_decode_bool(eIsEncoded); if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) @@ -5417,6 +5458,14 @@ ERL_NIF_TERM ngetopt(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "ngetopt -> entry with" + "\r\n isEncoded: %d" + "\r\n isOTP: %d" + "\r\n level: %d" + "\r\n eOpt: %d" + "\r\n", isEncoded, isOTP, level, eOpt) ); + if (isOTP) { /* These are not actual socket options, * but options for our implementation. @@ -5581,6 +5630,12 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "ngetopt_level -> entry with" + "\r\n level: %d" + "\r\n eOpt: %d" + "\r\n", level, eOpt) ); + switch (level) { case SOL_SOCKET: result = ngetopt_lvl_socket(env, descP, eOpt); @@ -5632,6 +5687,11 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_socket -> entry with" + "\r\n eOpt: %d" + "\r\n", eOpt) ); + switch (eOpt) { #if defined(SO_ACCEPTCONN) case SOCKET_OPT_SOCK_ACCEPTCONN: @@ -5639,6 +5699,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_BINDTODEVICE) + case SOCKET_OPT_SOCK_BINDTODEVICE: + result = ngetopt_lvl_sock_bindtodevice(env, descP); + break; +#endif + #if defined(SO_BROADCAST) case SOCKET_OPT_SOCK_BROADCAST: result = ngetopt_lvl_sock_broadcast(env, descP); @@ -5742,6 +5808,19 @@ ERL_NIF_TERM ngetopt_lvl_sock_acceptconn(ErlNifEnv* env, #endif +#if defined(SO_BINDTODEVICE) +static +ERL_NIF_TERM ngetopt_lvl_sock_bindtodevice(ErlNifEnv* env, + SocketDescriptor* descP) +{ + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_sock_bindtodevice -> entry with\r\n") ); + + return ngetopt_str_opt(env, descP, SOL_SOCKET, SO_BROADCAST, IFNAMSIZ+1); +} +#endif + + #if defined(SO_BROADCAST) static ERL_NIF_TERM ngetopt_lvl_sock_broadcast(ErlNifEnv* env, @@ -6628,18 +6707,17 @@ ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, -/* ngetopt_str_opt - get an string option +/* ngetopt_bool_opt - get an (integer) bool option */ static -ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt, - int max) +ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt) { ERL_NIF_TERM result; - char* val = MALLOC(max); - SOCKOPTLEN_T valSz = max; + int val; + SOCKOPTLEN_T valSz = sizeof(val); int res; res = sock_getopt(descP->sock, level, opt, &val, &valSz); @@ -6647,24 +6725,22 @@ ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, if (res != 0) { result = esock_make_error_errno(env, sock_errno()); } else { - ERL_NIF_TERM sval = MKSL(env, val, valSz); + ERL_NIF_TERM bval = ((val) ? atom_true : atom_false); - result = esock_make_ok2(env, sval); + result = esock_make_ok2(env, bval); } - FREE(val); - return result; } -/* nsetopt_bool_opt - get an (integer) bool option +/* ngetopt_int_opt - get an integer option */ static -ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, - SocketDescriptor* descP, - int level, - int opt) +ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt) { ERL_NIF_TERM result; int val; @@ -6676,42 +6752,62 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, if (res != 0) { result = esock_make_error_errno(env, sock_errno()); } else { - ERL_NIF_TERM bval = ((val) ? atom_true : atom_false); - - result = esock_make_ok2(env, bval); + result = esock_make_ok2(env, MKI(env, val)); } return result; } -/* nsetopt_int_opt - get an integer option + +/* ngetopt_str_opt - get an string option + * + * We provide the max size of the string. This is the + * size of the buffer we allocate for the value. + * The actual size of the (read) value will be communicated + * in the optSz variable. */ static -ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, +ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, SocketDescriptor* descP, int level, - int opt) + int opt, + int max) { ERL_NIF_TERM result; - int val; - SOCKOPTLEN_T valSz = sizeof(val); + char* val = MALLOC(max); + SOCKOPTLEN_T valSz = max; int res; - res = sock_getopt(descP->sock, level, opt, &val, &valSz); + SSDBG( descP, + ("SOCKET", "ngetopt_str_opt -> entry with" + "\r\n level: %d" + "\r\n opt: %d" + "\r\n max: %d" + "\r\n", level, opt, max) ); + + res = sock_getopt(descP->sock, level, opt, val, &valSz); if (res != 0) { result = esock_make_error_errno(env, sock_errno()); } else { - result = esock_make_ok2(env, MKI(env, val)); + ERL_NIF_TERM sval = MKSL(env, val, valSz); + + result = esock_make_ok2(env, sval); } + SSDBG( descP, + ("SOCKET", "ngetopt_str_opt -> done when" + "\r\n result: %T" + "\r\n", result) ); + + FREE(val); + return result; } - /* ---------------------------------------------------------------------- * nif_sockname - get socket name * -- cgit v1.2.3 From 8de18e84deaed4c9e6e7242ae2550fc6618dc44d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 Jul 2018 18:38:53 +0200 Subject: [socket-nif] Add support for socket (level socket) option reuseport Added support for socket level socket option REUSEPORT. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 141b42a3cd..713153d7c5 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -360,6 +360,7 @@ typedef union { #define SOCKET_OPT_SOCK_PROTOCOL 18 #define SOCKET_OPT_SOCK_RCVBUF 19 #define SOCKET_OPT_SOCK_REUSEADDR 23 +#define SOCKET_OPT_SOCK_REUSEPORT 24 #define SOCKET_OPT_SOCK_SNDBUF 27 #define SOCKET_OPT_SOCK_TYPE 32 @@ -862,6 +863,11 @@ static ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SO_REUSEPORT) +static ERL_NIF_TERM nsetopt_lvl_sock_reuseport(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SO_SNDBUF) static ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, SocketDescriptor* descP, @@ -1140,6 +1146,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_REUSEPORT) +static ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_SNDBUF) static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, SocketDescriptor* descP); @@ -3980,6 +3990,12 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_REUSEPORT) + case SOCKET_OPT_SOCK_REUSEPORT: + result = nsetopt_lvl_sock_reuseport(env, descP, eVal); + break; +#endif + #if defined(SO_SNDBUF) case SOCKET_OPT_SOCK_SNDBUF: result = nsetopt_lvl_sock_sndbuf(env, descP, eVal); @@ -4133,6 +4149,17 @@ ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, #endif +#if defined(SO_REUSEPORT) +static +ERL_NIF_TERM nsetopt_lvl_sock_reuseport(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEPORT, eVal); +} +#endif + + #if defined(SO_SNDBUF) static ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, @@ -5777,6 +5804,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_REUSEPORT) + case SOCKET_OPT_SOCK_REUSEPORT: + result = ngetopt_lvl_sock_reuseport(env, descP); + break; +#endif + #if defined(SO_SNDBUF) case SOCKET_OPT_SOCK_SNDBUF: result = ngetopt_lvl_sock_sndbuf(env, descP); @@ -6035,6 +6068,16 @@ ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, #endif +#if defined(SO_REUSEPORT) +static +ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEPORT); +} +#endif + + #if defined(SO_SNDBUF) static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, -- cgit v1.2.3 From 017565203f40860d24b80a54136a160aee460dbe Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 16 Jul 2018 18:21:48 +0200 Subject: [socket-nif] Add support for multiple acceptor processes Its now possible to have multiple (simultaneous) acceptor processes for the same listening socket. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 2 + erts/emulator/nifs/common/socket_nif.c | 310 +++++++++++++++++++++++++++++++-- 2 files changed, 302 insertions(+), 10 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index aa10260134..67e4baba27 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -92,6 +92,7 @@ typedef unsigned int BOOLEAN_T; #define BOOL2ATOM(__B__) ((__B__) ? esock_atom_true : esock_atom_false) +#define B2S(__B__) ((__B__) ? "true" : "false") /* Misc error strings */ #define ESOCK_STR_EAFNOSUPPORT "eafnosupport" @@ -141,6 +142,7 @@ extern ERL_NIF_TERM esock_atom_eagain; extern ERL_NIF_TERM esock_atom_einval; + /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * Various wrapper macros for enif functions */ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 713153d7c5..224dcc9ff6 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -572,7 +572,7 @@ typedef struct { /* +++ Accept stuff +++ */ ErlNifMutex* accMtx; SocketRequestor currentAcceptor; - SocketRequestor* currentAcceptorP; // NULL or points to currentReader + SocketRequestor* currentAcceptorP; // NULL or points to currentAcceptor SocketRequestQueue acceptorsQ; /* +++ Config & Misc stuff +++ */ @@ -1488,6 +1488,29 @@ static void inc_socket(int domain, int type, int protocol); static void dec_socket(int domain, int type, int protocol); +static BOOLEAN_T acceptor_search4pid(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid); +static ERL_NIF_TERM acceptor_push(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid pid, + ERL_NIF_TERM ref); +static BOOLEAN_T acceptor_pop(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid, + ErlNifMonitor* mon, + ERL_NIF_TERM* ref); + +static BOOLEAN_T qsearch4pid(ErlNifEnv* env, + SocketRequestQueue* q, + ErlNifPid* pid); +static void qpush(SocketRequestQueue* q, + SocketRequestQueueElement* e); +static SocketRequestQueueElement* qpop(SocketRequestQueue* q); +static BOOLEAN_T qunqueue(ErlNifEnv* env, + SocketRequestQueue* q, + const ErlNifPid* pid); + /* #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) static size_t my_strnlen(const char *s, size_t maxlen); @@ -2663,6 +2686,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, HANDLE accEvent; ErlNifPid caller; int save_errno; + ERL_NIF_TERM result; SSDBG( descP, ("SOCKET", "naccept_accepting -> get caller\r\n") ); @@ -2682,11 +2706,18 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, * for the select message). */ + SSDBG( descP, ("SOCKET", "naccept_accepting -> not (active) acceptor\r\n") ); + + if (!acceptor_search4pid(env, descP, &caller)) + result = acceptor_push(env, descP, caller, ref); + else + result = esock_make_error(env, esock_atom_eagain); + SSDBG( descP, ("SOCKET", - "naccept_accepting -> not current acceptor: busy\r\n") ); + "naccept_accepting -> queue (push) result: %T\r\n", result) ); - return esock_make_error(env, atom_exbusy); + return result; } n = sizeof(descP->remote); @@ -2777,8 +2808,30 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, * And if so, pop it and copy the (waiting) acceptor, and then * make a new select with that info). */ - descP->state = SOCKET_STATE_LISTENING; + if (acceptor_pop(env, descP, + &descP->currentAcceptor.pid, + &descP->currentAcceptor.mon, + &descP->currentAcceptor.ref)) { + + /* There was another one */ + + SSDBG( descP, ("SOCKET", "naccept_accepting -> new (active) acceptor: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentAcceptor.pid, + descP->currentAcceptor.ref) ); + + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, &descP->currentAcceptor.pid, descP->currentAcceptor.ref); + } else { + descP->currentAcceptorP = NULL; + descP->state = SOCKET_STATE_LISTENING; + } + return esock_make_ok2(env, accRef); } } @@ -8237,6 +8290,186 @@ char* send_msg(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * R e q u e s t Q u e u e F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* *** acceptor search for pid *** + * + * Search for a pid in the acceptor queue. + */ +static +BOOLEAN_T acceptor_search4pid(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid) +{ + return qsearch4pid(env, &descP->acceptorsQ, pid); +} + + +/* *** acceptor push *** + * + * Push an acceptor onto the acceptor queue. + * This happens when we already have atleast one current acceptor. + */ +static +ERL_NIF_TERM acceptor_push(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid pid, + ERL_NIF_TERM ref) +{ + SocketRequestQueueElement* e = MALLOC(sizeof(SocketRequestQueueElement)); + SocketRequestor* reqP = &e->data; + + reqP->pid = pid; + reqP->ref = ref; + + if (MONP(env, descP, &pid, &reqP->mon) > 0) { + FREE(reqP); + return esock_make_error(env, atom_exmon); + } + + qpush(&descP->acceptorsQ, e); + + // THIS IS OK => MAKES THE CALLER WAIT FOR ITS TURN + return esock_make_error(env, esock_atom_eagain); +} + + +/* *** acceptor pop *** + * + * Pop an acceptor from the acceptor queue. + */ +static +BOOLEAN_T acceptor_pop(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid, + ErlNifMonitor* mon, + ERL_NIF_TERM* ref) +{ + SocketRequestQueueElement* e = qpop(&descP->acceptorsQ); + + if (e != NULL) { + *pid = e->data.pid; + *mon = e->data.mon; + *ref = e->data.ref; + FREE(e); + return TRUE; + } else { + /* (acceptors) Queue was empty */ + // *pid = NULL; we have no null value for pids + // *mon = NULL; we have no null value for monitors + *ref = esock_atom_undefined; // Just in case + return FALSE; + } + +} + + +static +BOOLEAN_T qsearch4pid(ErlNifEnv* env, + SocketRequestQueue* q, + ErlNifPid* pid) +{ + SocketRequestQueueElement* tmp = q->first; + + while (tmp != NULL) { + if (compare_pids(env, &tmp->data.pid, pid)) + return TRUE; + else + tmp = tmp->nextP; + } + + return FALSE; +} + + +static +void qpush(SocketRequestQueue* q, + SocketRequestQueueElement* e) +{ + if (q->first != NULL) { + q->last->nextP = e; + q->last = e; + e->nextP = NULL; + } else { + q->first = e; + q->last = e; + e->nextP = NULL; + } +} + + +static +SocketRequestQueueElement* qpop(SocketRequestQueue* q) +{ + SocketRequestQueueElement* e = q->first; + + if (e != NULL) { + /* Atleast one element in the queue */ + if (e == q->last) { + /* Only one element in the queue */ + q->first = q->last = NULL; + } else { + /* More than one element in the queue */ + q->first = e->nextP; + } + } + + return e; +} + + + +static +BOOLEAN_T qunqueue(ErlNifEnv* env, + SocketRequestQueue* q, + const ErlNifPid* pid) +{ + SocketRequestQueueElement* e = q->first; + SocketRequestQueueElement* p = NULL; + + /* Check if it was one of the waiting acceptor processes */ + while (e != NULL) { + if (compare_pids(env, &e->data.pid, pid)) { + + /* We have a match */ + + if (p != NULL) { + /* Not the first, but could be the last */ + if (q->last == e) { + q->last = p; + p->nextP = NULL; + } else { + p->nextP = e->nextP; + } + + } else { + /* The first and could also be the last */ + if (q->last == e) { + q->last = NULL; + q->first = NULL; + } else { + q->first = e->nextP; + } + } + + FREE(e); + + return TRUE; + } + + /* Try next */ + p = e; + e = e->nextP; + } + + return FALSE; +} + + + /* ---------------------------------------------------------------------- * C o u n t e r F u n c t i o n s * ---------------------------------------------------------------------- @@ -8502,13 +8735,70 @@ void socket_down(ErlNifEnv* env, { 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) ); + SSDBG( descP, ("SOCKET", "socket_down -> entry with" + "\r\n sock: %d" + "\r\n pid: %T" + "\r\n", descP->sock, *pid) ); + + /* Eventually we should go through the other queues also, + * the process can be one of them... + */ + + /* Check first if its the current acceptor, and if not check the queue */ + if (compare_pids(env, &descP->currentAcceptor.pid, pid)) { + + SSDBG( descP, ("SOCKET", + "socket_down -> current acceptor - try pop the queue\r\n") ); + + if (acceptor_pop(env, descP, + &descP->currentAcceptor.pid, + &descP->currentAcceptor.mon, + &descP->currentAcceptor.ref)) { + int res; + + /* There was another one, so we will still be in accepting state */ + + SSDBG( descP, ("SOCKET", "socket_down -> new (active) acceptor: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentAcceptor.pid, + descP->currentAcceptor.ref) ); + + if ((res = enif_select(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, + &descP->currentAcceptor.pid, + descP->currentAcceptor.ref) < 0)) { + + esock_warning_msg("Failed select (%d) for new acceptor " + "after current (%T) died\r\n", + res, *pid); + + } + + } else { + + SSDBG( descP, ("SOCKET", "socket_down -> no active acceptor\r\n") ); + + descP->currentAcceptorP = NULL; + descP->state = SOCKET_STATE_LISTENING; + } + + } else { + + /* Maybe unqueue one of the waiting acceptors */ + + SSDBG( descP, ("SOCKET", + "socket_down -> " + "not current acceptor - maybe a waiting acceptor\r\n") ); + + qunqueue(env, &descP->acceptorsQ, pid); + } + SSDBG( descP, ("SOCKET", "socket_down -> done\r\n") ); + } -- cgit v1.2.3 From 8d759e6f00b1cf834fe21654de3f53df706e8c0f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 18 Jul 2018 10:23:45 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option v6only Added support for the IPv6 option V6ONLY. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 112 +++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 224dcc9ff6..15d43da9da 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -388,6 +388,7 @@ typedef union { #define SOCKET_OPT_IPV6_ADD_MEMBERSHIP 2 #define SOCKET_OPT_IPV6_DROP_MEMBERSHIP 6 #define SOCKET_OPT_IPV6_HOPLIMIT 12 +#define SOCKET_OPT_IPV6_V6ONLY 33 #define SOCKET_OPT_TCP_CONGESTION 1 #define SOCKET_OPT_TCP_CORK 2 @@ -1007,6 +1008,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_V6ONLY) +static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP) static ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, @@ -1221,6 +1227,11 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_V6ONLY) +static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, + SocketDescriptor* descP); +#endif + #endif // defined(SOL_IPV6) static ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, SocketDescriptor* descP, @@ -4235,6 +4246,11 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip -> entry with" + "\r\n opt: %d" + "\r\n", eOpt) ); + switch (eOpt) { #if defined(IP_ADD_MEMBERSHIP) case SOCKET_OPT_IP_ADD_MEMBERSHIP: @@ -4920,6 +4936,11 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6 -> entry with" + "\r\n opt: %d" + "\r\n", eOpt) ); + switch (eOpt) { #if defined(IPV6_ADD_MEMBERSHIP) case SOCKET_OPT_IPV6_ADD_MEMBERSHIP: @@ -4939,6 +4960,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_V6ONLY) + case SOCKET_OPT_IPV6_V6ONLY: + result = nsetopt_lvl_ipv6_v6only(env, descP, eVal); + break; +#endif + default: result = esock_make_error(env, esock_atom_einval); break; @@ -4983,6 +5010,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, #endif +#if defined(IPV6_V6ONLY) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_V6ONLY, eVal); +} +#endif + + #if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP) static ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, @@ -5046,6 +5084,11 @@ ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_tcp -> entry with" + "\r\n opt: %d" + "\r\n", eOpt) ); + switch (eOpt) { #if defined(TCP_CONGESTION) case SOCKET_OPT_TCP_CONGESTION: @@ -5126,6 +5169,11 @@ ERL_NIF_TERM nsetopt_lvl_udp(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_udp -> entry with" + "\r\n opt: %d" + "\r\n", eOpt) ); + switch (eOpt) { #if defined(UDP_CORK) case SOCKET_OPT_UDP_CORK: @@ -5168,6 +5216,11 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp -> entry with" + "\r\n opt: %d" + "\r\n", eOpt) ); + switch (eOpt) { #if defined(SCTP_AUTOCLOSE) case SOCKET_OPT_SCTP_AUTOCLOSE: @@ -5557,6 +5610,11 @@ ERL_NIF_TERM ngetopt(ErlNifEnv* env, result = ngetopt_level(env, descP, level, eOpt); } + SSDBG( descP, + ("SOCKET", "ngetopt -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -5629,6 +5687,12 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, uint16_t valueType; SOCKOPTLEN_T valueSz; + SSDBG( descP, + ("SOCKET", "ngetopt_native -> entry with" + "\r\n level: %d" + "\r\n eOpt: %d" + "\r\n", level, eOpt) ); + /* * We should really make it possible to specify common specific types, * such as integer or boolean (instead of the size)... @@ -5654,6 +5718,11 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, result = esock_make_error(env, esock_atom_einval); } + SSDBG( descP, + ("SOCKET", "ngetopt_native -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -5668,6 +5737,13 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, ERL_NIF_TERM result = enif_make_badarg(env); int res; + SSDBG( descP, + ("SOCKET", "ngetopt_native_unspec -> entry with" + "\r\n level: %d" + "\r\n opt: %d" + "\r\n valueSz: %d" + "\r\n", level, opt, valueSz) ); + if (valueSz == 0) { res = sock_getopt(descP->sock, level, opt, NULL, NULL); if (res != 0) @@ -5695,6 +5771,11 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, } } + SSDBG( descP, + ("SOCKET", "ngetopt_native_unspec -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -5754,6 +5835,11 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, break; } + SSDBG( descP, + ("SOCKET", "ngetopt_level -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -6599,6 +6685,11 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_ipv6 -> entry with" + "\r\n eOpt: %d" + "\r\n", eOpt) ); + switch (eOpt) { #if defined(IPV6_HOPLIMIT) case SOCKET_OPT_IPV6_HOPLIMIT: @@ -6606,11 +6697,22 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_V6ONLY) + case SOCKET_OPT_IPV6_V6ONLY: + result = ngetopt_lvl_ipv6_v6only(env, descP); + break; +#endif + default: result = esock_make_error(env, esock_atom_einval); break; } + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_ipv6 -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -6625,6 +6727,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, #endif +#if defined(IPV6_V6ONLY) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_V6ONLY); +} +#endif + + #endif // defined(SOL_IPV6) -- cgit v1.2.3 From ebd626e7b4259bdfb4ddb34ce2d298d0feb0a1c8 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 18 Jul 2018 11:33:50 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option mtu Added support for the VPv6 socket option MTU. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 15d43da9da..0222d58e6d 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -388,6 +388,7 @@ typedef union { #define SOCKET_OPT_IPV6_ADD_MEMBERSHIP 2 #define SOCKET_OPT_IPV6_DROP_MEMBERSHIP 6 #define SOCKET_OPT_IPV6_HOPLIMIT 12 +#define SOCKET_OPT_IPV6_MTU 17 #define SOCKET_OPT_IPV6_V6ONLY 33 #define SOCKET_OPT_TCP_CONGESTION 1 @@ -1008,6 +1009,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_MTU) +static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP, @@ -1227,12 +1233,17 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_MTU) +static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP); #endif #endif // defined(SOL_IPV6) + static ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); @@ -4960,6 +4971,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MTU) + case SOCKET_OPT_IPV6_MTU: + result = nsetopt_lvl_ipv6_mtu(env, descP, eVal); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = nsetopt_lvl_ipv6_v6only(env, descP, eVal); @@ -5010,6 +5027,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, #endif +#if defined(IPV6_MTU) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_MTU, eVal); +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, @@ -6697,6 +6725,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MTU) + case SOCKET_OPT_IPV6_MTU: + result = ngetopt_lvl_ipv6_mtu(env, descP); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = ngetopt_lvl_ipv6_v6only(env, descP); @@ -6727,6 +6761,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, #endif +#if defined(IPV6_MTU) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_MTU); +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, -- cgit v1.2.3 From 1a3aca0a849af0bae994c9cf89de0dcfe7b310c2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 18 Jul 2018 12:05:00 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option mtu_discover Added support for the VPv6 socket option MTU_DISCOVER. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 190 +++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 0222d58e6d..d2f7e21ad0 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -389,6 +389,7 @@ typedef union { #define SOCKET_OPT_IPV6_DROP_MEMBERSHIP 6 #define SOCKET_OPT_IPV6_HOPLIMIT 12 #define SOCKET_OPT_IPV6_MTU 17 +#define SOCKET_OPT_IPV6_MTU_DISCOVER 18 #define SOCKET_OPT_IPV6_V6ONLY 33 #define SOCKET_OPT_TCP_CONGESTION 1 @@ -1014,6 +1015,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_MTU_DISCOVER) +static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP, @@ -1237,6 +1243,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_MTU_DISCOVER) +static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP); @@ -1451,6 +1461,16 @@ static void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal); #endif +#if defined(IPV6_MTU_DISCOVER) +static char* decode_ipv6_pmtudisc(ErlNifEnv* env, + ERL_NIF_TERM eVal, + int* val); +#endif +#if defined(IPV6_MTU_DISCOVER) +static void encode_ipv6_pmtudisc(ErlNifEnv* env, + int val, + ERL_NIF_TERM* eVal); +#endif /* static BOOLEAN_T decode_bool(ErlNifEnv* env, @@ -4977,6 +4997,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MTU_DISCOVER) + case SOCKET_OPT_IPV6_MTU_DISCOVER: + result = nsetopt_lvl_ipv6_mtu_discover(env, descP, eVal); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = nsetopt_lvl_ipv6_v6only(env, descP, eVal); @@ -5038,6 +5064,42 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, #endif +/* nsetopt_lvl_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option + * + * The value is an atom of the type ipv6_pmtudisc(). + */ +#if defined(IPV6_MTU_DISCOVER) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + int val; + char* xres; + int res; + + if ((xres = decode_ipv6_pmtudisc(env, eVal, &val)) != NULL) { + + result = esock_make_error_str(env, xres); + + } else { + + res = socket_setopt(descP->sock, SOL_IPV6, IPV6_MTU_DISCOVER, + &val, sizeof(val)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + } + + return result; +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, @@ -6731,6 +6793,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MTU_DISCOVER) + case SOCKET_OPT_IPV6_MTU_DISCOVER: + result = ngetopt_lvl_ipv6_mtu_discover(env, descP); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = ngetopt_lvl_ipv6_v6only(env, descP); @@ -6771,6 +6839,35 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, #endif +/* ngetopt_lvl_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option + */ +#if defined(IPV6_MTU_DISCOVER) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + ERL_NIF_TERM eMtuDisc; + int mtuDisc; + SOCKOPTLEN_T mtuDiscSz = sizeof(mtuDisc); + int res; + + res = sock_getopt(descP->sock, SOL_IPV6, IPV6_MTU_DISCOVER, + &mtuDisc, &mtuDiscSz); + + if (res != 0) { + result = esock_make_error_errno(env, sock_errno()); + } else { + encode_ipv6_pmtudisc(env, mtuDisc, &eMtuDisc); + result = esock_make_ok2(env, eMtuDisc); + } + + return result; + +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, @@ -7723,6 +7820,57 @@ char* decode_ip_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) +/* +++ decode the ipv6 socket option MTU_DISCOVER +++ + * The (ip) option can be provide in two ways: + * + * atom() | integer() + * + * When its an atom it can have the values: + * + * want | dont | do | probe + * + */ +#if defined(IPV6_MTU_DISCOVER) +static +char* decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) +{ + char* res = NULL; + + if (IS_ATOM(env, eVal)) { + + if (COMPARE(eVal, atom_want) == 0) { + *val = IPV6_PMTUDISC_WANT; + } else if (COMPARE(eVal, atom_dont) == 0) { + *val = IPV6_PMTUDISC_DONT; + } else if (COMPARE(eVal, atom_do) == 0) { + *val = IPV6_PMTUDISC_DO; + } else if (COMPARE(eVal, atom_probe) == 0) { + *val = IPV6_PMTUDISC_PROBE; + } else { + *val = -1; + res = ESOCK_STR_EINVAL; + } + + } else if (IS_NUM(env, eVal)) { + + if (!GET_INT(env, eVal, val)) { + *val = -1; + res = ESOCK_STR_EINVAL; + } + + } else { + + *val = -1; + res = ESOCK_STR_EINVAL; + + } + + return res; +} +#endif + + + /* +++ encode the ip socket option MTU_DISCOVER +++ * The (ip) option can be provide in two ways: * @@ -7765,6 +7913,48 @@ void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) +/* +++ encode the ipv6 socket option MTU_DISCOVER +++ + * The (ipv6) option can be provide in two ways: + * + * atom() | integer() + * + * If its one of the "known" values, it will be an atom: + * + * want | dont | do | probe + * + */ +#if defined(IPV6_MTU_DISCOVER) +static +void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) +{ + switch (val) { + case IPV6_PMTUDISC_WANT: + *eVal = atom_want; + break; + + case IPV6_PMTUDISC_DONT: + *eVal = atom_dont; + break; + + case IPV6_PMTUDISC_DO: + *eVal = atom_do; + break; + + case IPV6_PMTUDISC_PROBE: + *eVal = atom_probe; + break; + + default: + *eVal = MKI(env, val); + break; + } + + return; +} +#endif + + + /* +++ decocde the native getopt option +++ * The option is in this case provide in the form of a two tuple: * -- cgit v1.2.3 From d28129b7098bce154264937862fcdafb21541433 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 14:00:42 +0200 Subject: [socket-nif] Add support for socket (level sctp) option events Added support for the SCTP option EVENTS. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 578 +++++++++++++++++++++++++-------- 1 file changed, 451 insertions(+), 127 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index d2f7e21ad0..7271db2143 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -164,6 +164,149 @@ #define HAVE_UDP +/* SCTP support -- currently for UNIX platforms only: */ +#undef HAVE_SCTP +#if defined(HAVE_SCTP_H) + +#include + +/* SCTP Socket API Draft from version 11 on specifies that netinet/sctp.h must + explicitly define HAVE_SCTP in case when SCTP is supported, but Solaris 10 + still apparently uses Draft 10, and does not define that symbol, so we have + to define it explicitly: +*/ +#ifndef HAVE_SCTP +# define HAVE_SCTP +#endif + +/* These changed in draft 11, so SOLARIS10 uses the old MSG_* */ +#if ! HAVE_DECL_SCTP_UNORDERED +# define SCTP_UNORDERED MSG_UNORDERED +#endif +#if ! HAVE_DECL_SCTP_ADDR_OVER +# define SCTP_ADDR_OVER MSG_ADDR_OVER +#endif +#if ! HAVE_DECL_SCTP_ABORT +# define SCTP_ABORT MSG_ABORT +#endif +#if ! HAVE_DECL_SCTP_EOF +# define SCTP_EOF MSG_EOF +#endif + +/* More Solaris 10 fixes: */ +#if ! HAVE_DECL_SCTP_CLOSED && HAVE_DECL_SCTPS_IDLE +# define SCTP_CLOSED SCTPS_IDLE +# undef HAVE_DECL_SCTP_CLOSED +# define HAVE_DECL_SCTP_CLOSED 1 +#endif +#if ! HAVE_DECL_SCTP_BOUND && HAVE_DECL_SCTPS_BOUND +# define SCTP_BOUND SCTPS_BOUND +# undef HAVE_DECL_SCTP_BOUND +# define HAVE_DECL_SCTP_BOUND 1 +#endif +#if ! HAVE_DECL_SCTP_LISTEN && HAVE_DECL_SCTPS_LISTEN +# define SCTP_LISTEN SCTPS_LISTEN +# undef HAVE_DECL_SCTP_LISTEN +# define HAVE_DECL_SCTP_LISTEN 1 +#endif +#if ! HAVE_DECL_SCTP_COOKIE_WAIT && HAVE_DECL_SCTPS_COOKIE_WAIT +# define SCTP_COOKIE_WAIT SCTPS_COOKIE_WAIT +# undef HAVE_DECL_SCTP_COOKIE_WAIT +# define HAVE_DECL_SCTP_COOKIE_WAIT 1 +#endif +#if ! HAVE_DECL_SCTP_COOKIE_ECHOED && HAVE_DECL_SCTPS_COOKIE_ECHOED +# define SCTP_COOKIE_ECHOED SCTPS_COOKIE_ECHOED +# undef HAVE_DECL_SCTP_COOKIE_ECHOED +# define HAVE_DECL_SCTP_COOKIE_ECHOED 1 +#endif +#if ! HAVE_DECL_SCTP_ESTABLISHED && HAVE_DECL_SCTPS_ESTABLISHED +# define SCTP_ESTABLISHED SCTPS_ESTABLISHED +# undef HAVE_DECL_SCTP_ESTABLISHED +# define HAVE_DECL_SCTP_ESTABLISHED 1 +#endif +#if ! HAVE_DECL_SCTP_SHUTDOWN_PENDING && HAVE_DECL_SCTPS_SHUTDOWN_PENDING +# define SCTP_SHUTDOWN_PENDING SCTPS_SHUTDOWN_PENDING +# undef HAVE_DECL_SCTP_SHUTDOWN_PENDING +# define HAVE_DECL_SCTP_SHUTDOWN_PENDING 1 +#endif +#if ! HAVE_DECL_SCTP_SHUTDOWN_SENT && HAVE_DECL_SCTPS_SHUTDOWN_SENT +# define SCTP_SHUTDOWN_SENT SCTPS_SHUTDOWN_SENT +# undef HAVE_DECL_SCTP_SHUTDOWN_SENT +# define HAVE_DECL_SCTP_SHUTDOWN_SENT 1 +#endif +#if ! HAVE_DECL_SCTP_SHUTDOWN_RECEIVED && HAVE_DECL_SCTPS_SHUTDOWN_RECEIVED +# define SCTP_SHUTDOWN_RECEIVED SCTPS_SHUTDOWN_RECEIVED +# undef HAVE_DECL_SCTP_SHUTDOWN_RECEIVED +# define HAVE_DECL_SCTP_SHUTDOWN_RECEIVED 1 +#endif +#if ! HAVE_DECL_SCTP_SHUTDOWN_ACK_SENT && HAVE_DECL_SCTPS_SHUTDOWN_ACK_SENT +# define SCTP_SHUTDOWN_ACK_SENT SCTPS_SHUTDOWN_ACK_SENT +# undef HAVE_DECL_SCTP_SHUTDOWN_ACK_SENT +# define HAVE_DECL_SCTP_SHUTDOWN_ACK_SENT 1 +#endif +/* New spelling in lksctp 2.6.22 or maybe even earlier: + * adaption -> adaptation + */ +#if !defined(SCTP_ADAPTATION_LAYER) && defined (SCTP_ADAPTION_LAYER) +# define SCTP_ADAPTATION_LAYER SCTP_ADAPTION_LAYER +# define SCTP_ADAPTATION_INDICATION SCTP_ADAPTION_INDICATION +# define sctp_adaptation_event sctp_adaption_event +# define sctp_setadaptation sctp_setadaption +# define sn_adaptation_event sn_adaption_event +# define sai_adaptation_ind sai_adaption_ind +# define ssb_adaptation_ind ssb_adaption_ind +# define sctp_adaptation_layer_event sctp_adaption_layer_event +#endif + +/* + * We *may* need this stuff later when we *fully* implement support for SCTP + * + +#if defined(__GNUC__) && defined(HAVE_SCTP_BINDX) +static typeof(sctp_bindx) *p_sctp_bindx = NULL; +#else +static int (*p_sctp_bindx) + (int sd, struct sockaddr *addrs, int addrcnt, int flags) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_PEELOFF) +static typeof(sctp_peeloff) *p_sctp_peeloff = NULL; +#else +static int (*p_sctp_peeloff) + (int sd, sctp_assoc_t assoc_id) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_GETLADDRS) +static typeof(sctp_getladdrs) *p_sctp_getladdrs = NULL; +#else +static int (*p_sctp_getladdrs) + (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_FREELADDRS) +static typeof(sctp_freeladdrs) *p_sctp_freeladdrs = NULL; +#else +static void (*p_sctp_freeladdrs)(struct sockaddr *addrs) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_GETPADDRS) +static typeof(sctp_getpaddrs) *p_sctp_getpaddrs = NULL; +#else +static int (*p_sctp_getpaddrs) + (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_FREEPADDRS) +static typeof(sctp_freepaddrs) *p_sctp_freepaddrs = NULL; +#else +static void (*p_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; +#endif + +*/ + +#endif /* #if defined(HAVE_SCTP_H) */ + + #ifndef WANT_NONBLOCKING #define WANT_NONBLOCKING #endif @@ -394,12 +537,13 @@ typedef union { #define SOCKET_OPT_TCP_CONGESTION 1 #define SOCKET_OPT_TCP_CORK 2 -#define SOCKET_OPT_TCP_MAXSEG 3 -#define SOCKET_OPT_TCP_NODELAY 4 +#define SOCKET_OPT_TCP_MAXSEG 7 +#define SOCKET_OPT_TCP_NODELAY 9 #define SOCKET_OPT_UDP_CORK 1 #define SOCKET_OPT_SCTP_AUTOCLOSE 8 +#define SOCKET_OPT_SCTP_EVENTS 14 #define SOCKET_OPT_SCTP_NODELAY 23 @@ -1072,6 +1216,11 @@ static ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SCTP_EVENTS) +static ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SCTP_NODELAY) static ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, SocketDescriptor* descP, @@ -1618,38 +1767,48 @@ static const struct in6_addr in6addr_loopback = /* *** String constants *** */ -// static char str_any[] = "any"; -static char str_close[] = "close"; -static char str_closed[] = "closed"; -static char str_closing[] = "closing"; -static char str_do[] = "do"; -static char str_dont[] = "dont"; -static char str_false[] = "false"; -static char str_global_counters[] = "global_counters"; -static char str_in4_sockaddr[] = "in4_sockaddr"; -static char str_in6_sockaddr[] = "in6_sockaddr"; -static char str_iow[] = "iow"; -static char str_interface[] = "interface"; -// static char str_loopback[] = "loopback"; -static char str_multiaddr[] = "multiaddr"; -static char str_nif_abort[] = "nif_abort"; -static char str_num_dlocal[] = "num_domain_local"; -static char str_num_dinet[] = "num_domain_inet"; -static char str_num_dinet6[] = "num_domain_inet6"; -static char str_num_pip[] = "num_proto_ip"; -static char str_num_psctp[] = "num_proto_sctp"; -static char str_num_ptcp[] = "num_proto_tcp"; -static char str_num_pudp[] = "num_proto_udp"; -static char str_num_sockets[] = "num_sockets"; -static char str_num_tdgrams[] = "num_type_dgram"; -static char str_num_tseqpkgs[] = "num_type_seqpacket"; -static char str_num_tstreams[] = "num_type_stream"; -static char str_probe[] = "probe"; -static char str_select[] = "select"; -static char str_sourceaddr[] = "sourceaddr"; -static char str_timeout[] = "timeout"; -static char str_true[] = "true"; -static char str_want[] = "want"; +static char str_adaptation_layer[] = "adaptation_layer"; +static char str_address[] = "address"; +static char str_association[] = "association"; +static char str_authentication[] = "authentication"; +// static char str_any[] = "any"; +static char str_close[] = "close"; +static char str_closed[] = "closed"; +static char str_closing[] = "closing"; +static char str_data_in[] = "data_in"; +static char str_do[] = "do"; +static char str_dont[] = "dont"; +static char str_false[] = "false"; +static char str_global_counters[] = "global_counters"; +static char str_in4_sockaddr[] = "in4_sockaddr"; +static char str_in6_sockaddr[] = "in6_sockaddr"; +static char str_iow[] = "iow"; +static char str_interface[] = "interface"; +// static char str_loopback[] = "loopback"; +static char str_multiaddr[] = "multiaddr"; +static char str_nif_abort[] = "nif_abort"; +static char str_num_dlocal[] = "num_domain_local"; +static char str_num_dinet[] = "num_domain_inet"; +static char str_num_dinet6[] = "num_domain_inet6"; +static char str_num_pip[] = "num_proto_ip"; +static char str_num_psctp[] = "num_proto_sctp"; +static char str_num_ptcp[] = "num_proto_tcp"; +static char str_num_pudp[] = "num_proto_udp"; +static char str_num_sockets[] = "num_sockets"; +static char str_num_tdgrams[] = "num_type_dgram"; +static char str_num_tseqpkgs[] = "num_type_seqpacket"; +static char str_num_tstreams[] = "num_type_stream"; +static char str_partial_delivery[] = "partial_delivery"; +static char str_peer_error[] = "peer_error"; +static char str_probe[] = "probe"; +static char str_select[] = "select"; +static char str_sender_dry[] = "sender_dry"; +static char str_send_failure[] = "send_failure"; +static char str_shutdown[] = "shutdown"; +static char str_sourceaddr[] = "sourceaddr"; +static char str_timeout[] = "timeout"; +static char str_true[] = "true"; +static char str_want[] = "want"; static char str_lowdelay[] = "lowdelay"; static char str_throughput[] = "throughput"; @@ -1706,9 +1865,14 @@ ERL_NIF_TERM esock_atom_eafnosupport; ERL_NIF_TERM esock_atom_einval; /* *** Atoms *** */ +static ERL_NIF_TERM atom_adaptation_layer; +static ERL_NIF_TERM atom_address; +static ERL_NIF_TERM atom_association; +static ERL_NIF_TERM atom_authentication; static ERL_NIF_TERM atom_close; static ERL_NIF_TERM atom_closed; static ERL_NIF_TERM atom_closing; +static ERL_NIF_TERM atom_data_in; static ERL_NIF_TERM atom_do; static ERL_NIF_TERM atom_dont; static ERL_NIF_TERM atom_false; @@ -1730,8 +1894,13 @@ static ERL_NIF_TERM atom_num_sockets; static ERL_NIF_TERM atom_num_tdgrams; static ERL_NIF_TERM atom_num_tseqpkgs; static ERL_NIF_TERM atom_num_tstreams; +static ERL_NIF_TERM atom_partial_delivery; +static ERL_NIF_TERM atom_peer_error; static ERL_NIF_TERM atom_probe; static ERL_NIF_TERM atom_select; +static ERL_NIF_TERM atom_sender_dry; +static ERL_NIF_TERM atom_send_failure; +static ERL_NIF_TERM atom_shutdown; static ERL_NIF_TERM atom_sourceaddr; static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; @@ -1895,7 +2064,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { int edomain, etype, eproto; - int domain, type, proto; + int domain, type, proto; char* netns; ERL_NIF_TERM emap; ERL_NIF_TERM result; @@ -1920,19 +2089,27 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, "\r\n extra: %T" "\r\n", argv[0], argv[1], argv[2], argv[3]) ); - if (!edomain2domain(edomain, &domain)) - return enif_make_badarg(env); + if (!edomain2domain(edomain, &domain)) { + SGDBG( ("SOCKET", "nif_open -> domain: %d\r\n", domain) ); + return esock_make_error(env, esock_atom_einval); + } - if (!etype2type(etype, &type)) - return enif_make_badarg(env); + if (!etype2type(etype, &type)) { + SGDBG( ("SOCKET", "nif_open -> type: %d\r\n", type) ); + return esock_make_error(env, esock_atom_einval); + } - if (!eproto2proto(eproto, &proto)) - return enif_make_badarg(env); + if (!eproto2proto(eproto, &proto)) { + SGDBG( ("SOCKET", "nif_open -> protocol: %d\r\n", proto) ); + return esock_make_error(env, esock_atom_einval); + } #ifdef HAVE_SETNS /* We *currently* only support one extra option: netns */ - if (!emap2netns(env, emap, &netns)) + if (!emap2netns(env, emap, &netns)) { + SGDBG( ("SOCKET", "nif_open -> namespace: %s\r\n", netns) ); return enif_make_badarg(env); + } #else netns = NULL; #endif @@ -3733,12 +3910,13 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - SocketDescriptor* descP; + SocketDescriptor* descP = NULL; int eLevel, level = -1; int eOpt; ERL_NIF_TERM eIsEncoded; ERL_NIF_TERM eVal; BOOLEAN_T isEncoded, isOTP; + ERL_NIF_TERM result; SGDBG( ("SOCKET", "nif_setopt -> entry with argc: %d\r\n", argc) ); @@ -3748,6 +3926,7 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !GET_INT(env, argv[2], &eLevel) || !GET_INT(env, argv[3], &eOpt)) { + SGDBG( ("SOCKET", "nif_setopt -> failed initial arg check\r\n") ); return enif_make_badarg(env); } eIsEncoded = argv[1]; @@ -3755,23 +3934,35 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, isEncoded = esock_decode_bool(eIsEncoded); - if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) + /* SGDBG( ("SOCKET", "nif_setopt -> eIsDecoded (%T) decoded: %d\r\n", */ + /* eIsEncoded, isEncoded) ); */ + + if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) { + SSDBG( descP, ("SOCKET", "nif_seopt -> failed decode level\r\n") ); return esock_make_error(env, esock_atom_einval); + } SSDBG( descP, ("SOCKET", "nif_setopt -> args when sock = %d:" "\r\n Socket: %T" - "\r\n Encoded: %T (%d)" + "\r\n Encoded: %d (%T)" "\r\n Level: %d (%d)" "\r\n Opt: %d" "\r\n Value: %T" "\r\n", descP->sock, argv[0], - eIsEncoded, isEncoded, - eLevel, level, + isEncoded, eIsEncoded, + level, eLevel, eOpt, eVal) ); - return nsetopt(env, descP, isEncoded, isOTP, level, eOpt, eVal); + result = nsetopt(env, descP, isEncoded, isOTP, level, eOpt, eVal); + + SSDBG( descP, + ("SOCKET", "nif_setopt -> done when" + "\r\n result: %T" + "\r\n", result) ); + + return result; } @@ -5318,6 +5509,12 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, break; #endif +#if defined(SCTP_EVENTS) + case SOCKET_OPT_SCTP_EVENTS: + result = nsetopt_lvl_sctp_events(env, descP, eVal); + break; +#endif + #if defined(SCTP_NODELAY) case SOCKET_OPT_SCTP_NODELAY: result = nsetopt_lvl_sctp_nodelay(env, descP, eVal); @@ -5346,6 +5543,104 @@ ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, #endif +/* nsetopt_lvl_sctp_events - Level SCTP EVENTS option + */ +#if defined(SCTP_EVENTS) +static +ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + ERL_NIF_TERM eDataIn, eAssoc, eAddr, eSndFailure; + ERL_NIF_TERM ePeerError, eShutdown, ePartialDelivery; + ERL_NIF_TERM eAdaptLayer, eAuth, eSndDry; + struct sctp_event_subscribe events; + int res; + size_t sz; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_events -> entry with" + "\r\n eVal: %T" + "\r\n", eVal) ); + + // It must be a map + if (!IS_MAP(env, eVal)) + return esock_make_error(env, esock_atom_einval); + + // It must have atleast ten attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz < 10)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_events -> extract attributes\r\n") ); + + if (!GET_MAP_VAL(env, eVal, atom_data_in, &eDataIn)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_association, &eAssoc)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_address, &eAddr)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_send_failure, &eSndFailure)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_peer_error, &ePeerError)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_shutdown, &eShutdown)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_partial_delivery, &ePartialDelivery)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_adaptation_layer, &eAdaptLayer)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_authentication, &eAuth)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_sender_dry, &eSndDry)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_events -> decode attributes\r\n") ); + + events.sctp_data_io_event = esock_decode_bool(eDataIn); + events.sctp_association_event = esock_decode_bool(eAssoc); + events.sctp_address_event = esock_decode_bool(eAddr); + events.sctp_send_failure_event = esock_decode_bool(eSndFailure); + events.sctp_peer_error_event = esock_decode_bool(ePeerError); + events.sctp_shutdown_event = esock_decode_bool(eShutdown); + events.sctp_partial_delivery_event = esock_decode_bool(ePartialDelivery); + events.sctp_adaptation_layer_event = esock_decode_bool(eAdaptLayer); + events.sctp_authentication_event = esock_decode_bool(eAuth); + events.sctp_sender_dry_event = esock_decode_bool(eSndDry); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_events -> set events option\r\n") ); + + res = socket_setopt(descP->sock, IPPROTO_SCTP, SCTP_EVENTS, + &events, sizeof(events)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_events -> done with" + "\r\n result: %T" + "\r\n", result) ); + + return result; + +} +#endif + + /* nsetopt_lvl_sctp_nodelay - Level SCTP NODELAY option */ #if defined(SCTP_NODELAY) @@ -8269,6 +8564,7 @@ BOOLEAN_T edomain2domain(int edomain, int* domain) #endif default: + *domain = -1; return FALSE; } @@ -8303,6 +8599,7 @@ BOOLEAN_T etype2type(int etype, int* type) #endif default: + *type = -1; return FALSE; } @@ -8337,11 +8634,12 @@ BOOLEAN_T eproto2proto(int eproto, int* proto) #endif default: + *proto = -1; return FALSE; } - return TRUE; - } + return TRUE; +} #ifdef HAVE_SETNS @@ -8909,8 +9207,8 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> entry when" "\r\n sock: %d (%d)" - "\r\n is_direct_call: %d" - "\r\n", descP->sock, fd, is_direct_call) ); + "\r\n Is Direct Call: %s" + "\r\n", descP->sock, fd, B2S(is_direct_call)) ); MLOCK(descP->writeMtx); MLOCK(descP->readMtx); @@ -9051,6 +9349,10 @@ void inform_waiting_procs(ErlNifEnv* env, * */ + SSDBG( descP, + ("SOCKET", "inform_waiting_procs -> abort %T (%T)\r\n", + currentP->data.ref, currentP->data.pid) ); + ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, currentP->data.ref, reason, @@ -9088,59 +9390,71 @@ void socket_down(ErlNifEnv* env, /* Eventually we should go through the other queues also, * the process can be one of them... + * + * Currently only the accteptors actuallu use the queues. */ - /* Check first if its the current acceptor, and if not check the queue */ - if (compare_pids(env, &descP->currentAcceptor.pid, pid)) { - - SSDBG( descP, ("SOCKET", - "socket_down -> current acceptor - try pop the queue\r\n") ); - - if (acceptor_pop(env, descP, - &descP->currentAcceptor.pid, - &descP->currentAcceptor.mon, - &descP->currentAcceptor.ref)) { - int res; - - /* There was another one, so we will still be in accepting state */ - - SSDBG( descP, ("SOCKET", "socket_down -> new (active) acceptor: " - "\r\n pid: %T" - "\r\n ref: %T" - "\r\n", - descP->currentAcceptor.pid, - descP->currentAcceptor.ref) ); + if (descP->currentAcceptorP != NULL) { - if ((res = enif_select(env, - descP->sock, - (ERL_NIF_SELECT_READ), - descP, - &descP->currentAcceptor.pid, - descP->currentAcceptor.ref) < 0)) { + /* + * We have acceptor(s) (atleast one) + * + * Check first if its the current acceptor, + * and if not check the queue. + */ - esock_warning_msg("Failed select (%d) for new acceptor " - "after current (%T) died\r\n", - res, *pid); + if (compare_pids(env, &descP->currentAcceptor.pid, pid)) { + SSDBG( descP, ("SOCKET", + "socket_down -> " + "current acceptor - try pop the queue\r\n") ); + + if (acceptor_pop(env, descP, + &descP->currentAcceptor.pid, + &descP->currentAcceptor.mon, + &descP->currentAcceptor.ref)) { + int res; + + /* There was another one, so we will still be in accepting state */ + + SSDBG( descP, ("SOCKET", "socket_down -> new (active) acceptor: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentAcceptor.pid, + descP->currentAcceptor.ref) ); + + if ((res = enif_select(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, + &descP->currentAcceptor.pid, + descP->currentAcceptor.ref) < 0)) { + + esock_warning_msg("Failed select (%d) for new acceptor " + "after current (%T) died\r\n", + res, *pid); + + } + + } else { + + SSDBG( descP, ("SOCKET", "socket_down -> no active acceptor\r\n") ); + + descP->currentAcceptorP = NULL; + descP->state = SOCKET_STATE_LISTENING; } - + } else { - - SSDBG( descP, ("SOCKET", "socket_down -> no active acceptor\r\n") ); - - descP->currentAcceptorP = NULL; - descP->state = SOCKET_STATE_LISTENING; + + /* Maybe unqueue one of the waiting acceptors */ + + SSDBG( descP, ("SOCKET", + "socket_down -> " + "not current acceptor - maybe a waiting acceptor\r\n") ); + + qunqueue(env, &descP->acceptorsQ, pid); } - - } else { - - /* Maybe unqueue one of the waiting acceptors */ - - SSDBG( descP, ("SOCKET", - "socket_down -> " - "not current acceptor - maybe a waiting acceptor\r\n") ); - - qunqueue(env, &descP->acceptorsQ, pid); } SSDBG( descP, ("SOCKET", "socket_down -> done\r\n") ); @@ -9246,36 +9560,46 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) data.numProtoSCTP = 0; /* +++ Misc atoms +++ */ - atom_close = MKA(env, str_close); - atom_closed = MKA(env, str_closed); - atom_closing = MKA(env, str_closing); - atom_do = MKA(env, str_do); - atom_dont = MKA(env, str_dont); - atom_false = MKA(env, str_false); - atom_global_counters = MKA(env, str_global_counters); - atom_in4_sockaddr = MKA(env, str_in4_sockaddr); - atom_in6_sockaddr = MKA(env, str_in6_sockaddr); - atom_iow = MKA(env, str_iow); - atom_interface = MKA(env, str_interface); - atom_multiaddr = MKA(env, str_multiaddr); - atom_nif_abort = MKA(env, str_nif_abort); - atom_num_dinet = MKA(env, str_num_dinet); - atom_num_dinet6 = MKA(env, str_num_dinet6); - atom_num_dlocal = MKA(env, str_num_dlocal); - atom_num_pip = MKA(env, str_num_pip); - atom_num_psctp = MKA(env, str_num_psctp); - atom_num_ptcp = MKA(env, str_num_ptcp); - atom_num_pudp = MKA(env, str_num_pudp); - atom_num_sockets = MKA(env, str_num_sockets); - atom_num_tdgrams = MKA(env, str_num_tdgrams); - atom_num_tseqpkgs = MKA(env, str_num_tseqpkgs); - atom_num_tstreams = MKA(env, str_num_tstreams); - atom_probe = MKA(env, str_probe); - atom_select = MKA(env, str_select); - atom_sourceaddr = MKA(env, str_sourceaddr); - atom_timeout = MKA(env, str_timeout); - atom_true = MKA(env, str_true); - atom_want = MKA(env, str_want); + atom_adaptation_layer = MKA(env, str_adaptation_layer); + atom_address = MKA(env, str_address); + atom_association = MKA(env, str_association); + atom_authentication = MKA(env, str_authentication); + atom_close = MKA(env, str_close); + atom_closed = MKA(env, str_closed); + atom_closing = MKA(env, str_closing); + atom_data_in = MKA(env, str_data_in); + atom_do = MKA(env, str_do); + atom_dont = MKA(env, str_dont); + atom_false = MKA(env, str_false); + atom_global_counters = MKA(env, str_global_counters); + atom_in4_sockaddr = MKA(env, str_in4_sockaddr); + atom_in6_sockaddr = MKA(env, str_in6_sockaddr); + atom_iow = MKA(env, str_iow); + atom_interface = MKA(env, str_interface); + atom_multiaddr = MKA(env, str_multiaddr); + atom_nif_abort = MKA(env, str_nif_abort); + atom_num_dinet = MKA(env, str_num_dinet); + atom_num_dinet6 = MKA(env, str_num_dinet6); + atom_num_dlocal = MKA(env, str_num_dlocal); + atom_num_pip = MKA(env, str_num_pip); + atom_num_psctp = MKA(env, str_num_psctp); + atom_num_ptcp = MKA(env, str_num_ptcp); + atom_num_pudp = MKA(env, str_num_pudp); + atom_num_sockets = MKA(env, str_num_sockets); + atom_num_tdgrams = MKA(env, str_num_tdgrams); + atom_num_tseqpkgs = MKA(env, str_num_tseqpkgs); + atom_num_tstreams = MKA(env, str_num_tstreams); + atom_partial_delivery = MKA(env, str_partial_delivery); + atom_peer_error = MKA(env, str_peer_error); + atom_probe = MKA(env, str_probe); + atom_select = MKA(env, str_select); + atom_sender_dry = MKA(env, str_sender_dry); + atom_send_failure = MKA(env, str_send_failure); + atom_shutdown = MKA(env, str_shutdown); + atom_sourceaddr = MKA(env, str_sourceaddr); + atom_timeout = MKA(env, str_timeout); + atom_true = MKA(env, str_true); + atom_want = MKA(env, str_want); /* Global atom(s) */ esock_atom_addr = MKA(env, "addr"); -- cgit v1.2.3 From f0a2e68a31ac585780ad05f777f1b7551770420e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 14:36:34 +0200 Subject: [socket-nif] Add support for socket (level sctp) option disable_fragments Added support for the SCTP option DISABLE_FRAGMENTS. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 53 ++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 7271db2143..2a60a840c8 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -542,9 +542,10 @@ typedef union { #define SOCKET_OPT_UDP_CORK 1 -#define SOCKET_OPT_SCTP_AUTOCLOSE 8 -#define SOCKET_OPT_SCTP_EVENTS 14 -#define SOCKET_OPT_SCTP_NODELAY 23 +#define SOCKET_OPT_SCTP_AUTOCLOSE 8 +#define SOCKET_OPT_SCTP_DISABLE_FRAGMENTS 12 +#define SOCKET_OPT_SCTP_EVENTS 14 +#define SOCKET_OPT_SCTP_NODELAY 23 /* =================================================================== * @@ -1216,6 +1217,11 @@ static ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SCTP_DISABLE_FRAGMENTS) +static ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SCTP_EVENTS) static ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, SocketDescriptor* descP, @@ -1433,6 +1439,10 @@ static ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SCTP_DISABLE_FRAGMENTS) +static ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SCTP_NODELAY) static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, SocketDescriptor* descP); @@ -5509,6 +5519,12 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, break; #endif +#if defined(SCTP_DISABLE_FRAGMENTS) + case SOCKET_OPT_SCTP_DISABLE_FRAGMENTS: + result = nsetopt_lvl_sctp_disable_fragments(env, descP, eVal); + break; +#endif + #if defined(SCTP_EVENTS) case SOCKET_OPT_SCTP_EVENTS: result = nsetopt_lvl_sctp_events(env, descP, eVal); @@ -5543,6 +5559,19 @@ ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, #endif +/* nsetopt_lvl_sctp_disable_fragments - Level SCTP DISABLE_FRAGMENTS option + */ +#if defined(SCTP_DISABLE_FRAGMENTS) +static +ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS, eVal); +} +#endif + + /* nsetopt_lvl_sctp_events - Level SCTP EVENTS option */ #if defined(SCTP_EVENTS) @@ -7308,6 +7337,12 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, break; #endif +#if defined(SCTP_DISABLE_FRAGMENTS) + case SOCKET_OPT_SCTP_DISABLE_FRAGMENTS: + result = ngetopt_lvl_sctp_disable_fragments(env, descP); + break; +#endif + #if defined(SCTP_NODELAY) case SOCKET_OPT_SCTP_NODELAY: result = ngetopt_lvl_sctp_nodelay(env, descP); @@ -7335,6 +7370,18 @@ ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, #endif +/* ngetopt_lvl_sctp_disable_fragments - Level SCTP DISABLE:FRAGMENTS option + */ +#if defined(SCTP_DISABLE_FRAGMENTS) +static +ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS); +} +#endif + + /* ngetopt_lvl_sctp_nodelay - Level SCTP NODELAY option */ #if defined(SCTP_NODELAY) -- cgit v1.2.3 From 7a5b320b5bb5ec45b21839005e8538172908fb57 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 16:39:13 +0200 Subject: [socket-nif] Add (partial) support for socket (level sctp) option associnfo Added support for the SCTP option ASSOCINFO. This option is a bit tricky. As the underlying structure (sctp_assocparams) contains the assoc_id, it begs the question what happens if this option is fetched for: * The own assoc (which means that we might have the assoc id in the descriptor and can initiate that part of the struct accordningly). * Another assoc: From assoc A asks for info with assoc_id set to that of assoc B. * The "owning" endpoint. * Another endpoint (an endpoint to which the assoc does not belong). So, if the user calls socket:[getopt|setopt] for an association socket, shall we require that the assoc_id field is set to -1? Or not set at all and therefor filled in automatically by the nif-code? And, if the user calls socket:[getopt|setopt] for an endpoint socket, shall we require that the assoc_id field is set to a valid id? Or shall it not be allowed? Questions, questions... OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 1 + erts/emulator/nifs/common/socket_nif.c | 224 ++++++++++++++++++++++++++++++++- 2 files changed, 224 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 67e4baba27..28f42a7345 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -167,6 +167,7 @@ extern ERL_NIF_TERM esock_atom_einval; #define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) #define MKTA(E, A, AL) enif_make_tuple_from_array((E), (A), (AL)) +#define MKUI(E,I) enif_make_uint((E), (I)) #define MCREATE(N) enif_mutex_create((N)) #define MDESTROY(M) enif_mutex_destroy((M)) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 2a60a840c8..50e7ec5f5e 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -542,6 +542,7 @@ typedef union { #define SOCKET_OPT_UDP_CORK 1 +#define SOCKET_OPT_SCTP_ASSOCINFO 2 #define SOCKET_OPT_SCTP_AUTOCLOSE 8 #define SOCKET_OPT_SCTP_DISABLE_FRAGMENTS 12 #define SOCKET_OPT_SCTP_EVENTS 14 @@ -1212,6 +1213,11 @@ static ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, ERL_NIF_TERM eVal); +#if defined(SCTP_ASSOCINFO) +static ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SCTP_AUTOCLOSE) static ERL_NIF_TERM nsetopt_lvl_sctp_autoclose(ErlNifEnv* env, SocketDescriptor* descP, @@ -1435,6 +1441,10 @@ static ERL_NIF_TERM ngetopt_lvl_udp_cork(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); +#if defined(SCTP_ASSOCINFO) +static ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SCTP_AUTOCLOSE) static ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, SocketDescriptor* descP); @@ -1780,11 +1790,13 @@ static const struct in6_addr in6addr_loopback = static char str_adaptation_layer[] = "adaptation_layer"; static char str_address[] = "address"; static char str_association[] = "association"; +static char str_assoc_id[] = "assoc_id"; static char str_authentication[] = "authentication"; // static char str_any[] = "any"; static char str_close[] = "close"; static char str_closed[] = "closed"; static char str_closing[] = "closing"; +static char str_cookie_life[] = "cookie_life"; static char str_data_in[] = "data_in"; static char str_do[] = "do"; static char str_dont[] = "dont"; @@ -1794,12 +1806,15 @@ static char str_in4_sockaddr[] = "in4_sockaddr"; static char str_in6_sockaddr[] = "in6_sockaddr"; static char str_iow[] = "iow"; static char str_interface[] = "interface"; +static char str_local_rwnd[] = "local_rwnd"; // static char str_loopback[] = "loopback"; +static char str_max_rxt[] = "max_rxt"; static char str_multiaddr[] = "multiaddr"; static char str_nif_abort[] = "nif_abort"; static char str_num_dlocal[] = "num_domain_local"; static char str_num_dinet[] = "num_domain_inet"; static char str_num_dinet6[] = "num_domain_inet6"; +static char str_num_peer_dests[] = "num_peer_dests"; static char str_num_pip[] = "num_proto_ip"; static char str_num_psctp[] = "num_proto_sctp"; static char str_num_ptcp[] = "num_proto_tcp"; @@ -1810,6 +1825,7 @@ static char str_num_tseqpkgs[] = "num_type_seqpacket"; static char str_num_tstreams[] = "num_type_stream"; static char str_partial_delivery[] = "partial_delivery"; static char str_peer_error[] = "peer_error"; +static char str_peer_rwnd[] = "peer_rwnd"; static char str_probe[] = "probe"; static char str_select[] = "select"; static char str_sender_dry[] = "sender_dry"; @@ -1878,10 +1894,12 @@ ERL_NIF_TERM esock_atom_einval; static ERL_NIF_TERM atom_adaptation_layer; static ERL_NIF_TERM atom_address; static ERL_NIF_TERM atom_association; +static ERL_NIF_TERM atom_assoc_id; static ERL_NIF_TERM atom_authentication; static ERL_NIF_TERM atom_close; static ERL_NIF_TERM atom_closed; static ERL_NIF_TERM atom_closing; +static ERL_NIF_TERM atom_cookie_life; static ERL_NIF_TERM atom_data_in; static ERL_NIF_TERM atom_do; static ERL_NIF_TERM atom_dont; @@ -1891,11 +1909,14 @@ static ERL_NIF_TERM atom_in4_sockaddr; static ERL_NIF_TERM atom_in6_sockaddr; static ERL_NIF_TERM atom_iow; static ERL_NIF_TERM atom_interface; +static ERL_NIF_TERM atom_local_rwnd; +static ERL_NIF_TERM atom_max_rxt; static ERL_NIF_TERM atom_multiaddr; static ERL_NIF_TERM atom_nif_abort; static ERL_NIF_TERM atom_num_dinet; static ERL_NIF_TERM atom_num_dinet6; static ERL_NIF_TERM atom_num_dlocal; +static ERL_NIF_TERM atom_num_peer_dests; static ERL_NIF_TERM atom_num_pip; static ERL_NIF_TERM atom_num_psctp; static ERL_NIF_TERM atom_num_ptcp; @@ -1906,6 +1927,7 @@ static ERL_NIF_TERM atom_num_tseqpkgs; static ERL_NIF_TERM atom_num_tstreams; static ERL_NIF_TERM atom_partial_delivery; static ERL_NIF_TERM atom_peer_error; +static ERL_NIF_TERM atom_peer_rwnd; static ERL_NIF_TERM atom_probe; static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_sender_dry; @@ -2042,7 +2064,7 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, ERL_NIF_TERM vals[] = {BOOL2ATOM(data.dbg), BOOL2ATOM(data.iow), lgcnt}; ERL_NIF_TERM info; unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); - unsigned int numVals = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); ESOCK_ASSERT( (numKeys == numVals) ); @@ -5513,6 +5535,12 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, "\r\n", eOpt) ); switch (eOpt) { +#if defined(SCTP_ASSOCINFO) + case SOCKET_OPT_SCTP_ASSOCINFO: + result = nsetopt_lvl_sctp_associnfo(env, descP, eVal); + break; +#endif + #if defined(SCTP_AUTOCLOSE) case SOCKET_OPT_SCTP_AUTOCLOSE: result = nsetopt_lvl_sctp_autoclose(env, descP, eVal); @@ -5546,6 +5574,116 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, } +/* nsetopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option + */ +#if defined(SCTP_ASSOCINFO) +static +ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + ERL_NIF_TERM eAssocId, eMaxRxt, eNumPeerDests; + ERL_NIF_TERM ePeerRWND, eLocalRWND, eCookieLife; + struct sctp_assocparams assocParams; + int res; + size_t sz; + unsigned int tmp; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_associnfo -> entry with" + "\r\n eVal: %T" + "\r\n", eVal) ); + + // It must be a map + if (!IS_MAP(env, eVal)) + return esock_make_error(env, esock_atom_einval); + + // It must have atleast ten attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz < 6)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_associnfo -> extract attributes\r\n") ); + + if (!GET_MAP_VAL(env, eVal, atom_assoc_id, &eAssocId)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_max_rxt, &eMaxRxt)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_num_peer_dests, &eNumPeerDests)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_peer_rwnd, &ePeerRWND)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_local_rwnd, &eLocalRWND)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_cookie_life, &eCookieLife)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_events -> decode attributes\r\n") ); + + if (!GET_INT(env, eAssocId, &assocParams.sasoc_assoc_id)) + return esock_make_error(env, esock_atom_einval); + + /* + * We should really make sure this is ok in erlang (to ensure that + * the value fits in 16-bits). + * The value should be a 16-bit unsigned int... + * Both sasoc_asocmaxrxt and sasoc_number_peer_destinations. + */ + + /* + if (!GET_UINT(env, eAssocId, (unsigned int*) &assocParams.sasoc_asocmaxrxt)) + return esock_make_error(env, esock_atom_einval); + */ + if (!GET_UINT(env, eAssocId, &tmp)) + return esock_make_error(env, esock_atom_einval); + assocParams.sasoc_asocmaxrxt = (uint16_t) tmp; + + /* + if (!GET_UINT(env, eAssocId, (unsigned int*) &assocParams.sasoc_number_peer_destinations)) + return esock_make_error(env, esock_atom_einval); + */ + if (!GET_UINT(env, eAssocId, &tmp)) + return esock_make_error(env, esock_atom_einval); + assocParams.sasoc_number_peer_destinations = (uint16_t) tmp; + + if (!GET_UINT(env, eAssocId, &assocParams.sasoc_peer_rwnd)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_UINT(env, eAssocId, &assocParams.sasoc_local_rwnd)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_UINT(env, eAssocId, &assocParams.sasoc_cookie_life)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_events -> set associnfo option\r\n") ); + + res = socket_setopt(descP->sock, IPPROTO_SCTP, SCTP_ASSOCINFO, + &assocParams, sizeof(assocParams)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_associnfo -> done with" + "\r\n result: %T" + "\r\n", result) ); + + return result; + +} +#endif + + /* nsetopt_lvl_sctp_autoclose - Level SCTP AUTOCLOSE option */ #if defined(SCTP_AUTOCLOSE) @@ -7330,7 +7468,18 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_sctp -> entry with" + "\r\n opt: %d" + "\r\n", eOpt) ); + switch (eOpt) { +#if defined(SCTP_ASSOCINFO) + case SOCKET_OPT_SCTP_ASSOCINFO: + result = ngetopt_lvl_sctp_associnfo(env, descP); + break; +#endif + #if defined(SCTP_AUTOCLOSE) case SOCKET_OPT_SCTP_AUTOCLOSE: result = ngetopt_lvl_sctp_autoclose(env, descP); @@ -7354,10 +7503,77 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, break; } + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_sctp -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } +/* ngetopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option + * + * + * + * We should really specify which association this relates to, + * as it is now we get assoc-id = 0. If this socket is an + * association (and not an endpoint) then it will have an + * assoc id. But since the sctp support at present is "limited", + * we leave it for now. + * What do we do if this is an endpoint? Invalid op? + * + * + */ +#if defined(SCTP_ASSOCINFO) +static +ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + struct sctp_assocparams val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + SSDBG( descP, ("SOCKET", "ngetopt_lvl_sctp_associnfo -> entry\r\n") ); + + sys_memzero((char*) &val, valSz); + res = sock_getopt(descP->sock, IPPROTO_SCTP, SCTP_ASSOCINFO, &val, &valSz); + + if (res != 0) { + result = esock_make_error_errno(env, sock_errno()); + } else { + ERL_NIF_TERM eAssocParams; + ERL_NIF_TERM keys[] = {atom_assoc_id, atom_max_rxt, atom_num_peer_dests, + atom_peer_rwnd, atom_local_rwnd, atom_cookie_life}; + ERL_NIF_TERM vals[] = {MKUI(env, val.sasoc_assoc_id), + MKUI(env, val.sasoc_asocmaxrxt), + MKUI(env, val.sasoc_number_peer_destinations), + MKUI(env, val.sasoc_peer_rwnd), + MKUI(env, val.sasoc_local_rwnd), + MKUI(env, val.sasoc_cookie_life)}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &eAssocParams)) + return esock_make_error(env, esock_atom_einval);; + + result = esock_make_ok2(env, eAssocParams); + } + + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_sctp_associnfo -> done with" + "\r\n res: %d" + "\r\n result: %T" + "\r\n", res, result) ); + + return result; +} +#endif + + /* ngetopt_lvl_sctp_autoclose - Level SCTP AUTOCLOSE option */ #if defined(SCTP_AUTOCLOSE) @@ -9610,10 +9826,12 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_adaptation_layer = MKA(env, str_adaptation_layer); atom_address = MKA(env, str_address); atom_association = MKA(env, str_association); + atom_assoc_id = MKA(env, str_assoc_id); atom_authentication = MKA(env, str_authentication); atom_close = MKA(env, str_close); atom_closed = MKA(env, str_closed); atom_closing = MKA(env, str_closing); + atom_cookie_life = MKA(env, str_cookie_life); atom_data_in = MKA(env, str_data_in); atom_do = MKA(env, str_do); atom_dont = MKA(env, str_dont); @@ -9623,11 +9841,14 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_in6_sockaddr = MKA(env, str_in6_sockaddr); atom_iow = MKA(env, str_iow); atom_interface = MKA(env, str_interface); + atom_local_rwnd = MKA(env, str_local_rwnd); + atom_max_rxt = MKA(env, str_max_rxt); atom_multiaddr = MKA(env, str_multiaddr); atom_nif_abort = MKA(env, str_nif_abort); atom_num_dinet = MKA(env, str_num_dinet); atom_num_dinet6 = MKA(env, str_num_dinet6); atom_num_dlocal = MKA(env, str_num_dlocal); + atom_num_peer_dests = MKA(env, str_num_peer_dests); atom_num_pip = MKA(env, str_num_pip); atom_num_psctp = MKA(env, str_num_psctp); atom_num_ptcp = MKA(env, str_num_ptcp); @@ -9637,6 +9858,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_num_tseqpkgs = MKA(env, str_num_tseqpkgs); atom_num_tstreams = MKA(env, str_num_tstreams); atom_partial_delivery = MKA(env, str_partial_delivery); + atom_peer_rwnd = MKA(env, str_peer_rwnd); atom_peer_error = MKA(env, str_peer_error); atom_probe = MKA(env, str_probe); atom_select = MKA(env, str_select); -- cgit v1.2.3 From 6bb60f1fecef368991806e25a0022c63b8650f39 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 17:34:15 +0200 Subject: [socket-nif] Add (partial) support for socket (level sctp) option rtoinfo Added support for the SCTP option RTOINFO. We have the same questions for this option as for ASSOCINFO. Maybe the assoc field shall be made 'out' only (that is, it will be ignored for setopt). The assoc id used will be that which is stored in the descriptor (how do we get it to begin with?). Questions, questions... OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 194 ++++++++++++++++++++++++++++++--- 1 file changed, 178 insertions(+), 16 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 50e7ec5f5e..f9643cccbf 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -547,6 +547,7 @@ typedef union { #define SOCKET_OPT_SCTP_DISABLE_FRAGMENTS 12 #define SOCKET_OPT_SCTP_EVENTS 14 #define SOCKET_OPT_SCTP_NODELAY 23 +#define SOCKET_OPT_SCTP_RTOINFO 29 /* =================================================================== * @@ -1238,6 +1239,11 @@ static ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SCTP_RTOINFO) +static ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #endif // defined(HAVE_SCTP) static ERL_NIF_TERM ngetopt(ErlNifEnv* env, @@ -1457,6 +1463,10 @@ static ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SCTP_RTOINFO) +static ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #endif // defined(HAVE_SCTP) static ERL_NIF_TERM nsockname(ErlNifEnv* env, SocketDescriptor* descP); @@ -1805,10 +1815,13 @@ static char str_global_counters[] = "global_counters"; static char str_in4_sockaddr[] = "in4_sockaddr"; static char str_in6_sockaddr[] = "in6_sockaddr"; static char str_iow[] = "iow"; +static char str_initial[] = "initial"; static char str_interface[] = "interface"; static char str_local_rwnd[] = "local_rwnd"; // static char str_loopback[] = "loopback"; +static char str_max[] = "max"; static char str_max_rxt[] = "max_rxt"; +static char str_min[] = "min"; static char str_multiaddr[] = "multiaddr"; static char str_nif_abort[] = "nif_abort"; static char str_num_dlocal[] = "num_domain_local"; @@ -1908,9 +1921,12 @@ static ERL_NIF_TERM atom_global_counters; static ERL_NIF_TERM atom_in4_sockaddr; static ERL_NIF_TERM atom_in6_sockaddr; static ERL_NIF_TERM atom_iow; +static ERL_NIF_TERM atom_initial; static ERL_NIF_TERM atom_interface; static ERL_NIF_TERM atom_local_rwnd; +static ERL_NIF_TERM atom_max; static ERL_NIF_TERM atom_max_rxt; +static ERL_NIF_TERM atom_min; static ERL_NIF_TERM atom_multiaddr; static ERL_NIF_TERM atom_nif_abort; static ERL_NIF_TERM atom_num_dinet; @@ -5565,6 +5581,12 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, break; #endif +#if defined(SCTP_RTOINFO) + case SOCKET_OPT_SCTP_RTOINFO: + result = nsetopt_lvl_sctp_rtoinfo(env, descP, eVal); + break; +#endif + default: result = esock_make_error(env, esock_atom_einval); break; @@ -5625,45 +5647,37 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, return esock_make_error(env, esock_atom_einval); SSDBG( descP, - ("SOCKET", "nsetopt_lvl_sctp_events -> decode attributes\r\n") ); + ("SOCKET", "nsetopt_lvl_sctp_associnfo -> decode attributes\r\n") ); if (!GET_INT(env, eAssocId, &assocParams.sasoc_assoc_id)) return esock_make_error(env, esock_atom_einval); /* * We should really make sure this is ok in erlang (to ensure that - * the value fits in 16-bits). + * the values (max-rxt and num-peer-dests) fits in 16-bits). * The value should be a 16-bit unsigned int... * Both sasoc_asocmaxrxt and sasoc_number_peer_destinations. */ - /* - if (!GET_UINT(env, eAssocId, (unsigned int*) &assocParams.sasoc_asocmaxrxt)) - return esock_make_error(env, esock_atom_einval); - */ - if (!GET_UINT(env, eAssocId, &tmp)) + if (!GET_UINT(env, eMaxRxt, &tmp)) return esock_make_error(env, esock_atom_einval); assocParams.sasoc_asocmaxrxt = (uint16_t) tmp; - /* - if (!GET_UINT(env, eAssocId, (unsigned int*) &assocParams.sasoc_number_peer_destinations)) - return esock_make_error(env, esock_atom_einval); - */ - if (!GET_UINT(env, eAssocId, &tmp)) + if (!GET_UINT(env, eNumPeerDests, &tmp)) return esock_make_error(env, esock_atom_einval); assocParams.sasoc_number_peer_destinations = (uint16_t) tmp; - if (!GET_UINT(env, eAssocId, &assocParams.sasoc_peer_rwnd)) + if (!GET_UINT(env, ePeerRWND, &assocParams.sasoc_peer_rwnd)) return esock_make_error(env, esock_atom_einval); - if (!GET_UINT(env, eAssocId, &assocParams.sasoc_local_rwnd)) + if (!GET_UINT(env, eLocalRWND, &assocParams.sasoc_local_rwnd)) return esock_make_error(env, esock_atom_einval); - if (!GET_UINT(env, eAssocId, &assocParams.sasoc_cookie_life)) + if (!GET_UINT(env, eCookieLife, &assocParams.sasoc_cookie_life)) return esock_make_error(env, esock_atom_einval); SSDBG( descP, - ("SOCKET", "nsetopt_lvl_sctp_events -> set associnfo option\r\n") ); + ("SOCKET", "nsetopt_lvl_sctp_associnfo -> set associnfo option\r\n") ); res = socket_setopt(descP->sock, IPPROTO_SCTP, SCTP_ASSOCINFO, &assocParams, sizeof(assocParams)); @@ -5821,6 +5835,85 @@ ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, #endif +/* nsetopt_lvl_sctp_rtoinfo - Level SCTP RTOINFO option + */ +#if defined(SCTP_RTOINFO) +static +ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + ERL_NIF_TERM eAssocId, eInitial, eMax, eMin; + struct sctp_rtoinfo rtoInfo; + int res; + size_t sz; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> entry with" + "\r\n eVal: %T" + "\r\n", eVal) ); + + // It must be a map + if (!IS_MAP(env, eVal)) + return esock_make_error(env, esock_atom_einval); + + // It must have atleast ten attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz < 4)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> extract attributes\r\n") ); + + if (!GET_MAP_VAL(env, eVal, atom_assoc_id, &eAssocId)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_initial, &eInitial)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_max, &eMax)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_min, &eMin)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> decode attributes\r\n") ); + + if (!GET_INT(env, eAssocId, &rtoInfo.srto_assoc_id)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_UINT(env, eInitial, &rtoInfo.srto_initial)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_UINT(env, eMax, &rtoInfo.srto_max)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_UINT(env, eMin, &rtoInfo.srto_min)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> set associnfo option\r\n") ); + + res = socket_setopt(descP->sock, IPPROTO_SCTP, SCTP_RTOINFO, + &rtoInfo, sizeof(rtoInfo)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> done with" + "\r\n result: %T" + "\r\n", result) ); + + return result; + +} +#endif + + #endif // defined(HAVE_SCTP) @@ -7498,6 +7591,12 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, break; #endif +#if defined(SCTP_RTOINFO) + case SOCKET_OPT_SCTP_RTOINFO: + result = ngetopt_lvl_sctp_rtoinfo(env, descP); + break; +#endif + default: result = esock_make_error(env, esock_atom_einval); break; @@ -7610,6 +7709,66 @@ ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, #endif +/* ngetopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option + * + * + * + * We should really specify which association this relates to, + * as it is now we get assoc-id = 0. If this socket is an + * association (and not an endpoint) then it will have an + * assoc id (we can assume). But since the sctp support at + * present is "limited", we leave it for now. + * What do we do if this is an endpoint? Invalid op? + * + * + */ +#if defined(SCTP_RTOINFO) +static +ERL_NIF_TERM ngetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + struct sctp_rtoinfo val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + SSDBG( descP, ("SOCKET", "ngetopt_lvl_sctp_rtoinfo -> entry\r\n") ); + + sys_memzero((char*) &val, valSz); + res = sock_getopt(descP->sock, IPPROTO_SCTP, SCTP_RTOINFO, &val, &valSz); + + if (res != 0) { + result = esock_make_error_errno(env, sock_errno()); + } else { + ERL_NIF_TERM eRTOInfo; + ERL_NIF_TERM keys[] = {atom_assoc_id, atom_initial, atom_max, atom_min}; + ERL_NIF_TERM vals[] = {MKUI(env, val.srto_assoc_id), + MKUI(env, val.srto_initial), + MKUI(env, val.srto_max), + MKUI(env, val.srto_min)}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &eRTOInfo)) + return esock_make_error(env, esock_atom_einval);; + + result = esock_make_ok2(env, eRTOInfo); + } + + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_sctp_rtoinfo -> done with" + "\r\n res: %d" + "\r\n result: %T" + "\r\n", res, result) ); + + return result; +} +#endif + + + #endif // defined(HAVE_SCTP) @@ -9840,9 +9999,12 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_in4_sockaddr = MKA(env, str_in4_sockaddr); atom_in6_sockaddr = MKA(env, str_in6_sockaddr); atom_iow = MKA(env, str_iow); + atom_initial = MKA(env, str_initial); atom_interface = MKA(env, str_interface); atom_local_rwnd = MKA(env, str_local_rwnd); + atom_max = MKA(env, str_max); atom_max_rxt = MKA(env, str_max_rxt); + atom_min = MKA(env, str_min); atom_multiaddr = MKA(env, str_multiaddr); atom_nif_abort = MKA(env, str_nif_abort); atom_num_dinet = MKA(env, str_num_dinet); -- cgit v1.2.3 From 08ce39bbc2cd3006475c87807042c2d08a68736f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 19 Jul 2018 17:54:14 +0200 Subject: [socket-nif] Add support for socket (level sctp) option maxseg Added support for the SCTP option MAXSEG. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f9643cccbf..e84eb7aa8e 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -546,6 +546,7 @@ typedef union { #define SOCKET_OPT_SCTP_AUTOCLOSE 8 #define SOCKET_OPT_SCTP_DISABLE_FRAGMENTS 12 #define SOCKET_OPT_SCTP_EVENTS 14 +#define SOCKET_OPT_SCTP_MAXSEG 21 #define SOCKET_OPT_SCTP_NODELAY 23 #define SOCKET_OPT_SCTP_RTOINFO 29 @@ -1234,6 +1235,11 @@ static ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SCTP_MAXSEG) +static ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SCTP_NODELAY) static ERL_NIF_TERM nsetopt_lvl_sctp_nodelay(ErlNifEnv* env, SocketDescriptor* descP, @@ -1459,6 +1465,10 @@ static ERL_NIF_TERM ngetopt_lvl_sctp_autoclose(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SCTP_MAXSEG) +static ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SCTP_NODELAY) static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, SocketDescriptor* descP); @@ -5575,6 +5585,12 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, break; #endif +#if defined(SCTP_MAXSEG) + case SOCKET_OPT_SCTP_MAXSEG: + result = nsetopt_lvl_sctp_maxseg(env, descP, eVal); + break; +#endif + #if defined(SCTP_NODELAY) case SOCKET_OPT_SCTP_NODELAY: result = nsetopt_lvl_sctp_nodelay(env, descP, eVal); @@ -5822,6 +5838,19 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, #endif +/* nsetopt_lvl_sctp_maxseg - Level SCTP MAXSEG option + */ +#if defined(SCTP_MAXSEG) +static +ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_MAXSEG, eVal); +} +#endif + + /* nsetopt_lvl_sctp_nodelay - Level SCTP NODELAY option */ #if defined(SCTP_NODELAY) @@ -7585,6 +7614,12 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, break; #endif +#if defined(SCTP_MAXSEG) + case SOCKET_OPT_SCTP_MAXSEG: + result = ngetopt_lvl_sctp_maxseg(env, descP); + break; +#endif + #if defined(SCTP_NODELAY) case SOCKET_OPT_SCTP_NODELAY: result = ngetopt_lvl_sctp_nodelay(env, descP); @@ -7697,6 +7732,18 @@ ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, #endif +/* ngetopt_lvl_sctp_maxseg - Level SCTP MAXSEG option + */ +#if defined(SCTP_MAXSEG) +static +ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, IPPROTO_SCTP, SCTP_MAXSEG); +} +#endif + + /* ngetopt_lvl_sctp_nodelay - Level SCTP NODELAY option */ #if defined(SCTP_NODELAY) -- cgit v1.2.3 From bd36af21717b138c91724128e592b3fc587bb07a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 10:10:28 +0200 Subject: [socket-nif] Add support for socket (level sctp) option initmsg Added support for the SCTP option INITMSG. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 198 ++++++++++++++++++++++++++++++--- 1 file changed, 183 insertions(+), 15 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index e84eb7aa8e..e8e1df0842 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -263,43 +263,43 @@ * #if defined(__GNUC__) && defined(HAVE_SCTP_BINDX) -static typeof(sctp_bindx) *p_sctp_bindx = NULL; +static typeof(sctp_bindx) *esock_sctp_bindx = NULL; #else -static int (*p_sctp_bindx) +static int (*esock_sctp_bindx) (int sd, struct sockaddr *addrs, int addrcnt, int flags) = NULL; #endif #if defined(__GNUC__) && defined(HAVE_SCTP_PEELOFF) -static typeof(sctp_peeloff) *p_sctp_peeloff = NULL; +static typeof(sctp_peeloff) *esock_sctp_peeloff = NULL; #else -static int (*p_sctp_peeloff) +static int (*esock_sctp_peeloff) (int sd, sctp_assoc_t assoc_id) = NULL; #endif #if defined(__GNUC__) && defined(HAVE_SCTP_GETLADDRS) -static typeof(sctp_getladdrs) *p_sctp_getladdrs = NULL; +static typeof(sctp_getladdrs) *esock_sctp_getladdrs = NULL; #else -static int (*p_sctp_getladdrs) +static int (*esock_sctp_getladdrs) (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; #endif #if defined(__GNUC__) && defined(HAVE_SCTP_FREELADDRS) -static typeof(sctp_freeladdrs) *p_sctp_freeladdrs = NULL; +static typeof(sctp_freeladdrs) *esock_sctp_freeladdrs = NULL; #else -static void (*p_sctp_freeladdrs)(struct sockaddr *addrs) = NULL; +static void (*esock_sctp_freeladdrs)(struct sockaddr *addrs) = NULL; #endif #if defined(__GNUC__) && defined(HAVE_SCTP_GETPADDRS) -static typeof(sctp_getpaddrs) *p_sctp_getpaddrs = NULL; +static typeof(sctp_getpaddrs) *esock_sctp_getpaddrs = NULL; #else -static int (*p_sctp_getpaddrs) +static int (*esock_sctp_getpaddrs) (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL; #endif #if defined(__GNUC__) && defined(HAVE_SCTP_FREEPADDRS) -static typeof(sctp_freepaddrs) *p_sctp_freepaddrs = NULL; +static typeof(sctp_freepaddrs) *esock_sctp_freepaddrs = NULL; #else -static void (*p_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; +static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #endif */ @@ -546,6 +546,7 @@ typedef union { #define SOCKET_OPT_SCTP_AUTOCLOSE 8 #define SOCKET_OPT_SCTP_DISABLE_FRAGMENTS 12 #define SOCKET_OPT_SCTP_EVENTS 14 +#define SOCKET_OPT_SCTP_INITMSG 18 #define SOCKET_OPT_SCTP_MAXSEG 21 #define SOCKET_OPT_SCTP_NODELAY 23 #define SOCKET_OPT_SCTP_RTOINFO 29 @@ -1235,6 +1236,11 @@ static ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SCTP_INITMSG) +static ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SCTP_MAXSEG) static ERL_NIF_TERM nsetopt_lvl_sctp_maxseg(ErlNifEnv* env, SocketDescriptor* descP, @@ -1469,6 +1475,10 @@ static ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sctp_maxseg(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SCTP_INITMSG) +static ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SCTP_NODELAY) static ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, SocketDescriptor* descP); @@ -1830,6 +1840,9 @@ static char str_interface[] = "interface"; static char str_local_rwnd[] = "local_rwnd"; // static char str_loopback[] = "loopback"; static char str_max[] = "max"; +static char str_max_attempts[] = "max_attempts"; +static char str_max_init_timeo[] = "max_init_timeo"; +static char str_max_instreams[] = "max_instreams"; static char str_max_rxt[] = "max_rxt"; static char str_min[] = "min"; static char str_multiaddr[] = "multiaddr"; @@ -1837,6 +1850,7 @@ static char str_nif_abort[] = "nif_abort"; static char str_num_dlocal[] = "num_domain_local"; static char str_num_dinet[] = "num_domain_inet"; static char str_num_dinet6[] = "num_domain_inet6"; +static char str_num_outstreams[] = "num_outstreams"; static char str_num_peer_dests[] = "num_peer_dests"; static char str_num_pip[] = "num_proto_ip"; static char str_num_psctp[] = "num_proto_sctp"; @@ -1935,6 +1949,9 @@ static ERL_NIF_TERM atom_initial; static ERL_NIF_TERM atom_interface; static ERL_NIF_TERM atom_local_rwnd; static ERL_NIF_TERM atom_max; +static ERL_NIF_TERM atom_max_attempts; +static ERL_NIF_TERM atom_max_init_timeo; +static ERL_NIF_TERM atom_max_instreams; static ERL_NIF_TERM atom_max_rxt; static ERL_NIF_TERM atom_min; static ERL_NIF_TERM atom_multiaddr; @@ -1942,6 +1959,7 @@ static ERL_NIF_TERM atom_nif_abort; static ERL_NIF_TERM atom_num_dinet; static ERL_NIF_TERM atom_num_dinet6; static ERL_NIF_TERM atom_num_dlocal; +static ERL_NIF_TERM atom_num_outstreams; static ERL_NIF_TERM atom_num_peer_dests; static ERL_NIF_TERM atom_num_pip; static ERL_NIF_TERM atom_num_psctp; @@ -5585,6 +5603,12 @@ ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, break; #endif +#if defined(SCTP_INITMSG) + case SOCKET_OPT_SCTP_INITMSG: + result = nsetopt_lvl_sctp_initmsg(env, descP, eVal); + break; +#endif + #if defined(SCTP_MAXSEG) case SOCKET_OPT_SCTP_MAXSEG: result = nsetopt_lvl_sctp_maxseg(env, descP, eVal); @@ -5745,8 +5769,8 @@ ERL_NIF_TERM nsetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, #if defined(SCTP_EVENTS) static ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, - SocketDescriptor* descP, - ERL_NIF_TERM eVal) + SocketDescriptor* descP, + ERL_NIF_TERM eVal) { ERL_NIF_TERM result; ERL_NIF_TERM eDataIn, eAssoc, eAddr, eSndFailure; @@ -5838,6 +5862,90 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, #endif +/* nsetopt_lvl_sctp_initmsg - Level SCTP INITMSG option + */ +#if defined(SCTP_INITMSG) +static +ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + ERL_NIF_TERM eNumOut, eMaxIn, eMaxAttempts, eMaxInitTO; + struct sctp_initmsg initMsg; + int res; + size_t sz; + unsigned int tmp; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_initmsg -> entry with" + "\r\n eVal: %T" + "\r\n", eVal) ); + + // It must be a map + if (!IS_MAP(env, eVal)) + return esock_make_error(env, esock_atom_einval); + + // It must have atleast ten attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz < 4)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_initmsg -> extract attributes\r\n") ); + + if (!GET_MAP_VAL(env, eVal, atom_num_outstreams, &eNumOut)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_max_instreams, &eMaxIn)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_max_attempts, &eMaxAttempts)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_max_init_timeo, &eMaxInitTO)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_initmsg -> decode attributes\r\n") ); + + if (!GET_UINT(env, eNumOut, &tmp)) + return esock_make_error(env, esock_atom_einval); + initMsg.sinit_num_ostreams = (uint16_t) tmp; + + if (!GET_UINT(env, eMaxIn, &tmp)) + return esock_make_error(env, esock_atom_einval); + initMsg.sinit_max_instreams = (uint16_t) tmp; + + if (!GET_UINT(env, eMaxAttempts, &tmp)) + return esock_make_error(env, esock_atom_einval); + initMsg.sinit_max_attempts = (uint16_t) tmp; + + if (!GET_UINT(env, eMaxInitTO, &tmp)) + return esock_make_error(env, esock_atom_einval); + initMsg.sinit_max_init_timeo = (uint16_t) tmp; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_initmsg -> set initmsg option\r\n") ); + + res = socket_setopt(descP->sock, IPPROTO_SCTP, SCTP_INITMSG, + &initMsg, sizeof(initMsg)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_initmsg -> done with" + "\r\n result: %T" + "\r\n", result) ); + + return result; + +} +#endif + + /* nsetopt_lvl_sctp_maxseg - Level SCTP MAXSEG option */ #if defined(SCTP_MAXSEG) @@ -7614,6 +7722,12 @@ ERL_NIF_TERM ngetopt_lvl_sctp(ErlNifEnv* env, break; #endif +#if defined(SCTP_INITMSG) + case SOCKET_OPT_SCTP_INITMSG: + result = ngetopt_lvl_sctp_initmsg(env, descP); + break; +#endif + #if defined(SCTP_MAXSEG) case SOCKET_OPT_SCTP_MAXSEG: result = ngetopt_lvl_sctp_maxseg(env, descP); @@ -7677,7 +7791,7 @@ ERL_NIF_TERM ngetopt_lvl_sctp_associnfo(ErlNifEnv* env, if (res != 0) { result = esock_make_error_errno(env, sock_errno()); } else { - ERL_NIF_TERM eAssocParams; + ERL_NIF_TERM eAssocParams; ERL_NIF_TERM keys[] = {atom_assoc_id, atom_max_rxt, atom_num_peer_dests, atom_peer_rwnd, atom_local_rwnd, atom_cookie_life}; ERL_NIF_TERM vals[] = {MKUI(env, val.sasoc_assoc_id), @@ -7732,6 +7846,56 @@ ERL_NIF_TERM ngetopt_lvl_sctp_disable_fragments(ErlNifEnv* env, #endif +/* ngetopt_lvl_sctp_initmsg - Level SCTP INITMSG option + * + */ +#if defined(SCTP_INITMSG) +static +ERL_NIF_TERM ngetopt_lvl_sctp_initmsg(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + struct sctp_initmsg val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + SSDBG( descP, ("SOCKET", "ngetopt_lvl_sctp_initmsg -> entry\r\n") ); + + sys_memzero((char*) &val, valSz); + res = sock_getopt(descP->sock, IPPROTO_SCTP, SCTP_INITMSG, &val, &valSz); + + if (res != 0) { + result = esock_make_error_errno(env, sock_errno()); + } else { + ERL_NIF_TERM eInitMsg; + ERL_NIF_TERM keys[] = {atom_num_outstreams, atom_max_instreams, + atom_max_attempts, atom_max_init_timeo}; + ERL_NIF_TERM vals[] = {MKUI(env, val.sinit_num_ostreams), + MKUI(env, val.sinit_max_instreams), + MKUI(env, val.sinit_max_attempts), + MKUI(env, val.sinit_max_init_timeo)}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &eInitMsg)) + return esock_make_error(env, esock_atom_einval);; + + result = esock_make_ok2(env, eInitMsg); + } + + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_sctp_initmsg -> done with" + "\r\n res: %d" + "\r\n result: %T" + "\r\n", res, result) ); + + return result; +} +#endif + + /* ngetopt_lvl_sctp_maxseg - Level SCTP MAXSEG option */ #if defined(SCTP_MAXSEG) @@ -10050,6 +10214,9 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_interface = MKA(env, str_interface); atom_local_rwnd = MKA(env, str_local_rwnd); atom_max = MKA(env, str_max); + atom_max_attempts = MKA(env, str_max_attempts); + atom_max_init_timeo = MKA(env, str_max_init_timeo); + atom_max_instreams = MKA(env, str_max_instreams); atom_max_rxt = MKA(env, str_max_rxt); atom_min = MKA(env, str_min); atom_multiaddr = MKA(env, str_multiaddr); @@ -10057,6 +10224,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_num_dinet = MKA(env, str_num_dinet); atom_num_dinet6 = MKA(env, str_num_dinet6); atom_num_dlocal = MKA(env, str_num_dlocal); + atom_num_outstreams = MKA(env, str_num_outstreams); atom_num_peer_dests = MKA(env, str_num_peer_dests); atom_num_pip = MKA(env, str_num_pip); atom_num_psctp = MKA(env, str_num_psctp); -- cgit v1.2.3 From 3f1d17f3031b71ca6ff1f8e051859ad55e55822b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 12:28:19 +0200 Subject: [socket-nif] Add support for socket (level socket) option(s) [rcv|snd]timeo Added support for socket level socket option RCVTIMEO and SNDTIMEO. These are both a little strange, at least on linux. See the man pages for more info. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 16 ++- erts/emulator/nifs/common/socket_nif.c | 233 +++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+), 6 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 28f42a7345..2d5049a9eb 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -152,6 +152,7 @@ extern ERL_NIF_TERM esock_atom_einval; #define MKA(E,S) enif_make_atom((E), (S)) #define MKBIN(E,B) enif_make_binary((E), (B)) #define MKI(E,I) enif_make_int((E), (I)) +#define MKL(E,L) enif_make_long((E), (L)) #define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) #define MKEL(E) enif_make_list((E), 0) #define MKMA(E,KA,VA,L,M) enif_make_map_from_arrays((E), (KA), (VA), (L), (M)) @@ -167,7 +168,8 @@ extern ERL_NIF_TERM esock_atom_einval; #define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) #define MKTA(E, A, AL) enif_make_tuple_from_array((E), (A), (AL)) -#define MKUI(E,I) enif_make_uint((E), (I)) +#define MKUI(E,UI) enif_make_uint((E), (UI)) +#define MKUL(E,UL) enif_make_ulong((E), (UL)) #define MCREATE(N) enif_mutex_create((N)) #define MDESTROY(M) enif_mutex_destroy((M)) @@ -194,16 +196,18 @@ extern ERL_NIF_TERM esock_atom_einval; enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) #define GET_ATOM(E, TE, BP, MAX) \ enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) -#define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) -#define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) +#define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) +#define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) #define GET_LIST_ELEM(E, L, HP, TP) enif_get_list_cell((E), (L), (HP), (TP)) #define GET_LIST_LEN(E, L, LP) enif_get_list_length((E), (L), (LP)) +#define GET_LONG(E, TE, LP) enif_get_long((E), (TE), (LP)) #define GET_LPID(E, T, P) enif_get_local_pid((E), (T), (P)) #define GET_STR(E, L, B, SZ) \ enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) -#define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) -#define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) -#define GET_MAP_VAL(E, M, K, V) enif_get_map_value((E), (M), (K), (V)) +#define GET_UINT(E, TE, UIP) enif_get_uint((E), (TE), (UIP)) +#define GET_ULONG(E, TE, ULP) enif_get_long((E), (TE), (ULP)) +#define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) +#define GET_MAP_VAL(E, M, K, V) enif_get_map_value((E), (M), (K), (V)) #define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) #define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index e8e1df0842..53122ab957 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -502,9 +502,11 @@ typedef union { #define SOCKET_OPT_SOCK_PRIORITY 17 #define SOCKET_OPT_SOCK_PROTOCOL 18 #define SOCKET_OPT_SOCK_RCVBUF 19 +#define SOCKET_OPT_SOCK_RCVTIMEO 22 #define SOCKET_OPT_SOCK_REUSEADDR 23 #define SOCKET_OPT_SOCK_REUSEPORT 24 #define SOCKET_OPT_SOCK_SNDBUF 27 +#define SOCKET_OPT_SOCK_SNDTIMEO 30 #define SOCKET_OPT_SOCK_TYPE 32 #define SOCKET_OPT_IP_ADD_MEMBERSHIP 1 @@ -1010,6 +1012,11 @@ static ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SO_RCVTIMEO) +static ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SO_REUSEADDR) static ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, SocketDescriptor* descP, @@ -1025,6 +1032,11 @@ static ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SO_SNDTIMEO) +static ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, @@ -1339,6 +1351,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_RCVTIMEO) +static ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_REUSEADDR) static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, SocketDescriptor* descP); @@ -1351,6 +1367,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_SNDTIMEO) +static ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_TYPE) static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, SocketDescriptor* descP); @@ -1509,6 +1529,11 @@ static ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, int level, int opt, ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); static ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, SocketDescriptor* descP, @@ -1523,6 +1548,10 @@ static ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, SocketDescriptor* descP, int level, int opt); +static ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt); static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -1864,6 +1893,7 @@ static char str_partial_delivery[] = "partial_delivery"; static char str_peer_error[] = "peer_error"; static char str_peer_rwnd[] = "peer_rwnd"; static char str_probe[] = "probe"; +static char str_sec[] = "sec"; static char str_select[] = "select"; static char str_sender_dry[] = "sender_dry"; static char str_send_failure[] = "send_failure"; @@ -1871,6 +1901,7 @@ static char str_shutdown[] = "shutdown"; static char str_sourceaddr[] = "sourceaddr"; static char str_timeout[] = "timeout"; static char str_true[] = "true"; +static char str_usec[] = "usec"; static char str_want[] = "want"; static char str_lowdelay[] = "lowdelay"; @@ -1973,6 +2004,7 @@ static ERL_NIF_TERM atom_partial_delivery; static ERL_NIF_TERM atom_peer_error; static ERL_NIF_TERM atom_peer_rwnd; static ERL_NIF_TERM atom_probe; +static ERL_NIF_TERM atom_sec; static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_sender_dry; static ERL_NIF_TERM atom_send_failure; @@ -1980,6 +2012,7 @@ static ERL_NIF_TERM atom_shutdown; static ERL_NIF_TERM atom_sourceaddr; static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_usec; static ERL_NIF_TERM atom_want; static ERL_NIF_TERM atom_lowdelay; @@ -4346,6 +4379,12 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_RCVTIMEO) + case SOCKET_OPT_SOCK_RCVTIMEO: + result = nsetopt_lvl_sock_rcvtimeo(env, descP, eVal); + break; +#endif + #if defined(SO_REUSEADDR) case SOCKET_OPT_SOCK_REUSEADDR: result = nsetopt_lvl_sock_reuseaddr(env, descP, eVal); @@ -4364,11 +4403,22 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_SNDTIMEO) + case SOCKET_OPT_SOCK_SNDTIMEO: + result = nsetopt_lvl_sock_sndtimeo(env, descP, eVal); + break; +#endif + default: result = esock_make_error(env, esock_atom_einval); break; } + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_socket -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -4500,6 +4550,17 @@ ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, #endif +#if defined(SO_RCVTIMEO) +static +ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_timeval_opt(env, descP, SOL_SOCKET, SO_RCVTIMEO, eVal); +} +#endif + + #if defined(SO_REUSEADDR) static ERL_NIF_TERM nsetopt_lvl_sock_reuseaddr(ErlNifEnv* env, @@ -4533,6 +4594,22 @@ ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, #endif +#if defined(SO_SNDTIMEO) +static +ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sock_sndtimeo -> entry with" + "\r\n eVal: %T" + "\r\n", eVal) ); + + return nsetopt_timeval_opt(env, descP, SOL_SOCKET, SO_SNDTIMEO, eVal); +} +#endif + + /* nsetopt_lvl_ip - Level *IP* option(s) */ @@ -6144,6 +6221,73 @@ ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, } +/* nsetopt_timeval_opt - set an option that has an (timeval) bool value + */ +static +ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + ERL_NIF_TERM eSec, eUSec; + struct timeval timeVal; + int res; + size_t sz; + + SSDBG( descP, + ("SOCKET", "nsetopt_timeval_opt -> entry with" + "\r\n eVal: %T" + "\r\n", eVal) ); + + // It must be a map + if (!IS_MAP(env, eVal)) + return esock_make_error(env, esock_atom_einval); + + // It must have atleast ten attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz < 2)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> extract attributes\r\n") ); + + if (!GET_MAP_VAL(env, eVal, atom_sec, &eSec)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_usec, &eUSec)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_timeval_opt -> decode attributes\r\n") ); + + if (!GET_LONG(env, eSec, &timeVal.tv_sec)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_LONG(env, eUSec, &timeVal.tv_usec)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_timeval_opt -> set timeval option\r\n") ); + + res = socket_setopt(descP->sock, level, opt, &timeVal, sizeof(timeVal)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + SSDBG( descP, + ("SOCKET", "nsetopt_timeval_opt -> done with" + "\r\n result: %T" + "\r\n", result) ); + + return result; + +} + + + static BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, int eLevel, @@ -6719,6 +6863,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_RCVTIMEO) + case SOCKET_OPT_SOCK_RCVTIMEO: + result = ngetopt_lvl_sock_rcvtimeo(env, descP); + break; +#endif + #if defined(SO_REUSEADDR) case SOCKET_OPT_SOCK_REUSEADDR: result = ngetopt_lvl_sock_reuseaddr(env, descP); @@ -6737,6 +6887,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_SNDTIMEO) + case SOCKET_OPT_SOCK_SNDTIMEO: + result = ngetopt_lvl_sock_sndtimeo(env, descP); + break; +#endif + #if defined(SO_TYPE) case SOCKET_OPT_SOCK_TYPE: result = ngetopt_lvl_sock_type(env, descP); @@ -6748,6 +6904,11 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; } + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_socket -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -6979,6 +7140,16 @@ ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, #endif +#if defined(SO_RCVTIMEO) +static +ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_timeval_opt(env, descP, SOL_SOCKET, SO_RCVTIMEO); +} +#endif + + #if defined(SO_REUSEADDR) static ERL_NIF_TERM ngetopt_lvl_sock_reuseaddr(ErlNifEnv* env, @@ -7009,6 +7180,16 @@ ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, #endif +#if defined(SO_SNDTIMEO) +static +ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_timeval_opt(env, descP, SOL_SOCKET, SO_SNDTIMEO); +} +#endif + + #if defined(SO_TYPE) static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, @@ -8037,6 +8218,56 @@ ERL_NIF_TERM ngetopt_int_opt(ErlNifEnv* env, +/* ngetopt_timeval_opt - get an timeval option + */ +static +ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt) +{ + ERL_NIF_TERM result; + struct timeval val; + SOCKOPTLEN_T valSz = sizeof(val); + int res; + + SSDBG( descP, + ("SOCKET", "ngetopt_timeval_opt -> entry with" + "\r\n level: %d" + "\r\n opt: %d" + "\r\n", level, opt) ); + + sys_memzero((char*) &val, valSz); + res = sock_getopt(descP->sock, level, opt, &val, &valSz); + + if (res != 0) { + result = esock_make_error_errno(env, sock_errno()); + } else { + ERL_NIF_TERM eTimeVal; + ERL_NIF_TERM keys[] = {atom_sec, atom_usec}; + ERL_NIF_TERM vals[] = {MKL(env, val.tv_sec), MKL(env, val.tv_usec)}; + + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &eTimeVal)) + return esock_make_error(env, esock_atom_einval);; + + result = esock_make_ok2(env, eTimeVal); + } + + SSDBG( descP, + ("SOCKET", "ngetopt_timeval_opt -> done when" + "\r\n result: %T" + "\r\n", result) ); + + return result; +} + + + /* ngetopt_str_opt - get an string option * * We provide the max size of the string. This is the @@ -10238,6 +10469,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_peer_rwnd = MKA(env, str_peer_rwnd); atom_peer_error = MKA(env, str_peer_error); atom_probe = MKA(env, str_probe); + atom_sec = MKA(env, str_sec); atom_select = MKA(env, str_select); atom_sender_dry = MKA(env, str_sender_dry); atom_send_failure = MKA(env, str_send_failure); @@ -10245,6 +10477,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_sourceaddr = MKA(env, str_sourceaddr); atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); + atom_usec = MKA(env, str_usec); atom_want = MKA(env, str_want); /* Global atom(s) */ -- cgit v1.2.3 From 75498c0dd7682bae7787c4e2cd8d2680fa2b9c45 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 13:00:22 +0200 Subject: [socket-nif] Add support for socket (level socket) option(s) [rcv|snd]lowat Added support for socket level socket option RCVLOWAT and SNDLOWAT. These are both a little strange, at least on Linux. See the man pages for more info. For instance, sndlowat cannot be set on Linux. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 53122ab957..365cbd541b 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -502,10 +502,12 @@ typedef union { #define SOCKET_OPT_SOCK_PRIORITY 17 #define SOCKET_OPT_SOCK_PROTOCOL 18 #define SOCKET_OPT_SOCK_RCVBUF 19 +#define SOCKET_OPT_SOCK_RCVLOWAT 21 #define SOCKET_OPT_SOCK_RCVTIMEO 22 #define SOCKET_OPT_SOCK_REUSEADDR 23 #define SOCKET_OPT_SOCK_REUSEPORT 24 #define SOCKET_OPT_SOCK_SNDBUF 27 +#define SOCKET_OPT_SOCK_SNDLOWAT 29 #define SOCKET_OPT_SOCK_SNDTIMEO 30 #define SOCKET_OPT_SOCK_TYPE 32 @@ -1012,6 +1014,11 @@ static ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SO_RCVLOWAT) +static ERL_NIF_TERM nsetopt_lvl_sock_rcvlowat(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SO_RCVTIMEO) static ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, SocketDescriptor* descP, @@ -1032,6 +1039,11 @@ static ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SO_SNDLOWAT) +static ERL_NIF_TERM nsetopt_lvl_sock_sndlowat(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(SO_SNDTIMEO) static ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, SocketDescriptor* descP, @@ -1351,6 +1363,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_RCVLOWAT) +static ERL_NIF_TERM ngetopt_lvl_sock_rcvlowat(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_RCVTIMEO) static ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, SocketDescriptor* descP); @@ -1367,6 +1383,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_reuseport(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_SNDLOWAT) +static ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_SNDTIMEO) static ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, SocketDescriptor* descP); @@ -4379,6 +4399,12 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_RCVLOWAT) + case SOCKET_OPT_SOCK_RCVLOWAT: + result = nsetopt_lvl_sock_rcvlowat(env, descP, eVal); + break; +#endif + #if defined(SO_RCVTIMEO) case SOCKET_OPT_SOCK_RCVTIMEO: result = nsetopt_lvl_sock_rcvtimeo(env, descP, eVal); @@ -4403,6 +4429,12 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_SNDLOWAT) + case SOCKET_OPT_SOCK_SNDLOWAT: + result = nsetopt_lvl_sock_sndlowat(env, descP, eVal); + break; +#endif + #if defined(SO_SNDTIMEO) case SOCKET_OPT_SOCK_SNDTIMEO: result = nsetopt_lvl_sock_sndtimeo(env, descP, eVal); @@ -4550,6 +4582,17 @@ ERL_NIF_TERM nsetopt_lvl_sock_rcvbuf(ErlNifEnv* env, #endif +#if defined(SO_RCVLOWAT) +static +ERL_NIF_TERM nsetopt_lvl_sock_rcvlowat(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVLOWAT, eVal); +} +#endif + + #if defined(SO_RCVTIMEO) static ERL_NIF_TERM nsetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, @@ -4594,6 +4637,17 @@ ERL_NIF_TERM nsetopt_lvl_sock_sndbuf(ErlNifEnv* env, #endif +#if defined(SO_SNDLOWAT) +static +ERL_NIF_TERM nsetopt_lvl_sock_sndlowat(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDLOWAT, eVal); +} +#endif + + #if defined(SO_SNDTIMEO) static ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, @@ -6863,6 +6917,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_RCVLOWAT) + case SOCKET_OPT_SOCK_RCVLOWAT: + result = ngetopt_lvl_sock_rcvlowat(env, descP); + break; +#endif + #if defined(SO_RCVTIMEO) case SOCKET_OPT_SOCK_RCVTIMEO: result = ngetopt_lvl_sock_rcvtimeo(env, descP); @@ -6887,6 +6947,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_SNDLOWAT) + case SOCKET_OPT_SOCK_SNDLOWAT: + result = ngetopt_lvl_sock_sndlowat(env, descP); + break; +#endif + #if defined(SO_SNDTIMEO) case SOCKET_OPT_SOCK_SNDTIMEO: result = ngetopt_lvl_sock_sndtimeo(env, descP); @@ -7140,6 +7206,16 @@ ERL_NIF_TERM ngetopt_lvl_sock_rcvbuf(ErlNifEnv* env, #endif +#if defined(SO_RCVLOWAT) +static +ERL_NIF_TERM ngetopt_lvl_sock_rcvlowat(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_RCVLOWAT); +} +#endif + + #if defined(SO_RCVTIMEO) static ERL_NIF_TERM ngetopt_lvl_sock_rcvtimeo(ErlNifEnv* env, @@ -7180,6 +7256,16 @@ ERL_NIF_TERM ngetopt_lvl_sock_sndbuf(ErlNifEnv* env, #endif +#if defined(SO_SNDLOWAT) +static +ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDLOWAT); +} +#endif + + #if defined(SO_SNDTIMEO) static ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, -- cgit v1.2.3 From 84f62ae80dd08874d0d5fbedc532605394e897c1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 14:01:14 +0200 Subject: [socket-nif] Add support for socket (level socket) option timestamp Added support for socket level socket option TIMESTAMP. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 365cbd541b..6df2020e5e 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -509,6 +509,7 @@ typedef union { #define SOCKET_OPT_SOCK_SNDBUF 27 #define SOCKET_OPT_SOCK_SNDLOWAT 29 #define SOCKET_OPT_SOCK_SNDTIMEO 30 +#define SOCKET_OPT_SOCK_TIMESTAMP 31 #define SOCKET_OPT_SOCK_TYPE 32 #define SOCKET_OPT_IP_ADD_MEMBERSHIP 1 @@ -1049,6 +1050,11 @@ static ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(SO_TIMESTAMP) +static ERL_NIF_TERM nsetopt_lvl_sock_timestamp(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif static ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, @@ -1391,6 +1397,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_sndlowat(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(SO_TIMESTAMP) +static ERL_NIF_TERM ngetopt_lvl_sock_timestamp(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(SO_TYPE) static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, SocketDescriptor* descP); @@ -4441,6 +4451,12 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_TIMESTAMP) + case SOCKET_OPT_SOCK_TIMESTAMP: + result = nsetopt_lvl_sock_timestamp(env, descP, eVal); + break; +#endif + default: result = esock_make_error(env, esock_atom_einval); break; @@ -4664,6 +4680,17 @@ ERL_NIF_TERM nsetopt_lvl_sock_sndtimeo(ErlNifEnv* env, #endif +#if defined(SO_TIMESTAMP) +static +ERL_NIF_TERM nsetopt_lvl_sock_timestamp(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_TIMESTAMP, eVal); +} +#endif + + /* nsetopt_lvl_ip - Level *IP* option(s) */ @@ -6959,6 +6986,12 @@ ERL_NIF_TERM ngetopt_lvl_socket(ErlNifEnv* env, break; #endif +#if defined(SO_TIMESTAMP) + case SOCKET_OPT_SOCK_TIMESTAMP: + result = ngetopt_lvl_sock_timestamp(env, descP); + break; +#endif + #if defined(SO_TYPE) case SOCKET_OPT_SOCK_TYPE: result = ngetopt_lvl_sock_type(env, descP); @@ -7276,6 +7309,16 @@ ERL_NIF_TERM ngetopt_lvl_sock_sndtimeo(ErlNifEnv* env, #endif +#if defined(SO_TIMESTAMP) +static +ERL_NIF_TERM ngetopt_lvl_sock_timestamp(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_TIMESTAMP); +} +#endif + + #if defined(SO_TYPE) static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, -- cgit v1.2.3 From 1c26ae984a79224ce64b40dbc7239bf9721bb096 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 20 Jul 2018 15:14:06 +0200 Subject: [socket-nif] Add support for socket (level ip) option freebind Added support for ip level socket option FREEBIND. Note that there is an option available on FreeBSD called IP_BINDANY, which seems to have similar properties (FREEBIND is *not* available on FreeBSD). There are some restrictions for this option though (which is not mentioned in the Linux man page). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 6df2020e5e..f319f171ad 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -517,6 +517,7 @@ typedef union { #define SOCKET_OPT_IP_BLOCK_SOURCE 3 #define SOCKET_OPT_IP_DROP_MEMBERSHIP 5 #define SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP 6 +#define SOCKET_OPT_IP_FREEBIND 7 #define SOCKET_OPT_IP_MINTTL 9 #define SOCKET_OPT_IP_MTU 11 #define SOCKET_OPT_IP_MTU_DISCOVER 12 @@ -1086,6 +1087,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_FREEBIND) +static ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_MINTTL) static ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, SocketDescriptor* descP, @@ -1408,6 +1414,10 @@ static ERL_NIF_TERM ngetopt_lvl_sock_type(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); +#if defined(IP_FREEBIND) +static ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_MINTTL) static ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, SocketDescriptor* descP); @@ -4738,6 +4748,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_FREEBIND) + case SOCKET_OPT_IP_FREEBIND: + result = nsetopt_lvl_ip_freebind(env, descP, eVal); + break; +#endif + #if defined(IP_MINTTL) case SOCKET_OPT_IP_MINTTL: result = nsetopt_lvl_ip_minttl(env, descP, eVal); @@ -4936,6 +4952,26 @@ ERL_NIF_TERM nsetopt_lvl_ip_drop_source_membership(ErlNifEnv* env, +/* nsetopt_lvl_ip_freebind - Level IP FREEBIND option + */ +#if defined(IP_FREEBIND) +static +ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_FREEBIND, eVal); +} +#endif + + + /* nsetopt_lvl_ip_minttl - Level IP MINTTL option */ #if defined(IP_MINTTL) @@ -7374,6 +7410,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, ERL_NIF_TERM result; switch (eOpt) { +#if defined(IP_FREEBIND) + case SOCKET_OPT_IP_FREEBIND: + result = ngetopt_lvl_ip_freebind(env, descP); + break; +#endif + #if defined(IP_MINTTL) case SOCKET_OPT_IP_MINTTL: result = ngetopt_lvl_ip_minttl(env, descP); @@ -7485,6 +7527,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_freebind - Level IP FREEBIND option + */ +#if defined(IP_FREEBIND) +static +ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_FREEBIND); +} +#endif + + /* ngetopt_lvl_ip_mtu - Level IP MTU option */ #if defined(IP_MTU) -- cgit v1.2.3 From 31ef72ceda0bf5bba902bf18f3b445950516d6af Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 10:57:25 +0200 Subject: [socket-nif] Add support for socket (level ip) option recvopts Added support for ip level socket option RECVOPTS. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 91 +++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f319f171ad..7e4504cd2d 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -527,6 +527,7 @@ typedef union { #define SOCKET_OPT_IP_MULTICAST_TTL 16 #define SOCKET_OPT_IP_NODEFRAG 17 #define SOCKET_OPT_IP_RECVIF 21 +#define SOCKET_OPT_IP_RECVOPTS 23 #define SOCKET_OPT_IP_RECVTOS 25 #define SOCKET_OPT_IP_RECVTTL 26 #define SOCKET_OPT_IP_ROUTER_ALERT 28 @@ -1127,6 +1128,16 @@ static ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_RECVIF) +static ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif +#if defined(IP_RECVOPTS) +static ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP, @@ -1450,6 +1461,14 @@ static ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_RECVIF) +static ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, + SocketDescriptor* descP); +#endif +#if defined(IP_RECVOPTS) +static ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP); @@ -4802,6 +4821,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVOPTS) + case SOCKET_OPT_IP_RECVOPTS: + result = nsetopt_lvl_ip_recvopts(env, descP, eVal); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = nsetopt_lvl_ip_recvtos(env, descP, eVal); @@ -5148,7 +5173,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, #endif -/* nsetopt_lvl_ip_recvif - Level IP RECVIFxs option +/* nsetopt_lvl_ip_recvif - Level IP RECVIF option */ #if defined(IP_RECVIF) static @@ -5167,6 +5192,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_recvopts - Level IP RECVOPTS option + */ +#if defined(IP_RECVOPTS) +static +ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_RECVOPTS, eVal); +} +#endif + + /* nsetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) @@ -6672,6 +6716,11 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "ngetopt_opt -> entry with" + "\r\n eOpt: %d" + "\r\n", eOpt) ); + switch (eOpt) { case SOCKET_OPT_OTP_DEBUG: result = ngetopt_otp_debug(env, descP); @@ -6686,6 +6735,11 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, break; } + SSDBG( descP, + ("SOCKET", "ngetopt_opt -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -7409,6 +7463,11 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, { ERL_NIF_TERM result; + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_ip -> entry with" + "\r\n eOpt: %d" + "\r\n", eOpt) ); + switch (eOpt) { #if defined(IP_FREEBIND) case SOCKET_OPT_IP_FREEBIND: @@ -7470,6 +7529,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVOPTS) + case SOCKET_OPT_IP_RECVOPTS: + result = ngetopt_lvl_ip_recvopts(env, descP); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = ngetopt_lvl_ip_recvtos(env, descP); @@ -7501,10 +7566,16 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, #endif default: + SSDBG( descP, ("SOCKET", "ngetopt_lvl_ip -> unknown opt %d\r\n", eOpt) ); result = esock_make_error(env, esock_atom_einval); break; } + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_ip -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -7742,6 +7813,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_recvopt - Level IP RECVOPTS option + */ +#if defined(IP_RECVOPTS) +static +ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_RECVOPTS); +} +#endif + + /* ngetopt_lvl_ip_recvttl - Level IP RECVTTL option */ #if defined(IP_RECVTTL) -- cgit v1.2.3 From 2f99a47953404a18965fe6fe434593ee851e8677 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 14:26:25 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option multicast_hops Added support for the IPv6 socket option MULTICAST_HOPS. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 7e4504cd2d..5ac190b3ec 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -540,6 +540,7 @@ typedef union { #define SOCKET_OPT_IPV6_HOPLIMIT 12 #define SOCKET_OPT_IPV6_MTU 17 #define SOCKET_OPT_IPV6_MTU_DISCOVER 18 +#define SOCKET_OPT_IPV6_MULTICAST_HOPS 19 #define SOCKET_OPT_IPV6_V6ONLY 33 #define SOCKET_OPT_TCP_CONGESTION 1 @@ -1216,6 +1217,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_MULTICAST_HOPS) +static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP, @@ -1505,6 +1511,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_MULTICAST_HOPS) +static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP); @@ -5508,6 +5518,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MULTICAST_HOPS) + case SOCKET_OPT_IPV6_MULTICAST_HOPS: + result = nsetopt_lvl_ipv6_multicast_hops(env, descP, eVal); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = nsetopt_lvl_ipv6_v6only(env, descP, eVal); @@ -5605,6 +5621,18 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, #endif +#if defined(IPV6_MULTICAST_HOPS) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_HOPS, eVal); +} +#endif + + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, @@ -7950,6 +7978,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MULTICAST_HOPS) + case SOCKET_OPT_IPV6_MULTICAST_HOPS: + result = ngetopt_lvl_ipv6_multicast_hops(env, descP); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = ngetopt_lvl_ipv6_v6only(env, descP); @@ -8019,6 +8053,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, #endif +#if defined(IPV6_MULTICAST_HOPS) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_HOPS); +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, -- cgit v1.2.3 From c077834b1465a8285f0c18e1d164c812aaadac9c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 15:02:39 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option multicast_if Added support for the IPv6 socket option MULTICAST_IF. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 5ac190b3ec..632b3469fa 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -541,6 +541,7 @@ typedef union { #define SOCKET_OPT_IPV6_MTU 17 #define SOCKET_OPT_IPV6_MTU_DISCOVER 18 #define SOCKET_OPT_IPV6_MULTICAST_HOPS 19 +#define SOCKET_OPT_IPV6_MULTICAST_IF 20 #define SOCKET_OPT_IPV6_V6ONLY 33 #define SOCKET_OPT_TCP_CONGESTION 1 @@ -1222,6 +1223,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_MULTICAST_IF) +static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP, @@ -1515,6 +1521,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_MULTICAST_IF) +static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP); @@ -5524,6 +5534,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MULTICAST_IF) + case SOCKET_OPT_IPV6_MULTICAST_IF: + result = nsetopt_lvl_ipv6_multicast_if(env, descP, eVal); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = nsetopt_lvl_ipv6_v6only(env, descP, eVal); @@ -5633,6 +5649,18 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, +#if defined(IPV6_MULTICAST_IF) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_IF, eVal); +} +#endif + + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, @@ -7984,6 +8012,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MULTICAST_IF) + case SOCKET_OPT_IPV6_MULTICAST_IF: + result = ngetopt_lvl_ipv6_multicast_if(env, descP); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = ngetopt_lvl_ipv6_v6only(env, descP); @@ -8063,6 +8097,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, #endif +#if defined(IPV6_MULTICAST_IF) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_IF); +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, -- cgit v1.2.3 From 4d14b84183c3c17f0ec03bf3631f1fd8575f07b9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 17:36:16 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option multicast_loop Added support for the IPv6 socket option MULTICAST_LOOP. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 632b3469fa..218ed66f52 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -542,6 +542,7 @@ typedef union { #define SOCKET_OPT_IPV6_MTU_DISCOVER 18 #define SOCKET_OPT_IPV6_MULTICAST_HOPS 19 #define SOCKET_OPT_IPV6_MULTICAST_IF 20 +#define SOCKET_OPT_IPV6_MULTICAST_LOOP 21 #define SOCKET_OPT_IPV6_V6ONLY 33 #define SOCKET_OPT_TCP_CONGESTION 1 @@ -1228,6 +1229,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_MULTICAST_LOOP) +static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP, @@ -1525,6 +1531,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_MULTICAST_LOOP) +static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP); @@ -5540,6 +5550,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MULTICAST_LOOP) + case SOCKET_OPT_IPV6_MULTICAST_LOOP: + result = nsetopt_lvl_ipv6_multicast_loop(env, descP, eVal); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = nsetopt_lvl_ipv6_v6only(env, descP, eVal); @@ -5661,6 +5677,18 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, +#if defined(IPV6_MULTICAST_LOOP) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_LOOP, eVal); +} +#endif + + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, @@ -8018,6 +8046,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_MULTICAST_LOOP) + case SOCKET_OPT_IPV6_MULTICAST_LOOP: + result = ngetopt_lvl_ipv6_multicast_loop(env, descP); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = ngetopt_lvl_ipv6_v6only(env, descP); @@ -8107,6 +8141,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, #endif +#if defined(IPV6_MULTICAST_LOOP) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_LOOP); +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, -- cgit v1.2.3 From de82310c32063b8556add3fe7cf62b26eef27841 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 23 Jul 2018 18:26:42 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option recvpktinfo Added support for the IPv6 socket option RECVPKTINFO. This option is called PKTINFO on FreeBSD, so that value will also be accepted. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 56 +++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 218ed66f52..35997c359d 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -543,7 +543,8 @@ typedef union { #define SOCKET_OPT_IPV6_MULTICAST_HOPS 19 #define SOCKET_OPT_IPV6_MULTICAST_IF 20 #define SOCKET_OPT_IPV6_MULTICAST_LOOP 21 -#define SOCKET_OPT_IPV6_V6ONLY 33 +#define SOCKET_OPT_IPV6_RECVPKTINFO 25 // PKTINFO on FreeBSD +#define SOCKET_OPT_IPV6_V6ONLY 32 #define SOCKET_OPT_TCP_CONGESTION 1 #define SOCKET_OPT_TCP_CORK 2 @@ -1234,6 +1235,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) +static ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP, @@ -1535,6 +1541,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) +static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP); @@ -5556,6 +5566,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) + case SOCKET_OPT_IPV6_RECVPKTINFO: + result = nsetopt_lvl_ipv6_recvpktinfo(env, descP, eVal); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = nsetopt_lvl_ipv6_v6only(env, descP, eVal); @@ -5688,6 +5704,22 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, #endif +#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(IPV6_RECVPKTINFO) + int opt = IPV6_RECVPKTINFO; +#else + int opt = IPV6_PKTINFO; +#endif + + return nsetopt_bool_opt(env, descP, SOL_IPV6, opt, eVal); +} +#endif + #if defined(IPV6_V6ONLY) static @@ -8052,6 +8084,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) + case SOCKET_OPT_IPV6_RECVPKTINFO: + result = ngetopt_lvl_ipv6_recvpktinfo(env, descP); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = ngetopt_lvl_ipv6_v6only(env, descP); @@ -8151,6 +8189,22 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, #endif +#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(IPV6_RECVPKTINFO) + int opt = IPV6_RECVPKTINFO; +#else + int opt = IPV6_PKTINFO; +#endif + + return ngetopt_bool_opt(env, descP, SOL_IPV6, opt); +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, -- cgit v1.2.3 From d7ca5ba9d8a7f094037878acfecb52b0bfbacc2e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 10:25:25 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option rthdr Added support for the IPv6 socket option RTHDR. On FreeBSD this option requires superuser privileges to update. There is no mention of this on linux, but its still not possible to update (einval), so I assume that its the same there. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 71 ++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 3 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 35997c359d..f4ac43a9cd 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -544,6 +544,7 @@ typedef union { #define SOCKET_OPT_IPV6_MULTICAST_IF 20 #define SOCKET_OPT_IPV6_MULTICAST_LOOP 21 #define SOCKET_OPT_IPV6_RECVPKTINFO 25 // PKTINFO on FreeBSD +#define SOCKET_OPT_IPV6_RTHDR 28 #define SOCKET_OPT_IPV6_V6ONLY 32 #define SOCKET_OPT_TCP_CONGESTION 1 @@ -1240,6 +1241,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_RTHDR) +static ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP, @@ -1545,6 +1551,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_RTHDR) +static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP); @@ -4323,9 +4333,10 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nsetopt_native -> entry with" - "\r\n opt: %d" - "\r\n eVal: %T" - "\r\n", opt, eVal) ); + "\r\n level: %d" + "\r\n opt: %d" + "\r\n eVal: %T" + "\r\n", level, opt, eVal) ); if (GET_BIN(env, eVal, &val)) { int res = socket_setopt(descP->sock, level, opt, @@ -4338,6 +4349,11 @@ ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, result = esock_make_error(env, esock_atom_einval); } + SSDBG( descP, + ("SOCKET", "nsetopt_native -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -4393,10 +4409,17 @@ ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, #endif default: + SSDBG( descP, + ("SOCKET", "nsetopt_level -> unknown level (%d)\r\n", level) ); result = esock_make_error(env, esock_atom_einval); break; } + SSDBG( descP, + ("SOCKET", "nsetopt_level -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -4527,6 +4550,8 @@ ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, #endif default: + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_socket -> unknown opt (%d)\r\n", eOpt) ); result = esock_make_error(env, esock_atom_einval); break; } @@ -5572,6 +5597,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_RTHDR) + case SOCKET_OPT_IPV6_RTHDR: + result = nsetopt_lvl_ipv6_rthdr(env, descP, eVal); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = nsetopt_lvl_ipv6_v6only(env, descP, eVal); @@ -5579,10 +5610,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, #endif default: + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6 -> unknown opt (%d)\r\n", eOpt) ); result = esock_make_error(env, esock_atom_einval); break; } + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6 -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -5721,6 +5759,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, #endif +#if defined(IPV6_RTHDR) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_RTHDR, eVal); +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, @@ -8090,6 +8139,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_RTHDR) + case SOCKET_OPT_IPV6_RTHDR: + result = ngetopt_lvl_ipv6_rthdr(env, descP); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = ngetopt_lvl_ipv6_v6only(env, descP); @@ -8205,6 +8260,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, #endif +#if defined(IPV6_RTHDR) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_RTHDR); +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, -- cgit v1.2.3 From 8b61022bdca354e541380391e3b0b2606f7af3f8 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 11:08:20 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option authhdr & hopopts Added support for the IPv6 socket option(s) AUTHHDR and HOPOPTS. Its possible that the option is AUTHHDR is obsolete. It says so in the include files and when trying to get it (getsockopt) it returns with enoprotoopt. The option HOPOPTS returns with einval when calling setsockopt, so either you need to be a privileged user to update, or its not actually possible to update this option (even though it says nothing about that in the man page. It only talks about set). This is the same behaviour as with RTHDR and HOPLIMIT. On FreeBSD, it says that HOPOPTS requires superuser privileges. Needs furher checking. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 102 ++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f4ac43a9cd..42a9bc3cb6 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -536,8 +536,10 @@ typedef union { #define SOCKET_OPT_IP_UNBLOCK_SOURCE 33 #define SOCKET_OPT_IPV6_ADD_MEMBERSHIP 2 +#define SOCKET_OPT_IPV6_AUTHHDR 3 #define SOCKET_OPT_IPV6_DROP_MEMBERSHIP 6 #define SOCKET_OPT_IPV6_HOPLIMIT 12 +#define SOCKET_OPT_IPV6_HOPOPTS 13 #define SOCKET_OPT_IPV6_MTU 17 #define SOCKET_OPT_IPV6_MTU_DISCOVER 18 #define SOCKET_OPT_IPV6_MULTICAST_HOPS 19 @@ -1201,6 +1203,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_AUTHHDR) +static ERL_NIF_TERM nsetopt_lvl_ipv6_authhdr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_DROP_MEMBERSHIP) static ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, SocketDescriptor* descP, @@ -1211,6 +1218,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_HOPOPTS) +static ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_MTU) static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, SocketDescriptor* descP, @@ -1523,10 +1535,18 @@ static ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); +#if defined(IPV6_AUTHHDR) +static ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_HOPOPTS) +static ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_MTU) static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, SocketDescriptor* descP); @@ -1553,7 +1573,7 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, #endif #if defined(IPV6_RTHDR) static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, - SocketDescriptor* descP); + SocketDescriptor* descP); #endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, @@ -5549,6 +5569,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_AUTHHDR) + case SOCKET_OPT_IPV6_AUTHHDR: + result = nsetopt_lvl_ipv6_authhdr(env, descP, eVal); + break; +#endif + #if defined(IPV6_DROP_MEMBERSHIP) case SOCKET_OPT_IPV6_DROP_MEMBERSHIP: result = nsetopt_lvl_ipv6_drop_membership(env, descP, eVal); @@ -5561,6 +5587,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_HOPOPTS) + case SOCKET_OPT_IPV6_HOPOPTS: + result = nsetopt_lvl_ipv6_hopopts(env, descP, eVal); + break; +#endif + #if defined(IPV6_MTU) case SOCKET_OPT_IPV6_MTU: result = nsetopt_lvl_ipv6_mtu(env, descP, eVal); @@ -5637,6 +5669,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, #endif +#if defined(IPV6_AUTHHDR) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_authhdr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_AUTHHDR, eVal); +} +#endif + + #if defined(IPV6_DROP_MEMBERSHIP) static ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, @@ -5660,6 +5703,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, #endif +#if defined(IPV6_HOPOPTS) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_HOPOPTS, eVal); +} +#endif + + #if defined(IPV6_MTU) static ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, @@ -8097,12 +8151,24 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, "\r\n", eOpt) ); switch (eOpt) { +#if defined(IPV6_AUTHHDR) + case SOCKET_OPT_IPV6_AUTHHDR: + result = ngetopt_lvl_ipv6_authhdr(env, descP); + break; +#endif + #if defined(IPV6_HOPLIMIT) case SOCKET_OPT_IPV6_HOPLIMIT: result = ngetopt_lvl_ipv6_hoplimit(env, descP); break; #endif +#if defined(IPV6_HOPOPTS) + case SOCKET_OPT_IPV6_HOPOPTS: + result = ngetopt_lvl_ipv6_hopopts(env, descP); + break; +#endif + #if defined(IPV6_MTU) case SOCKET_OPT_IPV6_MTU: result = ngetopt_lvl_ipv6_mtu(env, descP); @@ -8165,6 +8231,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, } +#if defined(IPV6_AUTHHDR) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_AUTHHDR); +} +#endif + + #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, @@ -8175,6 +8251,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, #endif +#if defined(IPV6_HOPOPTS) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_HOPOPTS); +} +#endif + + #if defined(IPV6_MTU) static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, @@ -8707,6 +8793,13 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, SOCKOPTLEN_T valSz = sizeof(val); int res; + /* + SSDBG( descP, ("SOCKET", "ngetopt_bool_opt -> entry with" + "\r\n: level: %d" + "\r\n: opt: %d" + "\r\n", level, opt) ); + */ + res = sock_getopt(descP->sock, level, opt, &val, &valSz); if (res != 0) { @@ -8717,6 +8810,13 @@ ERL_NIF_TERM ngetopt_bool_opt(ErlNifEnv* env, result = esock_make_ok2(env, bval); } + /* + SSDBG( descP, ("SOCKET", "ngetopt_bool_opt -> done when" + "\r\n: res: %d" + "\r\n: result: %T" + "\r\n", res, result) ); + */ + return result; } -- cgit v1.2.3 From 9ca6de6efbe844bcf7dc996cfcf51bcd50325007 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 11:49:12 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option dstopts Added support for the IPv6 socket option(s) DSTOPTS. The option returns with einval when calling setsockopt, so either you need to be a privileged user to update, or its not actually possible to update this option (even though it says nothing about that in the man page. It only talks about set). This is the same behaviour as with RTHDR and HOPLIMIT. On FreeBSD, it says that HOPOPTS requires superuser privileges. Needs furher checking. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 42a9bc3cb6..9504643cb8 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -538,6 +538,7 @@ typedef union { #define SOCKET_OPT_IPV6_ADD_MEMBERSHIP 2 #define SOCKET_OPT_IPV6_AUTHHDR 3 #define SOCKET_OPT_IPV6_DROP_MEMBERSHIP 6 +#define SOCKET_OPT_IPV6_DSTOPTS 7 #define SOCKET_OPT_IPV6_HOPLIMIT 12 #define SOCKET_OPT_IPV6_HOPOPTS 13 #define SOCKET_OPT_IPV6_MTU 17 @@ -1213,6 +1214,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_DSTOPTS) +static ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP, @@ -1539,6 +1545,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_DSTOPTS) +static ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP); @@ -5581,6 +5591,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_DSTOPTS) + case SOCKET_OPT_IPV6_DSTOPTS: + result = nsetopt_lvl_ipv6_dstopts(env, descP, eVal); + break; +#endif + #if defined(IPV6_HOPLIMIT) case SOCKET_OPT_IPV6_HOPLIMIT: result = nsetopt_lvl_ipv6_hoplimit(env, descP, eVal); @@ -5692,6 +5708,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_drop_membership(ErlNifEnv* env, #endif +#if defined(IPV6_DSTOPTS) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_DSTOPTS, eVal); +} +#endif + + #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, @@ -8157,6 +8184,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_DSTOPTS) + case SOCKET_OPT_IPV6_DSTOPTS: + result = ngetopt_lvl_ipv6_dstopts(env, descP); + break; +#endif + #if defined(IPV6_HOPLIMIT) case SOCKET_OPT_IPV6_HOPLIMIT: result = ngetopt_lvl_ipv6_hoplimit(env, descP); @@ -8241,6 +8274,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, #endif +#if defined(IPV6_DSTOPTS) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_DSTOPTS); +} +#endif + + #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, -- cgit v1.2.3 From e54642b537177941ff361b1eebdec10e02cfc22d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 12:05:00 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option flowinfo Added support for the IPv6 socket option(s) FLOWINFO. The option returns with einval when calling setsockopt, so either you need to be a privileged user to update, or its not actually possible to update this option (even though it says nothing about that in the man page. It only talks about set). This is the same behaviour as with DSTOPTS. Needs furher checking. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 9504643cb8..6a2904f75f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -539,6 +539,7 @@ typedef union { #define SOCKET_OPT_IPV6_AUTHHDR 3 #define SOCKET_OPT_IPV6_DROP_MEMBERSHIP 6 #define SOCKET_OPT_IPV6_DSTOPTS 7 +#define SOCKET_OPT_IPV6_FLOWINFO 11 #define SOCKET_OPT_IPV6_HOPLIMIT 12 #define SOCKET_OPT_IPV6_HOPOPTS 13 #define SOCKET_OPT_IPV6_MTU 17 @@ -1219,6 +1220,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_FLOWINFO) +static ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP, @@ -1549,6 +1555,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_authhdr(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_FLOWINFO) +static ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP); @@ -5597,6 +5607,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_FLOWINFO) + case SOCKET_OPT_IPV6_FLOWINFO: + result = nsetopt_lvl_ipv6_flowinfo(env, descP, eVal); + break; +#endif + #if defined(IPV6_HOPLIMIT) case SOCKET_OPT_IPV6_HOPLIMIT: result = nsetopt_lvl_ipv6_hoplimit(env, descP, eVal); @@ -5719,6 +5735,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, #endif +#if defined(IPV6_FLOWINFO) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_FLOWINFO, eVal); +} +#endif + + #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, @@ -8190,6 +8217,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_FLOWINFO) + case SOCKET_OPT_IPV6_FLOWINFO: + result = ngetopt_lvl_ipv6_flowinfo(env, descP); + break; +#endif + #if defined(IPV6_HOPLIMIT) case SOCKET_OPT_IPV6_HOPLIMIT: result = ngetopt_lvl_ipv6_hoplimit(env, descP); @@ -8284,6 +8317,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, #endif +#if defined(IPV6_FLOWINFO) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_FLOWINFO); +} +#endif + + #if defined(IPV6_HOPLIMIT) static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, -- cgit v1.2.3 From b598160c2f1162658ea948284aee5b53951a3b9e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 12:29:22 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option unicast_hops Added support for the IPv6 socket option UNICAST_HOPS. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 6a2904f75f..5aba0bca03 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -549,6 +549,7 @@ typedef union { #define SOCKET_OPT_IPV6_MULTICAST_LOOP 21 #define SOCKET_OPT_IPV6_RECVPKTINFO 25 // PKTINFO on FreeBSD #define SOCKET_OPT_IPV6_RTHDR 28 +#define SOCKET_OPT_IPV6_UNICAST_HOPS 30 #define SOCKET_OPT_IPV6_V6ONLY 32 #define SOCKET_OPT_TCP_CONGESTION 1 @@ -1270,6 +1271,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_UNICAST_HOPS) +static ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP, @@ -1595,6 +1601,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_UNICAST_HOPS) +static ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP); @@ -5667,6 +5677,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_UNICAST_HOPS) + case SOCKET_OPT_IPV6_UNICAST_HOPS: + result = nsetopt_lvl_ipv6_unicast_hops(env, descP, eVal); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = nsetopt_lvl_ipv6_v6only(env, descP, eVal); @@ -5878,6 +5894,18 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, #endif +#if defined(IPV6_UNICAST_HOPS) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_UNICAST_HOPS, eVal); +} +#endif + + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, @@ -8277,6 +8305,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_UNICAST_HOPS) + case SOCKET_OPT_IPV6_UNICAST_HOPS: + result = ngetopt_lvl_ipv6_unicast_hops(env, descP); + break; +#endif + #if defined(IPV6_V6ONLY) case SOCKET_OPT_IPV6_V6ONLY: result = ngetopt_lvl_ipv6_v6only(env, descP); @@ -8442,6 +8476,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, #endif +#if defined(IPV6_UNICAST_HOPS) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_UNICAST_HOPS); +} +#endif + + #if defined(IPV6_V6ONLY) static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, -- cgit v1.2.3 From 7d5b6e7bf640eb5d64679e3bf7b440b8e21e3a4d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 15:03:36 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option router_alert Added support for the IPv6 socket option ROUTER_ALERT. Only supported for raw sockets. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 138 +++++++++++++++++++++++++++------ 1 file changed, 114 insertions(+), 24 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 5aba0bca03..4d78e89a4f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -470,6 +470,8 @@ typedef union { #define SOCKET_PROTOCOL_TCP 2 #define SOCKET_PROTOCOL_UDP 3 #define SOCKET_PROTOCOL_SCTP 4 +#define SOCKET_PROTOCOL_ICMP 5 +#define SOCKET_PROTOCOL_IGMP 6 /* shutdown how */ #define SOCKET_SHUTDOWN_HOW_RD 0 @@ -548,6 +550,7 @@ typedef union { #define SOCKET_OPT_IPV6_MULTICAST_IF 20 #define SOCKET_OPT_IPV6_MULTICAST_LOOP 21 #define SOCKET_OPT_IPV6_RECVPKTINFO 25 // PKTINFO on FreeBSD +#define SOCKET_OPT_IPV6_ROUTER_ALERT 27 #define SOCKET_OPT_IPV6_RTHDR 28 #define SOCKET_OPT_IPV6_UNICAST_HOPS 30 #define SOCKET_OPT_IPV6_V6ONLY 32 @@ -1266,6 +1269,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_ROUTER_ALERT) +static ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_RTHDR) static ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, SocketDescriptor* descP, @@ -1597,6 +1605,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_ROUTER_ALERT) +static ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_RTHDR) static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, SocketDescriptor* descP); @@ -1896,7 +1908,9 @@ static int compare_pids(ErlNifEnv* env, static BOOLEAN_T edomain2domain(int edomain, int* domain); static BOOLEAN_T etype2type(int etype, int* type); -static BOOLEAN_T eproto2proto(int eproto, int* proto); +static BOOLEAN_T eproto2proto(ErlNifEnv* env, + const ERL_NIF_TERM eproto, + int* proto); static BOOLEAN_T ehow2how(unsigned int ehow, int* how); static BOOLEAN_T esendflags2sendflags(unsigned int esendflags, int* sendflags); static BOOLEAN_T erecvflags2recvflags(unsigned int erecvflags, int* recvflags); @@ -2344,18 +2358,18 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, if ((argc != 4) || !GET_INT(env, argv[0], &edomain) || !GET_INT(env, argv[1], &etype) || - !GET_INT(env, argv[2], &eproto) || !IS_MAP(env, argv[3])) { return enif_make_badarg(env); } - emap = argv[3]; + eproto = argv[2]; + emap = argv[3]; SGDBG( ("SOCKET", "nif_open -> " "\r\n edomain: %T" "\r\n etype: %T" "\r\n eproto: %T" "\r\n extra: %T" - "\r\n", argv[0], argv[1], argv[2], argv[3]) ); + "\r\n", argv[0], argv[1], eproto, emap) ); if (!edomain2domain(edomain, &domain)) { SGDBG( ("SOCKET", "nif_open -> domain: %d\r\n", domain) ); @@ -2367,7 +2381,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, return esock_make_error(env, esock_atom_einval); } - if (!eproto2proto(eproto, &proto)) { + if (!eproto2proto(env, eproto, &proto)) { SGDBG( ("SOCKET", "nif_open -> protocol: %d\r\n", proto) ); return esock_make_error(env, esock_atom_einval); } @@ -5671,6 +5685,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_ROUTER_ALERT) + case SOCKET_OPT_IPV6_ROUTER_ALERT: + result = nsetopt_lvl_ipv6_router_alert(env, descP, eVal); + break; +#endif + #if defined(IPV6_RTHDR) case SOCKET_OPT_IPV6_RTHDR: result = nsetopt_lvl_ipv6_rthdr(env, descP, eVal); @@ -5883,6 +5903,18 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, #endif +#if defined(IPV6_ROUTER_ALERT) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_ROUTER_ALERT, eVal); +} +#endif + + + #if defined(IPV6_RTHDR) static ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, @@ -8299,6 +8331,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_ROUTER_ALERT) + case SOCKET_OPT_IPV6_ROUTER_ALERT: + result = ngetopt_lvl_ipv6_router_alert(env, descP); + break; +#endif + #if defined(IPV6_RTHDR) case SOCKET_OPT_IPV6_RTHDR: result = ngetopt_lvl_ipv6_rthdr(env, descP); @@ -8466,6 +8504,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, #endif +#if defined(IPV6_ROUTER_ALERT) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_ROUTER_ALERT); +} +#endif + + #if defined(IPV6_RTHDR) static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, @@ -8479,7 +8527,7 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, #if defined(IPV6_UNICAST_HOPS) static ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, - SocketDescriptor* descP) + SocketDescriptor* descP) { return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_UNICAST_HOPS); } @@ -10235,30 +10283,72 @@ BOOLEAN_T etype2type(int etype, int* type) * Note that only a subset is supported. */ static -BOOLEAN_T eproto2proto(int eproto, int* proto) +BOOLEAN_T eproto2proto(ErlNifEnv* env, + ERL_NIF_TERM eproto, + int* proto) { - switch (eproto) { - case SOCKET_PROTOCOL_IP: - *proto = IPPROTO_IP; - break; - - case SOCKET_PROTOCOL_TCP: - *proto = IPPROTO_TCP; - break; + if (IS_NUM(env, eproto)) { + int ep; - case SOCKET_PROTOCOL_UDP: - *proto = IPPROTO_UDP; - break; + if (!GET_INT(env, eproto, &ep)) { + *proto = -1; + return FALSE; + } + switch (ep) { + case SOCKET_PROTOCOL_IP: + *proto = IPPROTO_IP; + break; + + case SOCKET_PROTOCOL_TCP: + *proto = IPPROTO_TCP; + break; + + case SOCKET_PROTOCOL_UDP: + *proto = IPPROTO_UDP; + break; + #if defined(HAVE_SCTP) - case SOCKET_PROTOCOL_SCTP: - *proto = IPPROTO_SCTP; - break; + case SOCKET_PROTOCOL_SCTP: + *proto = IPPROTO_SCTP; + break; #endif + + case SOCKET_PROTOCOL_ICMP: + *proto = IPPROTO_ICMP; + break; + + case SOCKET_PROTOCOL_IGMP: + *proto = IPPROTO_IGMP; + break; + + default: + *proto = -2; + return FALSE; + } + } else { + const ERL_NIF_TERM* a; + int sz; - default: - *proto = -1; - return FALSE; + if (!GET_TUPLE(env, eproto, &sz, &a)) { + *proto = -3; + return FALSE; + } + + if (sz != 2) { + *proto = -4; + return FALSE; + } + + if (COMPARE(a[0], esock_atom_raw) != 0) { + *proto = -5; + return FALSE; + } + + if (!GET_INT(env, a[1], proto)) { + *proto = -6; + return FALSE; + } } return TRUE; -- cgit v1.2.3 From b9237c96b2b86c82bb128625cc532b3528222560 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 16:11:32 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option addrform Added support for the IPv6 socket option ADDRFORM. Only allowed for IPv6 sockets that are connected and bound to a v4-mapped-on-v6 address. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 54 ++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 4d78e89a4f..f7053d77fa 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -537,6 +537,7 @@ typedef union { #define SOCKET_OPT_IP_TTL 32 #define SOCKET_OPT_IP_UNBLOCK_SOURCE 33 +#define SOCKET_OPT_IPV6_ADDRFORM 1 #define SOCKET_OPT_IPV6_ADD_MEMBERSHIP 2 #define SOCKET_OPT_IPV6_AUTHHDR 3 #define SOCKET_OPT_IPV6_DROP_MEMBERSHIP 6 @@ -1204,6 +1205,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, ERL_NIF_TERM eVal); +#if defined(IPV6_ADDRFORM) +static ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_ADD_MEMBERSHIP) static ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, SocketDescriptor* descP, @@ -5607,6 +5613,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, "\r\n", eOpt) ); switch (eOpt) { +#if defined(IPV6_ADDRFORM) + case SOCKET_OPT_IPV6_ADDRFORM: + result = nsetopt_lvl_ipv6_addrform(env, descP, eVal); + break; +#endif + #if defined(IPV6_ADD_MEMBERSHIP) case SOCKET_OPT_IPV6_ADD_MEMBERSHIP: result = nsetopt_lvl_ipv6_add_membership(env, descP, eVal); @@ -5725,6 +5737,48 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, } +#if defined(IPV6_ADDRFORM) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_addrform(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + int res, edomain, domain; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6_addrform -> entry with" + "\r\n eVal: %T" + "\r\n", eVal) ); + + if (!GET_INT(env, eVal, &edomain)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6_addrform -> decode" + "\r\n edomain: %d" + "\r\n", edomain) ); + + if (!edomain2domain(edomain, &domain)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, ("SOCKET", "nsetopt_lvl_ipv6_addrform -> try set opt to %d\r\n", + domain) ); + + res = socket_setopt(descP->sock, + SOL_IPV6, IPV6_ADDRFORM, + &domain, sizeof(domain)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + return result; +} +#endif + + #if defined(IPV6_ADD_MEMBERSHIP) static ERL_NIF_TERM nsetopt_lvl_ipv6_add_membership(ErlNifEnv* env, -- cgit v1.2.3 From 00a2425bde77ddb9ae4c03b8c4e5470064773981 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 16:40:55 +0200 Subject: [socket-nif] Add support for socket (level ip) option recverr Added support for the IP socket option RECVERR. To actually make use of this option, we need the recvmsg function, which we don't have yet. Baby steps. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f7053d77fa..86a81c882b 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -528,6 +528,7 @@ typedef union { #define SOCKET_OPT_IP_MULTICAST_LOOP 15 #define SOCKET_OPT_IP_MULTICAST_TTL 16 #define SOCKET_OPT_IP_NODEFRAG 17 +#define SOCKET_OPT_IP_RECVERR 20 #define SOCKET_OPT_IP_RECVIF 21 #define SOCKET_OPT_IP_RECVOPTS 23 #define SOCKET_OPT_IP_RECVTOS 25 @@ -1142,6 +1143,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_RECVERR) +static ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_RECVIF) static ERL_NIF_TERM nsetopt_lvl_ip_recvif(ErlNifEnv* env, SocketDescriptor* descP, @@ -1535,6 +1541,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_RECVERR) +static ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_RECVIF) static ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, SocketDescriptor* descP); @@ -4950,6 +4960,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVERR) + case SOCKET_OPT_IP_RECVERR: + result = nsetopt_lvl_ip_recverr(env, descP, eVal); + break; +#endif + #if defined(IP_RECVIF) case SOCKET_OPT_IP_RECVIF: result = nsetopt_lvl_ip_recvif(env, descP, eVal); @@ -5308,6 +5324,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_recverr - Level IP RECVERR option + */ +#if defined(IP_RECVERR) +static +ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_RECVERR, eVal); +} +#endif + + /* nsetopt_lvl_ip_recvif - Level IP RECVIF option */ #if defined(IP_RECVIF) @@ -7910,6 +7945,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVERR) + case SOCKET_OPT_IP_RECVERR: + result = ngetopt_lvl_ip_recverr(env, descP); + break; +#endif + #if defined(IP_RECVIF) case SOCKET_OPT_IP_RECVIF: result = ngetopt_lvl_ip_recvif(env, descP); @@ -8200,6 +8241,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_recverr - Level IP RECVERR option + */ +#if defined(IP_RECVERR) +static +ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_RECVERR); +} +#endif + + /* ngetopt_lvl_ip_recvopt - Level IP RECVOPTS option */ #if defined(IP_RECVOPTS) -- cgit v1.2.3 From 4e24993aff4c5a0cb2bec96e5499131a660a79f9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 24 Jul 2018 17:01:46 +0200 Subject: [socket-nif] Add support for socket (level ipv6) option recverr Added support for the IPv6 socket option RECVERR. To actually make use of this option, we need the recvmsg function, which we don't have yet. Baby steps. OTP-14831. --- erts/emulator/nifs/common/socket_nif.c | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 86a81c882b..3f87232000 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -551,6 +551,7 @@ typedef union { #define SOCKET_OPT_IPV6_MULTICAST_HOPS 19 #define SOCKET_OPT_IPV6_MULTICAST_IF 20 #define SOCKET_OPT_IPV6_MULTICAST_LOOP 21 +#define SOCKET_OPT_IPV6_RECVERR 24 #define SOCKET_OPT_IPV6_RECVPKTINFO 25 // PKTINFO on FreeBSD #define SOCKET_OPT_IPV6_ROUTER_ALERT 27 #define SOCKET_OPT_IPV6_RTHDR 28 @@ -1276,6 +1277,11 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IPV6_RECVERR) +static ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) static ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP, @@ -1617,6 +1623,10 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IPV6_RECVERR) +static ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP); @@ -5726,6 +5736,12 @@ ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_RECVERR) + case SOCKET_OPT_IPV6_RECVERR: + result = nsetopt_lvl_ipv6_recverr(env, descP, eVal); + break; +#endif + #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) case SOCKET_OPT_IPV6_RECVPKTINFO: result = nsetopt_lvl_ipv6_recvpktinfo(env, descP, eVal); @@ -5975,6 +5991,17 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, #endif +#if defined(IPV6_RECVERR) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_RECVERR, eVal); +} +#endif + + #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) static ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, @@ -8438,6 +8465,12 @@ ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, break; #endif +#if defined(IPV6_RECVERR) + case SOCKET_OPT_IPV6_RECVERR: + result = ngetopt_lvl_ipv6_recverr(env, descP); + break; +#endif + #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) case SOCKET_OPT_IPV6_RECVPKTINFO: result = ngetopt_lvl_ipv6_recvpktinfo(env, descP); @@ -8601,6 +8634,16 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, #endif +#if defined(IPV6_RECVERR) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_RECVERR); +} +#endif + + #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, -- cgit v1.2.3 From 5d9de1cdc46c75117f15f1ab17f017cdb700eb4c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 11:28:49 +0200 Subject: [socket-nif] Add support for socket (level ip) option msfilter Added support for ip level socket option MSFILTER. This option has not been tested *in any way*... OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 173 ++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 3 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 3f87232000..ae14e6b4ab 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -521,6 +521,7 @@ typedef union { #define SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP 6 #define SOCKET_OPT_IP_FREEBIND 7 #define SOCKET_OPT_IP_MINTTL 9 +#define SOCKET_OPT_IP_MSFILTER 10 #define SOCKET_OPT_IP_MTU 11 #define SOCKET_OPT_IP_MTU_DISCOVER 12 #define SOCKET_OPT_IP_MULTICAST_ALL 13 @@ -1114,6 +1115,18 @@ static ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_MSFILTER) +static ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +static BOOLEAN_T decode_ip_msfilter_mode(ErlNifEnv* env, + ERL_NIF_TERM eVal, + uint32_t* mode); +static ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env, + SOCKET sock, + struct ip_msfilter* msfP, + SOCKLEN_T optLen); +#endif #if defined(IP_MTU_DISCOVER) static ERL_NIF_TERM nsetopt_lvl_ip_mtu_discover(ErlNifEnv* env, SocketDescriptor* descP, @@ -2058,13 +2071,15 @@ static char str_cookie_life[] = "cookie_life"; static char str_data_in[] = "data_in"; static char str_do[] = "do"; static char str_dont[] = "dont"; +static char str_exclude[] = "exclude"; static char str_false[] = "false"; static char str_global_counters[] = "global_counters"; static char str_in4_sockaddr[] = "in4_sockaddr"; static char str_in6_sockaddr[] = "in6_sockaddr"; -static char str_iow[] = "iow"; +static char str_include[] = "include"; static char str_initial[] = "initial"; static char str_interface[] = "interface"; +static char str_iow[] = "iow"; static char str_local_rwnd[] = "local_rwnd"; // static char str_loopback[] = "loopback"; static char str_max[] = "max"; @@ -2073,8 +2088,10 @@ static char str_max_init_timeo[] = "max_init_timeo"; static char str_max_instreams[] = "max_instreams"; static char str_max_rxt[] = "max_rxt"; static char str_min[] = "min"; +static char str_mode[] = "mode"; static char str_multiaddr[] = "multiaddr"; static char str_nif_abort[] = "nif_abort"; +static char str_null[] = "null"; static char str_num_dlocal[] = "num_domain_local"; static char str_num_dinet[] = "num_domain_inet"; static char str_num_dinet6[] = "num_domain_inet6"; @@ -2097,6 +2114,7 @@ static char str_select[] = "select"; static char str_sender_dry[] = "sender_dry"; static char str_send_failure[] = "send_failure"; static char str_shutdown[] = "shutdown"; +static char str_slist[] = "slist"; static char str_sourceaddr[] = "sourceaddr"; static char str_timeout[] = "timeout"; static char str_true[] = "true"; @@ -2170,13 +2188,15 @@ static ERL_NIF_TERM atom_cookie_life; static ERL_NIF_TERM atom_data_in; static ERL_NIF_TERM atom_do; static ERL_NIF_TERM atom_dont; +static ERL_NIF_TERM atom_exclude; static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_global_counters; static ERL_NIF_TERM atom_in4_sockaddr; static ERL_NIF_TERM atom_in6_sockaddr; -static ERL_NIF_TERM atom_iow; +static ERL_NIF_TERM atom_include; static ERL_NIF_TERM atom_initial; static ERL_NIF_TERM atom_interface; +static ERL_NIF_TERM atom_iow; static ERL_NIF_TERM atom_local_rwnd; static ERL_NIF_TERM atom_max; static ERL_NIF_TERM atom_max_attempts; @@ -2184,8 +2204,10 @@ static ERL_NIF_TERM atom_max_init_timeo; static ERL_NIF_TERM atom_max_instreams; static ERL_NIF_TERM atom_max_rxt; static ERL_NIF_TERM atom_min; +static ERL_NIF_TERM atom_mode; static ERL_NIF_TERM atom_multiaddr; static ERL_NIF_TERM atom_nif_abort; +static ERL_NIF_TERM atom_null; static ERL_NIF_TERM atom_num_dinet; static ERL_NIF_TERM atom_num_dinet6; static ERL_NIF_TERM atom_num_dlocal; @@ -2208,6 +2230,7 @@ static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_sender_dry; static ERL_NIF_TERM atom_send_failure; static ERL_NIF_TERM atom_shutdown; +static ERL_NIF_TERM atom_slist; static ERL_NIF_TERM atom_sourceaddr; static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; @@ -4934,6 +4957,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_MSFILTER) + case SOCKET_OPT_IP_MSFILTER: + result = nsetopt_lvl_ip_msfilter(env, descP, eVal); + break; +#endif + #if defined(IP_MTU_DISCOVER) case SOCKET_OPT_IP_MTU_DISCOVER: result = nsetopt_lvl_ip_mtu_discover(env, descP, eVal); @@ -5178,6 +5207,139 @@ ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, +/* nsetopt_lvl_ip_msfilter - Level IP MSFILTER option + * + * The value can be *either* the atom 'null' or a map of type ip_msfilter(). + */ +#if defined(IP_MSFILTER) +static +ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + + if (COMPARE(eVal, atom_null) == 0) { + return nsetopt_lvl_ip_msfilter_set(env, descP->sock, NULL, 0); + } else { + struct ip_msfilter* msfP; + uint32_t msfSz; + ERL_NIF_TERM eMultiAddr, eInterface, eFMode, eSList, elem, tail; + size_t sz; + unsigned int slistLen, idx; + + if (!IS_MAP(env, eVal)) + return esock_make_error(env, esock_atom_einval); + + // It must have atleast four attributes + if (!enif_get_map_size(env, eVal, &sz) || (sz < 4)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_mode, &eFMode)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_MAP_VAL(env, eVal, atom_slist, &eSList)) + return esock_make_error(env, esock_atom_einval); + + /* We start (decoding) with the slist, since without it we don't + * really know how much (memory) to allocate. + */ + if (!GET_LIST_LEN(env, eSList, &slistLen)) + return esock_make_error(env, esock_atom_einval); + + msfSz = IP_MSFILTER_SIZE(slistLen); + msfP = MALLOC(msfSz); + + if (!esock_decode_ip4_address(env, eMultiAddr, &msfP->imsf_multiaddr)) { + FREE(msfP); + return esock_make_error(env, esock_atom_einval); + } + + if (!esock_decode_ip4_address(env, eInterface, &msfP->imsf_interface)) { + FREE(msfP); + return esock_make_error(env, esock_atom_einval); + } + + if (!decode_ip_msfilter_mode(env, eFMode, &msfP->imsf_fmode)) { + FREE(msfP); + return esock_make_error(env, esock_atom_einval); + } + + /* And finally, extract the source addresses */ + msfP->imsf_numsrc = slistLen; + for (idx = 0; idx < slistLen; idx++) { + if (GET_LIST_ELEM(env, eSList, &elem, &tail)) { + if (!esock_decode_ip4_address(env, elem, &msfP->imsf_slist[idx])) { + FREE(msfP); + return esock_make_error(env, esock_atom_einval); + } else { + eSList = tail; + } + } + } + + /* And now, finally, set the option */ + result = nsetopt_lvl_ip_msfilter_set(env, descP->sock, msfP, msfSz); + FREE(msfP); + return result; + } + +} + + +static +BOOLEAN_T decode_ip_msfilter_mode(ErlNifEnv* env, + ERL_NIF_TERM eVal, + uint32_t* mode) +{ + BOOLEAN_T result; + + if (COMPARE(eVal, atom_include) == 0) { + *mode = MCAST_INCLUDE; + result = TRUE; + } else if (COMPARE(eVal, atom_exclude) == 0) { + *mode = MCAST_EXCLUDE; + result = TRUE; + } else { + result = FALSE; + } + + return result; +} + + +static +ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env, + SOCKET sock, + struct ip_msfilter* msfP, + SOCKLEN_T optLen) +{ + ERL_NIF_TERM result; + int res; +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + res = socket_setopt(sock, level, IP_MSFILTER, (void*) msfP, optLen); + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + return result; +} +#endif // IP_MSFILTER + + + /* nsetopt_lvl_ip_mtu_discover - Level IP MTU_DISCOVER option * * The value is an atom of the type ip_pmtudisc(). @@ -11441,13 +11603,15 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_data_in = MKA(env, str_data_in); atom_do = MKA(env, str_do); atom_dont = MKA(env, str_dont); + atom_exclude = MKA(env, str_exclude); atom_false = MKA(env, str_false); atom_global_counters = MKA(env, str_global_counters); atom_in4_sockaddr = MKA(env, str_in4_sockaddr); atom_in6_sockaddr = MKA(env, str_in6_sockaddr); - atom_iow = MKA(env, str_iow); + atom_include = MKA(env, str_include); atom_initial = MKA(env, str_initial); atom_interface = MKA(env, str_interface); + atom_iow = MKA(env, str_iow); atom_local_rwnd = MKA(env, str_local_rwnd); atom_max = MKA(env, str_max); atom_max_attempts = MKA(env, str_max_attempts); @@ -11455,8 +11619,10 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_max_instreams = MKA(env, str_max_instreams); atom_max_rxt = MKA(env, str_max_rxt); atom_min = MKA(env, str_min); + atom_mode = MKA(env, str_mode); atom_multiaddr = MKA(env, str_multiaddr); atom_nif_abort = MKA(env, str_nif_abort); + atom_null = MKA(env, str_null); atom_num_dinet = MKA(env, str_num_dinet); atom_num_dinet6 = MKA(env, str_num_dinet6); atom_num_dlocal = MKA(env, str_num_dlocal); @@ -11479,6 +11645,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_sender_dry = MKA(env, str_sender_dry); atom_send_failure = MKA(env, str_send_failure); atom_shutdown = MKA(env, str_shutdown); + atom_slist = MKA(env, str_slist); atom_sourceaddr = MKA(env, str_sourceaddr); atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); -- cgit v1.2.3 From 587d3a9a76b6ef2c88b850d007d39d34c37b5825 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 12:00:34 +0200 Subject: [socket-nif] Add support for socket (level ip) option hdrincl Added support for ip level socket option HDRINCL. As this option is raw only, it has not yet been tested! OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 60 ++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index ae14e6b4ab..19b40920d3 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -520,6 +520,7 @@ typedef union { #define SOCKET_OPT_IP_DROP_MEMBERSHIP 5 #define SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP 6 #define SOCKET_OPT_IP_FREEBIND 7 +#define SOCKET_OPT_IP_HDRINCL 8 #define SOCKET_OPT_IP_MINTTL 9 #define SOCKET_OPT_IP_MSFILTER 10 #define SOCKET_OPT_IP_MTU 11 @@ -1110,6 +1111,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_HDRINCL) +static ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_MINTTL) static ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, SocketDescriptor* descP, @@ -1528,6 +1534,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_HDRINCL) +static ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_MINTTL) static ERL_NIF_TERM ngetopt_lvl_ip_minttl(ErlNifEnv* env, SocketDescriptor* descP); @@ -4951,6 +4961,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_HDRINCL) + case SOCKET_OPT_IP_HDRINCL: + result = nsetopt_lvl_ip_hdrincl(env, descP, eVal); + break; +#endif + #if defined(IP_MINTTL) case SOCKET_OPT_IP_MINTTL: result = nsetopt_lvl_ip_minttl(env, descP, eVal); @@ -5187,6 +5203,26 @@ ERL_NIF_TERM nsetopt_lvl_ip_freebind(ErlNifEnv* env, +/* nsetopt_lvl_ip_hdrincl - Level IP HDRINCL option + */ +#if defined(IP_HDRINCL) +static +ERL_NIF_TERM nsetopt_lvl_ip_hdrincl(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_HDRINCL, eVal); +} +#endif + + + /* nsetopt_lvl_ip_minttl - Level IP MINTTL option */ #if defined(IP_MINTTL) @@ -8086,6 +8122,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_HDRINCL) + case SOCKET_OPT_IP_HDRINCL: + result = ngetopt_lvl_ip_hdrincl(env, descP); + break; +#endif + #if defined(IP_MINTTL) case SOCKET_OPT_IP_MINTTL: result = ngetopt_lvl_ip_minttl(env, descP); @@ -8233,6 +8275,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_freebind(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_hdrincl - Level IP HDRINCL option + */ +#if defined(IP_HDRINCL) +static +ERL_NIF_TERM ngetopt_lvl_ip_hdrincl(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_HDRINCL); +} +#endif + + /* ngetopt_lvl_ip_mtu - Level IP MTU option */ #if defined(IP_MTU) -- cgit v1.2.3 From 70a5b8d6a01b91a6044c6a5a0f8ed8919afd509b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 12:29:41 +0200 Subject: [socket-nif] Add support for socket (level ip) option pktinfo Added support for ip level socket option PKTINFO. This option requires sendmsg and/or recvmsg to actually use, so we cannot test this fully at the moment (although both set and get works). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 19b40920d3..f4da7cdbb0 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -530,6 +530,7 @@ typedef union { #define SOCKET_OPT_IP_MULTICAST_LOOP 15 #define SOCKET_OPT_IP_MULTICAST_TTL 16 #define SOCKET_OPT_IP_NODEFRAG 17 +#define SOCKET_OPT_IP_PKTINFO 19 #define SOCKET_OPT_IP_RECVERR 20 #define SOCKET_OPT_IP_RECVIF 21 #define SOCKET_OPT_IP_RECVOPTS 23 @@ -1163,6 +1164,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_PKTINFO) +static ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_RECVERR) static ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env, SocketDescriptor* descP, @@ -1570,6 +1576,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_multicast_ttl(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_PKTINFO) +static ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_RECVERR) static ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, SocketDescriptor* descP); @@ -5015,6 +5025,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_PKTINFO) + case SOCKET_OPT_IP_PKTINFO: + result = nsetopt_lvl_ip_pktinfo(env, descP, eVal); + break; +#endif + #if defined(IP_RECVERR) case SOCKET_OPT_IP_RECVERR: result = nsetopt_lvl_ip_recverr(env, descP, eVal); @@ -5532,6 +5548,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_nodefrag(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_pktinfo - Level IP PKTINFO option + */ +#if defined(IP_PKTINFO) +static +ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_PKTINFO, eVal); +} +#endif + + /* nsetopt_lvl_ip_recverr - Level IP RECVERR option */ #if defined(IP_RECVERR) @@ -8176,6 +8211,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_PKTINFO) + case SOCKET_OPT_IP_PKTINFO: + result = ngetopt_lvl_ip_pktinfo(env, descP); + break; +#endif + #if defined(IP_RECVERR) case SOCKET_OPT_IP_RECVERR: result = ngetopt_lvl_ip_recverr(env, descP); @@ -8454,6 +8495,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_pktinfo - Level IP PKTINFO option + */ +#if defined(IP_PKTINFO) +static +ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_PKTINFO); +} +#endif + + /* ngetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) -- cgit v1.2.3 From 673367a0c17349a8b57dfad5dbc349c68417c6a5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 13:07:34 +0200 Subject: [socket-nif] Add support for socket (level ip) option transparent Added support for ip level socket option TRANSPARENT. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 67 ++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f4da7cdbb0..9e9ac333e7 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -538,6 +538,7 @@ typedef union { #define SOCKET_OPT_IP_RECVTTL 26 #define SOCKET_OPT_IP_ROUTER_ALERT 28 #define SOCKET_OPT_IP_TOS 30 +#define SOCKET_OPT_IP_TRANSPARENT 31 #define SOCKET_OPT_IP_TTL 32 #define SOCKET_OPT_IP_UNBLOCK_SOURCE 33 @@ -1204,6 +1205,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_TRANSPARENT) +static ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_TTL) static ERL_NIF_TERM nsetopt_lvl_ip_ttl(ErlNifEnv* env, SocketDescriptor* descP, @@ -1608,6 +1614,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_TRANSPARENT) +static ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_TTL) static ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, SocketDescriptor* descP); @@ -5073,6 +5083,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_TRANSPARENT) + case SOCKET_OPT_IP_TRANSPARENT: + result = nsetopt_lvl_ip_transparent(env, descP, eVal); + break; +#endif + #if defined(IP_TTL) case SOCKET_OPT_IP_TTL: result = nsetopt_lvl_ip_ttl(env, descP, eVal); @@ -5086,10 +5102,16 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, #endif default: + SSDBG( descP, ("SOCKET", "nsetopt_lvl_ip -> unknown opt (%d)\r\n", eOpt) ); result = esock_make_error(env, esock_atom_einval); break; } + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ip -> done when" + "\r\n result: %T" + "\r\n", result) ); + return result; } @@ -5714,6 +5736,26 @@ ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_transparent - Level IP TRANSPARENT option + */ +#if defined(IP_TRANSPARENT) +static +ERL_NIF_TERM nsetopt_lvl_ip_transparent(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_TRANSPARENT, eVal); +} +#endif + + + /* nsetopt_lvl_ip_ttl - Level IP TTL option */ #if defined(IP_TTL) @@ -8259,6 +8301,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_TRANSPARENT) + case SOCKET_OPT_IP_TRANSPARENT: + result = ngetopt_lvl_ip_transparent(env, descP); + break; +#endif + #if defined(IP_TTL) case SOCKET_OPT_IP_TTL: result = ngetopt_lvl_ip_ttl(env, descP); @@ -8651,6 +8699,25 @@ ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_transparent - Level IP TRANSPARENT option + */ +#if defined(IP_TRANSPARENT) +static +ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_TRANSPARENT); +} +#endif + + + /* ngetopt_lvl_ip_ttl - Level IP TTL option */ #if defined(IP_TTL) -- cgit v1.2.3 From cb8877a5561ac64704337441936b62c8c87f8d13 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 25 Jul 2018 14:09:24 +0200 Subject: [socket-nif] Add support for socket (level ip) option retopts Added support for ip level socket option RETOPTS. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 9e9ac333e7..288188d2dd 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -536,6 +536,7 @@ typedef union { #define SOCKET_OPT_IP_RECVOPTS 23 #define SOCKET_OPT_IP_RECVTOS 25 #define SOCKET_OPT_IP_RECVTTL 26 +#define SOCKET_OPT_IP_RETOPTS 27 #define SOCKET_OPT_IP_ROUTER_ALERT 28 #define SOCKET_OPT_IP_TOS 30 #define SOCKET_OPT_IP_TRANSPARENT 31 @@ -1195,6 +1196,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_RETOPTS) +static ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_ROUTER_ALERT) static ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, SocketDescriptor* descP, @@ -1606,6 +1612,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_RETOPTS) +static ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_ROUTER_ALERT) static ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, SocketDescriptor* descP); @@ -5071,6 +5081,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RETOPTS) + case SOCKET_OPT_IP_RETOPTS: + result = nsetopt_lvl_ip_retopts(env, descP, eVal); + break; +#endif + #if defined(IP_ROUTER_ALERT) case SOCKET_OPT_IP_ROUTER_ALERT: result = nsetopt_lvl_ip_router_alert(env, descP, eVal); @@ -5684,6 +5700,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvttl(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_retopts - Level IP RETOPTS option + */ +#if defined(IP_RETOPTS) +static +ERL_NIF_TERM nsetopt_lvl_ip_retopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_RETOPTS, eVal); +} +#endif + + /* nsetopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option */ #if defined(IP_ROUTER_ALERT) @@ -8289,6 +8324,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RETOPTS) + case SOCKET_OPT_IP_RETOPTS: + result = ngetopt_lvl_ip_retopts(env, descP); + break; +#endif + #if defined(IP_ROUTER_ALERT) case SOCKET_OPT_IP_ROUTER_ALERT: result = ngetopt_lvl_ip_router_alert(env, descP); @@ -8651,6 +8692,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvttl(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_retopts - Level IP RETOPTS option + */ +#if defined(IP_RETOPTS) +static +ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_RETOPTS); +} +#endif + + /* ngetopt_lvl_ip_router_alert - Level IP ROUTER_ALERT option */ #if defined(IP_ROUTER_ALERT) -- cgit v1.2.3 From 8ed757c8df2df54e19e67ca0a0734cd5a0f9ab23 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 26 Jul 2018 10:10:16 +0200 Subject: [socket-nif] Add support for socket (level ip) option recvorigdstaddr Added support for ip level socket option RECVORIGDSTADDR. This option requires recvmsg to actually use, so we cannot test this fully at the moment (although both set and get works). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 132 +++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 7 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 288188d2dd..4ba94f4f60 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -531,9 +531,11 @@ typedef union { #define SOCKET_OPT_IP_MULTICAST_TTL 16 #define SOCKET_OPT_IP_NODEFRAG 17 #define SOCKET_OPT_IP_PKTINFO 19 -#define SOCKET_OPT_IP_RECVERR 20 -#define SOCKET_OPT_IP_RECVIF 21 +#define SOCKET_OPT_IP_RECVDSTADDR 20 +#define SOCKET_OPT_IP_RECVERR 21 +#define SOCKET_OPT_IP_RECVIF 22 #define SOCKET_OPT_IP_RECVOPTS 23 +#define SOCKET_OPT_IP_RECVORIGDSTADDR 24 #define SOCKET_OPT_IP_RECVTOS 25 #define SOCKET_OPT_IP_RECVTTL 26 #define SOCKET_OPT_IP_RETOPTS 27 @@ -1171,6 +1173,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_RECVDSTADDR) +static ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_RECVERR) static ERL_NIF_TERM nsetopt_lvl_ip_recverr(ErlNifEnv* env, SocketDescriptor* descP, @@ -1186,6 +1193,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_RECVORIGDSTADDR) +static ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM nsetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP, @@ -1592,6 +1604,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_RECVDSTADDRS) +static ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_RECVERR) static ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, SocketDescriptor* descP); @@ -1604,6 +1620,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_RECVORIGDSTADDR) +static ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_RECVTOS) static ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, SocketDescriptor* descP); @@ -5051,6 +5071,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVDSTADDR) + case SOCKET_OPT_IP_RECVDSTADDR: + result = nsetopt_lvl_ip_recvdstaddr(env, descP, eVal); + break; +#endif + #if defined(IP_RECVERR) case SOCKET_OPT_IP_RECVERR: result = nsetopt_lvl_ip_recverr(env, descP, eVal); @@ -5069,6 +5095,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVORIGDSTADDR) + case SOCKET_OPT_IP_RECVORIGDSTADDR: + result = nsetopt_lvl_ip_recvorigdstaddr(env, descP, eVal); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = nsetopt_lvl_ip_recvtos(env, descP, eVal); @@ -5605,6 +5637,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_pktinfo(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_recvdstaddr - Level IP RECVDSTADDR option + */ +#if defined(IP_RECVDSTADDR) +static +ERL_NIF_TERM nsetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_RECVDSTADDR, eVal); +} +#endif + + /* nsetopt_lvl_ip_recverr - Level IP RECVERR option */ #if defined(IP_RECVERR) @@ -5662,6 +5713,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_recvopts(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_recvorigdstaddr - Level IP RECVORIGDSTADDR option + */ +#if defined(IP_RECVORIGDSTADDR) +static +ERL_NIF_TERM nsetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_RECVORIGDSTADDR, eVal); +} +#endif + + /* nsetopt_lvl_ip_recvtos - Level IP RECVTOS option */ #if defined(IP_RECVTOS) @@ -8294,6 +8364,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVDSTADDR) + case SOCKET_OPT_IP_RECVDSTADDR: + result = ngetopt_lvl_ip_recvdstaddr(env, descP); + break; +#endif + #if defined(IP_RECVERR) case SOCKET_OPT_IP_RECVERR: result = ngetopt_lvl_ip_recverr(env, descP); @@ -8312,6 +8388,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_RECVORIGDSTADDR) + case SOCKET_OPT_IP_RECVORIGDSTADDR: + result = ngetopt_lvl_ip_recvorigdstaddr(env, descP); + break; +#endif + #if defined(IP_RECVTOS) case SOCKET_OPT_IP_RECVTOS: result = ngetopt_lvl_ip_recvtos(env, descP); @@ -8620,12 +8702,12 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvtos(ErlNifEnv* env, #endif -/* ngetopt_lvl_ip_recvif - Level IP RECVIF option +/* ngetopt_lvl_ip_recvdstaddr - Level IP RECVDSTADDR option */ -#if defined(IP_RECVIF) +#if defined(IP_RECVDSTADDR) static -ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, - SocketDescriptor* descP) +ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, + SocketDescriptor* descP) { #if defined(SOL_IP) int level = SOL_IP; @@ -8633,7 +8715,7 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, int level = IPPROTO_IP; #endif - return ngetopt_bool_opt(env, descP, level, IP_RECVIF); + return ngetopt_bool_opt(env, descP, level, IP_RECVDSTADDR); } #endif @@ -8656,6 +8738,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_recverr(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_recvif - Level IP RECVIF option + */ +#if defined(IP_RECVIF) +static +ERL_NIF_TERM ngetopt_lvl_ip_recvif(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_RECVIF); +} +#endif + + /* ngetopt_lvl_ip_recvopt - Level IP RECVOPTS option */ #if defined(IP_RECVOPTS) @@ -8674,6 +8774,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_recvopts(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_recvorigdstaddr - Level IP RECVORIGDSTADDR option + */ +#if defined(IP_RECVORIGDSTADDR) +static +ERL_NIF_TERM ngetopt_lvl_ip_recvorigdstaddr(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_RECVORIGDSTADDR); +} +#endif + + /* ngetopt_lvl_ip_recvttl - Level IP RECVTTL option */ #if defined(IP_RECVTTL) -- cgit v1.2.3 From 6b01561dc13a0152f56da0a2c61ad88236f87de7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 26 Jul 2018 11:07:05 +0200 Subject: [socket-nif] Add support for socket (level ip) option sendsrcaddr Added support for ip level socket option SENDSRCADDR. This option requires sendmsg to actually use, so we cannot test this fully at the moment. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 59 ++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 4ba94f4f60..13250349db 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -540,6 +540,7 @@ typedef union { #define SOCKET_OPT_IP_RECVTTL 26 #define SOCKET_OPT_IP_RETOPTS 27 #define SOCKET_OPT_IP_ROUTER_ALERT 28 +#define SOCKET_OPT_IP_SENDSRCADDR 29 // Same as IP_RECVDSTADDR? #define SOCKET_OPT_IP_TOS 30 #define SOCKET_OPT_IP_TRANSPARENT 31 #define SOCKET_OPT_IP_TTL 32 @@ -1218,6 +1219,11 @@ static ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif +#if defined(IP_SENDSRCADDR) +static ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +#endif #if defined(IP_TOS) static ERL_NIF_TERM nsetopt_lvl_ip_tos(ErlNifEnv* env, SocketDescriptor* descP, @@ -1640,6 +1646,10 @@ static ERL_NIF_TERM ngetopt_lvl_ip_retopts(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, SocketDescriptor* descP); #endif +#if defined(IP_SENDSRCADDR) +static ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, + SocketDescriptor* descP); +#endif #if defined(IP_TOS) static ERL_NIF_TERM ngetopt_lvl_ip_tos(ErlNifEnv* env, SocketDescriptor* descP); @@ -5125,6 +5135,12 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_SENDSRCADDR) + case SOCKET_OPT_IP_SENDSRCADDR: + result = nsetopt_lvl_ip_sendsrcaddr(env, descP, eVal); + break; +#endif + #if defined(IP_TOS) case SOCKET_OPT_IP_TOS: result = nsetopt_lvl_ip_tos(env, descP, eVal); @@ -5808,6 +5824,25 @@ ERL_NIF_TERM nsetopt_lvl_ip_router_alert(ErlNifEnv* env, #endif +/* nsetopt_lvl_ip_sendsrcaddr - Level IP SENDSRCADDR option + */ +#if defined(IP_SENDSRCADDR) +static +ERL_NIF_TERM nsetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return nsetopt_bool_opt(env, descP, level, IP_SENDSRCADDR, eVal); +} +#endif + + /* nsetopt_lvl_ip_tos - Level IP TOS option */ #if defined(IP_TOS) @@ -8418,6 +8453,12 @@ ERL_NIF_TERM ngetopt_lvl_ip(ErlNifEnv* env, break; #endif +#if defined(IP_SENDSRCADDR) + case SOCKET_OPT_IP_SENDSRCADDR: + result = ngetopt_lvl_ip_sendsrcaddr(env, descP); + break; +#endif + #if defined(IP_TOS) case SOCKET_OPT_IP_TOS: result = ngetopt_lvl_ip_tos(env, descP); @@ -8846,6 +8887,24 @@ ERL_NIF_TERM ngetopt_lvl_ip_router_alert(ErlNifEnv* env, #endif +/* ngetopt_lvl_ip_sendsrcaddr - Level IP SENDSRCADDR option + */ +#if defined(IP_SENDSRCADDR) +static +ERL_NIF_TERM ngetopt_lvl_ip_sendsrcaddr(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IP) + int level = SOL_IP; +#else + int level = IPPROTO_IP; +#endif + + return ngetopt_bool_opt(env, descP, level, IP_SENDSRCADDR); +} +#endif + + /* ngetopt_lvl_ip_tos - Level IP TOS option */ #if defined(IP_TOS) -- cgit v1.2.3 From 165666d8b8b1b21ffaf43ac436cfc1657ba83649 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 30 Jul 2018 18:22:46 +0200 Subject: [socket-nif] Add support for recvmsg Added preliminary support for function recvmsg. At the moment this only works on *nix (Windows has another function, WSARecvMsg, which has a slightly different API). Also we have "no" cmsg decode at the moment (just level and type). OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 11 + erts/emulator/nifs/common/socket_nif.c | 343 +++++++++++++++++++++++++++++- erts/emulator/nifs/common/socket_tarray.c | 139 ++++++++++++ erts/emulator/nifs/common/socket_tarray.h | 47 ++++ erts/emulator/nifs/common/socket_util.c | 267 ++++++++++++++++++++++- erts/emulator/nifs/common/socket_util.h | 25 +++ 6 files changed, 820 insertions(+), 12 deletions(-) create mode 100644 erts/emulator/nifs/common/socket_tarray.c create mode 100644 erts/emulator/nifs/common/socket_tarray.h (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 2d5049a9eb..18e94e80ef 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -105,19 +105,28 @@ typedef unsigned int BOOLEAN_T; */ extern ERL_NIF_TERM esock_atom_addr; extern ERL_NIF_TERM esock_atom_any; +extern ERL_NIF_TERM esock_atom_ctrl; +extern ERL_NIF_TERM esock_atom_ctrunc; +extern ERL_NIF_TERM esock_atom_data; extern ERL_NIF_TERM esock_atom_debug; extern ERL_NIF_TERM esock_atom_dgram; +extern ERL_NIF_TERM esock_atom_eor; extern ERL_NIF_TERM esock_atom_error; +extern ERL_NIF_TERM esock_atom_errqueue; extern ERL_NIF_TERM esock_atom_false; extern ERL_NIF_TERM esock_atom_family; +extern ERL_NIF_TERM esock_atom_flags; extern ERL_NIF_TERM esock_atom_flowinfo; extern ERL_NIF_TERM esock_atom_inet; extern ERL_NIF_TERM esock_atom_inet6; +extern ERL_NIF_TERM esock_atom_iov; extern ERL_NIF_TERM esock_atom_ip; extern ERL_NIF_TERM esock_atom_ipv6; +extern ERL_NIF_TERM esock_atom_level; extern ERL_NIF_TERM esock_atom_local; extern ERL_NIF_TERM esock_atom_loopback; extern ERL_NIF_TERM esock_atom_ok; +extern ERL_NIF_TERM esock_atom_oob; extern ERL_NIF_TERM esock_atom_path; extern ERL_NIF_TERM esock_atom_port; extern ERL_NIF_TERM esock_atom_protocol; @@ -129,6 +138,7 @@ extern ERL_NIF_TERM esock_atom_seqpacket; extern ERL_NIF_TERM esock_atom_stream; extern ERL_NIF_TERM esock_atom_tcp; extern ERL_NIF_TERM esock_atom_true; +extern ERL_NIF_TERM esock_atom_trunc; extern ERL_NIF_TERM esock_atom_type; extern ERL_NIF_TERM esock_atom_udp; extern ERL_NIF_TERM esock_atom_undefined; @@ -147,6 +157,7 @@ extern ERL_NIF_TERM esock_atom_einval; * Various wrapper macros for enif functions */ #define MALLOC(SZ) enif_alloc((SZ)) +#define REALLOC(P, SZ) enif_realloc((P), (SZ)) #define FREE(P) enif_free((P)) #define MKA(E,S) enif_make_atom((E), (S)) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 13250349db..6b6ddb29ca 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -417,7 +417,8 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define SOCKET_RECV_FLAG_LOW SOCKET_RECV_FLAG_CMSG_CLOEXEC #define SOCKET_RECV_FLAG_HIGH SOCKET_RECV_FLAG_TRUNC -#define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048 +#define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048 +#define SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024 #define SOCKET_OPT_VALUE_TYPE_UNSPEC 0 #define SOCKET_OPT_VALUE_TYPE_INT 1 @@ -582,6 +583,10 @@ typedef union { #define SOCKET_OPT_SCTP_NODELAY 23 #define SOCKET_OPT_SCTP_RTOINFO 29 +/* We should *eventually* use this instead of hard-coding the size (to 1) */ +#define ESOCK_RECVMSG_IOVEC_SZ 1 + + /* =================================================================== * * * @@ -663,6 +668,7 @@ static unsigned long one_value = 1; #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)) +#define sock_recvmsg(s,msghdr,flag) recvmsg((s),(msghdr),(flag)) #define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) @@ -759,8 +765,9 @@ typedef struct { SocketRequestQueue acceptorsQ; /* +++ Config & Misc stuff +++ */ - size_t rBufSz; // Read buffer size (when data length = 0 is specified) - BOOLEAN_T iow; // Inform On Wrap + size_t rBufSz; // Read buffer size (when data length = 0 is specified) + size_t rCtrlSz; // Read control buffer size + BOOLEAN_T iow; // Inform On Wrap BOOLEAN_T dbg; /* +++ Close stuff +++ */ @@ -871,6 +878,9 @@ static ERL_NIF_TERM nif_recv(ErlNifEnv* env, static ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -947,6 +957,12 @@ static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, ERL_NIF_TERM recvRef, uint16_t bufSz, int flags); +static ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM recvRef, + uint16_t bufLen, + uint16_t ctrlLen, + int flags); static ERL_NIF_TERM nclose(ErlNifEnv* env, SocketDescriptor* descP); static ERL_NIF_TERM nshutdown(ErlNifEnv* env, @@ -1854,6 +1870,13 @@ static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, SocketAddress* fromAddrP, unsigned int fromAddrLen, ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, + SocketDescriptor* descP, + int read, + int saveErrno, + struct msghdr* msgHdrP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM recvRef); static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP); @@ -2211,19 +2234,28 @@ static char str_exsend[] = "exsend"; // failed send /* *** "Global" Atoms *** */ ERL_NIF_TERM esock_atom_addr; ERL_NIF_TERM esock_atom_any; +ERL_NIF_TERM esock_atom_ctrl; +ERL_NIF_TERM esock_atom_ctrunc; +ERL_NIF_TERM esock_atom_data; ERL_NIF_TERM esock_atom_dgram; ERL_NIF_TERM esock_atom_debug; +ERL_NIF_TERM esock_atom_eor; ERL_NIF_TERM esock_atom_error; +ERL_NIF_TERM esock_atom_errqueue; ERL_NIF_TERM esock_atom_false; ERL_NIF_TERM esock_atom_family; +ERL_NIF_TERM esock_atom_flags; ERL_NIF_TERM esock_atom_flowinfo; ERL_NIF_TERM esock_atom_inet; ERL_NIF_TERM esock_atom_inet6; +ERL_NIF_TERM esock_atom_iov; ERL_NIF_TERM esock_atom_ip; ERL_NIF_TERM esock_atom_ipv6; +ERL_NIF_TERM esock_atom_level; ERL_NIF_TERM esock_atom_local; ERL_NIF_TERM esock_atom_loopback; ERL_NIF_TERM esock_atom_ok; +ERL_NIF_TERM esock_atom_oob; ERL_NIF_TERM esock_atom_path; ERL_NIF_TERM esock_atom_protocol; ERL_NIF_TERM esock_atom_port; @@ -2235,6 +2267,7 @@ ERL_NIF_TERM esock_atom_seqpacket; ERL_NIF_TERM esock_atom_stream; ERL_NIF_TERM esock_atom_tcp; ERL_NIF_TERM esock_atom_true; +ERL_NIF_TERM esock_atom_trunc; ERL_NIF_TERM esock_atom_type; ERL_NIF_TERM esock_atom_udp; ERL_NIF_TERM esock_atom_undefined; @@ -2355,7 +2388,8 @@ static SocketData data; * nif_send(Sock, SendRef, Data, Flags) * nif_sendto(Sock, SendRef, Data, Dest, Flags) * nif_recv(Sock, RecvRef, Length, Flags) - * nif_recvfrom(Sock, Flags) + * nif_recvfrom(Sock, RecvRef, BufSz, Flags) + * nif_recvmsg(Sock, RecvRef, BufSz, CtrlSz, Flags) * nif_close(Sock) * nif_shutdown(Sock, How) * nif_sockname(Sock) @@ -4023,6 +4057,189 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * nif_recvmsg + * + * Description: + * Receive a message on a socket. + * Normally used only on a (un-) connected socket! + * If a buffer size = 0 is specified, then we will use the default + * buffer size for this socket (whatever has been configured). + * If ctrl (buffer) size = 0 is specified, then the default ctrl + * (buffer) size is used (1024). + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * RecvRef - A unique id for this (send) request. + * BufSz - Size of the buffer into which we put the received message. + * CtrlSz - Size of the ctrl (buffer) into which we put the received + * ancillary data. + * Flags - Receive flags. + * + * + * + * 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. + * + * + */ + +static +ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + ERL_NIF_TERM recvRef; + unsigned int bufSz; + unsigned int ctrlSz; + unsigned int eflags; + int flags; + ERL_NIF_TERM res; + + SGDBG( ("SOCKET", "nif_recvmsg -> 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) || + !GET_UINT(env, argv[3], &ctrlSz) || + !GET_UINT(env, argv[4], &eflags)) { + return enif_make_badarg(env); + } + recvRef = argv[1]; + + SSDBG( descP, + ("SOCKET", "nif_recvmsg -> args when sock = %d:" + "\r\n Socket: %T" + "\r\n recvRef: %T" + "\r\n bufSz: %d" + "\r\n ctrlSz: %d" + "\r\n eflags: %d" + "\r\n", descP->sock, argv[0], recvRef, bufSz, ctrlSz, eflags) ); + + /* if (IS_OPEN(descP)) */ + /* return esock_make_error(env, atom_enotconn); */ + + if (!erecvflags2recvflags(eflags, &flags)) + return enif_make_badarg(env); + + MLOCK(descP->readMtx); + + /* + * + * We need to handle the case when another process tries + * to receive at the same time. + * If the current recv could not read its entire package + * this time (resulting in an select). The read of the + * other process must be made to wait until current + * is done! + * Basically, we need a read queue! + * + * A 'reading' field (boolean), which is set if we did + * not manage to read the entire message and reset every + * time we do. + * + * + */ + + res = nrecvmsg(env, descP, recvRef, bufSz, ctrlSz, flags); + + MUNLOCK(descP->readMtx); + + return res; + +} + + +/* The (read) buffer handling *must* be optimized! + * But for now we make it easy for ourselves by + * allocating a binary (of the specified or default + * size) and then throwing it away... + */ +static +ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM recvRef, + uint16_t bufLen, + uint16_t ctrlLen, + int flags) +{ + unsigned int addrLen; + ssize_t read; + int save_errno; + ErlNifBinary buf, ctrl; + int bufSz = (bufLen ? bufLen : descP->rBufSz); + int ctrlSz = (ctrlLen ? ctrlLen : descP->rCtrlSz); + struct msghdr msgHdr; + struct iovec iov[1]; // Shall we always use 1? + SocketAddress addr; + + SSDBG( descP, ("SOCKET", "nrecvfrom -> entry with" + "\r\n bufSz: %d (%d)" + "\r\n ctrlSz: %d (%d)" + "\r\n flags: %d" + "\r\n", bufSz, bufLen, ctrlSz, ctrlLen, flags) ); + + if (!descP->isReadable) + return enif_make_badarg(env); + + /* + for (i = 0; i < sizeof(buf); i++) { + if (!ALLOC_BIN(bifSz, &buf[i])) + return esock_make_error(env, atom_exalloc); + iov[i].iov_base = buf[i].data; + iov[i].iov_len = buf[i].size; + } + */ + + /* Allocate the (msg) data buffer: + */ + if (!ALLOC_BIN(bufSz, &buf)) + return esock_make_error(env, atom_exalloc); + + /* Allocate the ctrl (buffer): + */ + if (!ALLOC_BIN(ctrlSz, &ctrl)) + return esock_make_error(env, atom_exalloc); + + /* We ignore the wrap for the moment. + * Maybe we should issue a wrap-message to controlling process... + */ + cnt_inc(&descP->readTries, 1); + + addrLen = sizeof(addr); + sys_memzero((char*) &addr, addrLen); + sys_memzero((char*) &msgHdr, sizeof(msgHdr)); + + iov[0].iov_base = buf.data; + iov[0].iov_len = buf.size; + + msgHdr.msg_name = &addr; + msgHdr.msg_namelen = addrLen; + msgHdr.msg_iov = iov; + msgHdr.msg_iovlen = 1; // Should use a constant or calculate... + msgHdr.msg_control = ctrl.data; + msgHdr.msg_controllen = ctrl.size; + + read = sock_recvmsg(descP->sock, &msgHdr, flags); + if (IS_SOCKET_ERROR(read)) + save_errno = sock_errno(); + else + save_errno = -1; // The value does not actually matter in this case + + return recvmsg_check_result(env, descP, + read, + save_errno, + &msgHdr, + &ctrl, // Needed for ctrl header decode + recvRef); +} + + + /* ---------------------------------------------------------------------- * nif_close * @@ -9800,7 +10017,7 @@ ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, ESOCK_ASSERT( (numKeys == numVals) ); if (!MKMA(env, keys, vals, numKeys, &eTimeVal)) - return esock_make_error(env, esock_atom_einval);; + return esock_make_error(env, esock_atom_einval); result = esock_make_ok2(env, eTimeVal); } @@ -10370,6 +10587,110 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, +/* The recvmsg function delivers one (1) message. If our buffer + * is to small, the message will be truncated. So, regardless + * if we filled the buffer or not, we have got what we are going + * to get regarding this message. + */ +static +ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, + SocketDescriptor* descP, + int read, + int saveErrno, + struct msghdr* msgHdrP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM recvRef) +{ + + SSDBG( descP, + ("SOCKET", "recvmsg_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. + */ + + if (read < 0) { + + /* +++ Error handling +++ */ + + if (saveErrno == ECONNRESET) { + + /* +++ Oups - closed +++ */ + + SSDBG( descP, ("SOCKET", "recvfrom_check_result -> closed\r\n") ); + + /* + * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING + * PROCESS, WE NEED TO INFORM IT!!! + * + * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! + * + * + */ + + descP->closeLocal = FALSE; + descP->state = SOCKET_STATE_CLOSING; + + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_STOP), + descP, NULL, recvRef); + + return esock_make_error(env, atom_closed); + + } 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 { + + SSDBG( descP, + ("SOCKET", + "recvfrom_check_result -> errno: %d\r\n", saveErrno) ); + + return esock_make_error_errno(env, saveErrno); + } + + } else { + + /* +++ We sucessfully got a message - time to encode it +++ */ + + ERL_NIF_TERM eMsgHdr; + char* xres; + + /* + * + * + * The return value of recvmsg is the *total* number of bytes + * that where successfully read. This data has been put into + * the *IO vector*. + * + * + */ + + if ((xres = esock_encode_msghdr(env, read, + msgHdrP, ctrlBufP, + &eMsgHdr)) != NULL) + return esock_make_error_str(env, xres); + else + return esock_make_ok2(env, eMsgHdr); + + } +} + + + /* +++ decode the linger value +++ * The (socket) linger option is provided as a two tuple: * @@ -10831,6 +11152,7 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->closeMtx = MCREATE(buf); descP->rBufSz = SOCKET_RECV_BUFFER_SIZE_DEFAULT; + descP->rCtrlSz = SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT; descP->iow = FALSE; descP->dbg = SOCKET_DEBUG_DEFAULT; @@ -11941,6 +12263,7 @@ ErlNifFunc socket_funcs[] = {"nif_sendto", 5, nif_sendto, 0}, {"nif_recv", 4, nif_recv, 0}, {"nif_recvfrom", 4, nif_recvfrom, 0}, + {"nif_recvmsg", 5, nif_recvmsg, 0}, {"nif_close", 1, nif_close, 0}, {"nif_shutdown", 2, nif_shutdown, 0}, {"nif_setopt", 5, nif_setopt, 0}, @@ -12077,16 +12400,23 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) /* Global atom(s) */ esock_atom_addr = MKA(env, "addr"); esock_atom_any = MKA(env, "any"); + esock_atom_ctrl = MKA(env, "ctrl"); + esock_atom_ctrunc = MKA(env, "ctrunc"); + esock_atom_data = MKA(env, "data"); esock_atom_debug = MKA(env, "debug"); esock_atom_dgram = MKA(env, "dgram"); + esock_atom_eor = MKA(env, "eor"); esock_atom_error = MKA(env, "error"); + esock_atom_errqueue = MKA(env, "errqueue"); esock_atom_false = MKA(env, "false"); esock_atom_family = MKA(env, "family"); + esock_atom_flags = MKA(env, "flags"); esock_atom_flowinfo = MKA(env, "flowinfo"); esock_atom_inet = MKA(env, "inet"); esock_atom_inet6 = MKA(env, "inet6"); - esock_atom_ip = MKA(env, "ip"); + esock_atom_ip = MKA(env, "iov"); esock_atom_ipv6 = MKA(env, "ipvp"); + esock_atom_level = MKA(env, "level"); esock_atom_local = MKA(env, "local"); esock_atom_loopback = MKA(env, "loopback"); esock_atom_ok = MKA(env, "ok"); @@ -12101,6 +12431,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_stream = MKA(env, "stream"); esock_atom_tcp = MKA(env, "tcp"); esock_atom_true = MKA(env, "true"); + esock_atom_trunc = MKA(env, "trunc"); esock_atom_type = MKA(env, "type"); esock_atom_udp = MKA(env, "udp"); esock_atom_undefined = MKA(env, "undefined"); diff --git a/erts/emulator/nifs/common/socket_tarray.c b/erts/emulator/nifs/common/socket_tarray.c new file mode 100644 index 0000000000..bf37e5bc0e --- /dev/null +++ b/erts/emulator/nifs/common/socket_tarray.c @@ -0,0 +1,139 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. 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. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Build and "maintain" a (erlang) term array of + * variable length. + * ---------------------------------------------------------------------- + * + */ + +#include +#include + +#include + +#include "socket_int.h" +#include "socket_util.h" +#include "socket_tarray.h" + + + +/* ---------------------------------------------------------------------- + * Types + */ + +typedef struct { + uint32_t sz; + uint32_t idx; + ERL_NIF_TERM* array; +} SocketTArrayInt; + + +/* ---------------------------------------------------------------------- + * Forward for internal functions + */ + +static void esock_tarray_add1(SocketTArrayInt* taP, ERL_NIF_TERM t); +static void esock_tarray_ensure_fits(SocketTArrayInt* taP, uint32_t needs); + + +/* ---------------------------------------------------------------------- + * API + */ + +extern +void* esock_tarray_create(uint32_t sz) +{ + SocketTArrayInt* tarrayP; + + ESOCK_ASSERT( (sz == 0) ); + + tarrayP = MALLOC(sizeof(SocketTArrayInt)); + ESOCK_ASSERT( (tarrayP == NULL) ); + + tarrayP->array = MALLOC(sz * sizeof(ERL_NIF_TERM)); + ESOCK_ASSERT( (tarrayP->array == NULL) ); + tarrayP->sz = sz; + tarrayP->idx = 0; + + return ((SocketTArray) tarrayP); +} + +extern +void esock_tarray_delete(SocketTArray ta) +{ + SocketTArrayInt* taP = (SocketTArrayInt*) ta; + + FREE(taP->array); + FREE(taP); +} + + +extern +uint32_t esock_tarray_sz(SocketTArray a) +{ + return ( ((SocketTArrayInt*) a)->idx ); +} + +extern +void esock_tarray_add(SocketTArray ta, ERL_NIF_TERM t) +{ + esock_tarray_add1((SocketTArrayInt*) ta, t); +} + +extern +void esock_tarray_tolist(SocketTArray ta, + ErlNifEnv* env, + ERL_NIF_TERM* list) +{ + SocketTArrayInt* taP = (SocketTArrayInt*) ta; + + *list = MKLA(env, taP->array, taP->idx); + + esock_tarray_delete(taP); +} + + + +/* ---------------------------------------------------------------------- + * "Internal" functions + */ + +static +void esock_tarray_add1(SocketTArrayInt* taP, ERL_NIF_TERM t) +{ + esock_tarray_ensure_fits(taP, 1); + + taP->array[taP->idx++] = t; +} + +static +void esock_tarray_ensure_fits(SocketTArrayInt* taP, uint32_t needs) +{ + if (taP->sz < (taP->idx + needs)) { + uint32_t newSz = (needs < taP->sz) ? 2*taP->sz : 2*needs; + void* mem = REALLOC(taP->array, newSz * sizeof(ERL_NIF_TERM)); + + ESOCK_ASSERT( (mem == NULL) ); + + taP->sz = newSz; + taP->array = (ERL_NIF_TERM*) mem; + } +} diff --git a/erts/emulator/nifs/common/socket_tarray.h b/erts/emulator/nifs/common/socket_tarray.h new file mode 100644 index 0000000000..2e9506d288 --- /dev/null +++ b/erts/emulator/nifs/common/socket_tarray.h @@ -0,0 +1,47 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. 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. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Build and "maintain" a (erlang) term array of + * variable length. + * ---------------------------------------------------------------------- + * + */ + +#ifndef SOCKET_TARRAY_H__ +#define SOCKET_TARRAY_H__ + +typedef void* SocketTArray; + +extern SocketTArray esock_tarray_create(uint32_t sz); +extern void esock_tarray_delete(SocketTArray ta); +extern uint32_t esock_tarray_sz(SocketTArray ta); +extern void esock_tarray_add(SocketTArray ta, ERL_NIF_TERM t); +extern void esock_tarray_tolist(SocketTArray ta, + ErlNifEnv* env, + ERL_NIF_TERM* list); + +#define TARRAY_CREATE(SZ) esock_tarray_create((SZ)) +#define TARRAY_DELETE(TA) esock_tarray_delete((TA)) +#define TARRAY_SZ(TA) esock_tarray_sz((TA)) +#define TARRAY_ADD(TA, T) esock_tarray_add((TA), (T)) +#define TARRAY_TOLIST(TA, E, L) esock_tarray_tolist((TA), (E), (L)) + + +#endif // SOCKET_TARRAY_H__ diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index e6eb21adcf..5998ff35a4 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -23,17 +23,18 @@ * */ -#include -#include "socket_int.h" -#include "socket_util.h" -#include "socket_dbg.h" -#include "sys.h" - #include #include #include #include #include +#include + +#include "socket_int.h" +#include "socket_tarray.h" +#include "socket_util.h" +#include "socket_dbg.h" +#include "sys.h" /* We don't have a "debug flag" to check here, so we * should use the compile debug flag, whatever that is... @@ -69,6 +70,260 @@ static char* make_sockaddr_un(ErlNifEnv* env, ERL_NIF_TERM* sa); +/* +++ esock_encode_msghdr +++ + * + * Encode a msghdr (recvmsg). In erlang its represented as + * a map, which has a specific set of attributes: + * + * addr (source address) - sockaddr() + * iov - [binary()] + * ctrl - [cmsghdr()] + * flags - msghdr_flags() + */ + +extern +char* esock_encode_msghdr(ErlNifEnv* env, + int read, + struct msghdr* msgHdrP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM* eSockAddr) +{ + char* xres; + ERL_NIF_TERM addr, iov, ctrl, flags; + + if ((xres = esock_encode_sockaddr(env, + (SocketAddress*) msgHdrP->msg_name, + msgHdrP->msg_namelen, + &addr)) != NULL) + return xres; + + if ((xres = esock_encode_iov(env, + read, + msgHdrP->msg_iov, + msgHdrP->msg_iovlen, + &iov)) != NULL) + return xres; + + if ((xres = esock_encode_cmsghdrs(env, + ctrlBufP, + msgHdrP, + &ctrl)) != NULL) + return xres; + + if ((xres = esock_encode_mshghdr_flags(env, + msgHdrP->msg_flags, + &flags)) != NULL) + return xres; + + { + ERL_NIF_TERM keys[] = {esock_atom_addr, + esock_atom_iov, + esock_atom_ctrl, + esock_atom_flags}; + ERL_NIF_TERM vals[] = {addr, iov, ctrl, flags}; + + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, eSockAddr)) + return ESOCK_STR_EINVAL; + + } + + return NULL; +} + + + +/* +++ esock_encode_iov +++ + * + * Encode a IO Vector. In erlang we represented this as a list of binaries. + * + * We iterate through the IO vector, and as long as the remaining (rem) + * number of bytes is greater than the size of the current buffer, we + * contunue. When we have a buffer that is greater than rem, we have found + * the last buffer (it may be empty, and then the previous was last). + * We may need to split this (if 0 < rem < bufferSz). + */ + +extern +char* esock_encode_iov(ErlNifEnv* env, + int read, + struct iovec* iov, + size_t len, + ERL_NIF_TERM* eIOV) +{ + int rem = read; + uint16_t i; + BOOLEAN_T done = FALSE; + ERL_NIF_TERM a[len]; // At most this length + + if (len == 0) { + *eIOV = MKEL(env); + return NULL; + } + + for (i = 0; (!done) && (i < len); i++) { + if (iov[i].iov_len == rem) { + /* We have the exact amount - we are done */ + a[i] = MKBIN(env, iov[i].iov_base); + done = TRUE; + } else if (iov[i].iov_len < rem) { + /* Filled another buffer - continue */ + a[i] = MKBIN(env, iov[i].iov_base); + } else if (iov[i].iov_len > rem) { + /* Partly filled buffer (=> split) - we are done */ + a[i] = MKBIN(env, iov[i].iov_base); + a[i] = MKSBIN(env, a[i], 0, rem); + done = TRUE; + } + } + + *eIOV = MKLA(env, a, i+1); + + return NULL; +} + + + +/* +++ esock_encode_cmsghdrs +++ + * + * Encode a list of cmsghdr(). The X can 0 or more cmsghdr blocks. + * + * Our problem is that we have no idea how many control messages + * we have. + * + * The cmsgHdrP arguments points to the start of the control data buffer, + * an actual binary. Its the only way to create sub-binaries. So, what we + * need to continue processing this is to tern that into an binary erlang + * term (which can then in turn be turned into sub-binaries). + * + * We need the cmsgBufP (even though cmsgHdrP points to it) to be able + * to create sub-binaries (one for each HDR). + * + * The TArray is created with the size of 128, which should be enough. + * But if its not, then it will be automatically realloc'ed during add. + * Once we are done adding hdr's to it, we convert it to a list. + */ + +extern +char* esock_encode_cmsghdrs(ErlNifEnv* env, + ErlNifBinary* cmsgBinP, + struct msghdr* msgHdrP, + ERL_NIF_TERM* eCMsgHdr) +{ + ERL_NIF_TERM ctrlBuf = MKBIN(env, cmsgBinP); // The *entire* binary + SocketTArray cmsghdrs = TARRAY_CREATE(128); + struct cmsghdr* firstP = CMSG_FIRSTHDR(msgHdrP); + struct cmsghdr* currentP; + + for (currentP = firstP; + currentP != NULL; + currentP = CMSG_NXTHDR(msgHdrP, currentP)) { + + /* MUST check this since on Linux the returned "cmsg" may actually + * go too far! + */ + if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) > + msgHdrP->msg_controllen) { + /* Ouch, fatal error - give up + * We assume we cannot trust any data if this is wrong. + */ + TARRAY_DELETE(cmsghdrs); + return ESOCK_STR_EINVAL; + } else { + ERL_NIF_TERM level; + ERL_NIF_TERM type = MKI(env, currentP->cmsg_type); + unsigned char* dataP = (unsigned char*) CMSG_DATA(currentP); + size_t dataPos = dataP - cmsgBinP->data; + size_t dataLen = currentP->cmsg_len - (CHARP(currentP)-CHARP(dataP)); + ERL_NIF_TERM dataBin = MKSBIN(env, ctrlBuf, dataPos, dataLen); + + /* We can't give up just because its an unknown protocol, + * so if its a protocol we don't know, we return its integer + * value and leave it to the user. + */ + if (esock_encode_protocol(env, currentP->cmsg_level, &level) != NULL) + level = MKI(env, currentP->cmsg_level); + + /* And finally create the 'cmsghdr' map - + * and if successfull add it to the tarray. + */ + { + ERL_NIF_TERM keys[] = {esock_atom_level, + esock_atom_type, + esock_atom_data}; + ERL_NIF_TERM vals[] = {level, type, dataBin}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM cmsgHdr; + + /* Guard agains cut-and-paste errors */ + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &cmsgHdr)) { + TARRAY_DELETE(cmsghdrs); + return ESOCK_STR_EINVAL; + } + + /* And finally add it to the list... */ + TARRAY_ADD(cmsghdrs, cmsgHdr); + } + } + } + + /* The tarray is populated - convert it to a list */ + TARRAY_TOLIST(cmsghdrs, env, eCMsgHdr); + + return NULL; +} + + + +/* +++ esock_encode_mshghdr_flags +++ + * + * Encode a list of msghdr_flag(). + * + * The following flags are handled: eor | trunc | ctrunc | oob | errqueue. + */ + +extern +char* esock_encode_mshghdr_flags(ErlNifEnv* env, + int msgFlags, + ERL_NIF_TERM* flags) +{ + if (msgFlags == 0) { + *flags = MKEL(env); + return NULL; + } else { + SocketTArray ta = TARRAY_CREATE(10); // Just to be on the safe side + + if ((msgFlags & MSG_EOR) == MSG_EOR) + TARRAY_ADD(ta, esock_atom_eor); + + if ((msgFlags & MSG_TRUNC) == MSG_TRUNC) + TARRAY_ADD(ta, esock_atom_trunc); + + if ((msgFlags & MSG_CTRUNC) == MSG_CTRUNC) + TARRAY_ADD(ta, esock_atom_ctrunc); + + if ((msgFlags & MSG_OOB) == MSG_OOB) + TARRAY_ADD(ta, esock_atom_oob); + + if ((msgFlags & MSG_ERRQUEUE) == MSG_ERRQUEUE) + TARRAY_ADD(ta, esock_atom_errqueue); + + TARRAY_TOLIST(ta, env, flags); + + return NULL; + } +} + + + + /* +++ esock_decode_sockaddr +++ * * Decode a socket address - sockaddr. In erlang its represented as diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index 686ce0bac6..af0bf70d8f 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -29,9 +29,34 @@ #include #include "socket_int.h" +#define VOIDP(P) ((void*)P) +#define CHARP(P) ((char*)P) + #define ESOCK_ABORT(E) esock_abort(E, __func__, __FILE__, __LINE__) #define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0))) +extern +char* esock_encode_msghdr(ErlNifEnv* env, + int read, + struct msghdr* msgHdrP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM* eSockAddr); +extern +char* esock_encode_iov(ErlNifEnv* env, + int read, + struct iovec* iov, + size_t len, + ERL_NIF_TERM* eIOV); +extern +char* esock_encode_cmsghdrs(ErlNifEnv* env, + ErlNifBinary* cmsgBinP, + struct msghdr* msgHdrP, + ERL_NIF_TERM* eCMsgHdr); + +extern +char* esock_encode_mshghdr_flags(ErlNifEnv* env, + int msgFlags, + ERL_NIF_TERM* flags); extern char* esock_decode_sockaddr(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, -- cgit v1.2.3 From d4c6b555aea77198d662822c7f5a134a16cff8af Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 31 Jul 2018 12:53:52 +0200 Subject: [socket-nif] Debugged and stuff It seems to work, with atleast some of the cmsg options. More testing needed... OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 44 +++++++++---- erts/emulator/nifs/common/socket_tarray.c | 8 +-- erts/emulator/nifs/common/socket_tarray.h | 2 +- erts/emulator/nifs/common/socket_util.c | 104 +++++++++++++++++++++++++----- erts/emulator/nifs/common/socket_util.h | 2 + 5 files changed, 125 insertions(+), 35 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 6b6ddb29ca..205b3335e1 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -1875,6 +1875,7 @@ static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, int read, int saveErrno, struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, ErlNifBinary* ctrlBufP, ERL_NIF_TERM recvRef); @@ -4102,7 +4103,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, /* Extract arguments and perform preliminary validation */ - if ((argc != 4) || + if ((argc != 5) || !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !GET_UINT(env, argv[2], &bufSz) || !GET_UINT(env, argv[3], &ctrlSz) || @@ -4170,14 +4171,15 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, unsigned int addrLen; ssize_t read; int save_errno; - ErlNifBinary buf, ctrl; int bufSz = (bufLen ? bufLen : descP->rBufSz); int ctrlSz = (ctrlLen ? ctrlLen : descP->rCtrlSz); struct msghdr msgHdr; - struct iovec iov[1]; // Shall we always use 1? + struct iovec iov[1]; // Shall we always use 1? + ErlNifBinary data[1]; // Shall we always use 1? + ErlNifBinary ctrl; SocketAddress addr; - SSDBG( descP, ("SOCKET", "nrecvfrom -> entry with" + SSDBG( descP, ("SOCKET", "nrecvmsg -> entry with" "\r\n bufSz: %d (%d)" "\r\n ctrlSz: %d (%d)" "\r\n flags: %d" @@ -4197,7 +4199,7 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, /* Allocate the (msg) data buffer: */ - if (!ALLOC_BIN(bufSz, &buf)) + if (!ALLOC_BIN(bufSz, &data[0])) return esock_make_error(env, atom_exalloc); /* Allocate the ctrl (buffer): @@ -4214,8 +4216,8 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, sys_memzero((char*) &addr, addrLen); sys_memzero((char*) &msgHdr, sizeof(msgHdr)); - iov[0].iov_base = buf.data; - iov[0].iov_len = buf.size; + iov[0].iov_base = data[0].data; + iov[0].iov_len = data[0].size; msgHdr.msg_name = &addr; msgHdr.msg_namelen = addrLen; @@ -4234,7 +4236,8 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, read, save_errno, &msgHdr, - &ctrl, // Needed for ctrl header decode + data, // Needed for iov encode + &ctrl, // Needed for ctrl header encode recvRef); } @@ -10598,6 +10601,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, int read, int saveErrno, struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, ErlNifBinary* ctrlBufP, ERL_NIF_TERM recvRef) { @@ -10680,11 +10684,24 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, */ if ((xres = esock_encode_msghdr(env, read, - msgHdrP, ctrlBufP, - &eMsgHdr)) != NULL) + msgHdrP, dataBufP, ctrlBufP, + &eMsgHdr)) != NULL) { + + SSDBG( descP, + ("SOCKET", + "recvfrom_check_result -> " + "(msghdr) encode failed: %s\r\n", xres) ); + return esock_make_error_str(env, xres); - else + } else { + + SSDBG( descP, + ("SOCKET", + "recvfrom_check_result -> " + "(msghdr) encode ok: %T\r\n", eMsgHdr) ); + return esock_make_ok2(env, eMsgHdr); + } } } @@ -12414,8 +12431,9 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_flowinfo = MKA(env, "flowinfo"); esock_atom_inet = MKA(env, "inet"); esock_atom_inet6 = MKA(env, "inet6"); - esock_atom_ip = MKA(env, "iov"); - esock_atom_ipv6 = MKA(env, "ipvp"); + esock_atom_iov = MKA(env, "iov"); + esock_atom_ip = MKA(env, "ip"); + esock_atom_ipv6 = MKA(env, "ipv6"); esock_atom_level = MKA(env, "level"); esock_atom_local = MKA(env, "local"); esock_atom_loopback = MKA(env, "loopback"); diff --git a/erts/emulator/nifs/common/socket_tarray.c b/erts/emulator/nifs/common/socket_tarray.c index bf37e5bc0e..a327e014c4 100644 --- a/erts/emulator/nifs/common/socket_tarray.c +++ b/erts/emulator/nifs/common/socket_tarray.c @@ -63,13 +63,13 @@ void* esock_tarray_create(uint32_t sz) { SocketTArrayInt* tarrayP; - ESOCK_ASSERT( (sz == 0) ); + ESOCK_ASSERT( (sz > 0) ); tarrayP = MALLOC(sizeof(SocketTArrayInt)); - ESOCK_ASSERT( (tarrayP == NULL) ); + ESOCK_ASSERT( (tarrayP != NULL) ); tarrayP->array = MALLOC(sz * sizeof(ERL_NIF_TERM)); - ESOCK_ASSERT( (tarrayP->array == NULL) ); + ESOCK_ASSERT( (tarrayP->array != NULL) ); tarrayP->sz = sz; tarrayP->idx = 0; @@ -131,7 +131,7 @@ void esock_tarray_ensure_fits(SocketTArrayInt* taP, uint32_t needs) uint32_t newSz = (needs < taP->sz) ? 2*taP->sz : 2*needs; void* mem = REALLOC(taP->array, newSz * sizeof(ERL_NIF_TERM)); - ESOCK_ASSERT( (mem == NULL) ); + ESOCK_ASSERT( (mem != NULL) ); taP->sz = newSz; taP->array = (ERL_NIF_TERM*) mem; diff --git a/erts/emulator/nifs/common/socket_tarray.h b/erts/emulator/nifs/common/socket_tarray.h index 2e9506d288..4d78d2ccb7 100644 --- a/erts/emulator/nifs/common/socket_tarray.h +++ b/erts/emulator/nifs/common/socket_tarray.h @@ -33,7 +33,7 @@ extern SocketTArray esock_tarray_create(uint32_t sz); extern void esock_tarray_delete(SocketTArray ta); extern uint32_t esock_tarray_sz(SocketTArray ta); extern void esock_tarray_add(SocketTArray ta, ERL_NIF_TERM t); -extern void esock_tarray_tolist(SocketTArray ta, +extern void esock_tarray_tolist(SocketTArray ta, ErlNifEnv* env, ERL_NIF_TERM* list); diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 5998ff35a4..50cc94c263 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -40,7 +40,7 @@ * should use the compile debug flag, whatever that is... */ -// #define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 +#define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 #if defined(COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK) #define UTIL_DEBUG TRUE #else @@ -85,53 +85,76 @@ extern char* esock_encode_msghdr(ErlNifEnv* env, int read, struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, ErlNifBinary* ctrlBufP, ERL_NIF_TERM* eSockAddr) { char* xres; ERL_NIF_TERM addr, iov, ctrl, flags; + UDBG( ("SUTIL", "esock_encode_msghdr -> entry with" + "\r\n read: %d" + "\r\n", read) ); + if ((xres = esock_encode_sockaddr(env, (SocketAddress*) msgHdrP->msg_name, msgHdrP->msg_namelen, &addr)) != NULL) return xres; + UDBG( ("SUTIL", "esock_encode_msghdr -> try encode iov\r\n") ); if ((xres = esock_encode_iov(env, read, msgHdrP->msg_iov, msgHdrP->msg_iovlen, + dataBufP, &iov)) != NULL) return xres; + UDBG( ("SUTIL", "esock_encode_msghdr -> try encode cmsghdrs\r\n") ); if ((xres = esock_encode_cmsghdrs(env, ctrlBufP, msgHdrP, &ctrl)) != NULL) return xres; + UDBG( ("SUTIL", "esock_encode_msghdr -> try encode flags\r\n") ); if ((xres = esock_encode_mshghdr_flags(env, msgHdrP->msg_flags, &flags)) != NULL) return xres; + UDBG( ("SUTIL", "esock_encode_msghdr -> components encoded:" + "\r\n addr: %T" + "\r\n iov: %T" + "\r\n ctrl: %T" + "\r\n flags: %T" + "\r\n", addr, iov, ctrl, flags) ); { - ERL_NIF_TERM keys[] = {esock_atom_addr, - esock_atom_iov, - esock_atom_ctrl, - esock_atom_flags}; - ERL_NIF_TERM vals[] = {addr, iov, ctrl, flags}; - + ERL_NIF_TERM keys[] = {esock_atom_addr, + esock_atom_iov, + esock_atom_ctrl, + esock_atom_flags}; + ERL_NIF_TERM vals[] = {addr, iov, ctrl, flags}; unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM tmp; ESOCK_ASSERT( (numKeys == numVals) ); - if (!MKMA(env, keys, vals, numKeys, eSockAddr)) + UDBG( ("SUTIL", "esock_encode_msghdr -> create msghdr map\r\n") ); + if (!MKMA(env, keys, vals, numKeys, &tmp)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_encode_msghdr -> msghdr: " + "\r\n %T" + "\r\n", tmp) ); + + *eSockAddr = tmp; } + UDBG( ("SUTIL", "esock_encode_msghdr -> done\r\n") ); + return NULL; } @@ -153,6 +176,7 @@ char* esock_encode_iov(ErlNifEnv* env, int read, struct iovec* iov, size_t len, + ErlNifBinary* data, ERL_NIF_TERM* eIOV) { int rem = read; @@ -160,28 +184,51 @@ char* esock_encode_iov(ErlNifEnv* env, BOOLEAN_T done = FALSE; ERL_NIF_TERM a[len]; // At most this length + UDBG( ("SUTIL", "esock_encode_iov -> entry with" + "\r\n read: %d" + "\r\n (IOV) len: %d" + "\r\n", read, len) ); + if (len == 0) { *eIOV = MKEL(env); return NULL; } for (i = 0; (!done) && (i < len); i++) { + UDBG( ("SUTIL", "esock_encode_iov -> process iov:" + "\r\n iov[%d].iov_len: %d" + "\r\n rem: %d" + "\r\n", i, iov[i].iov_len, rem) ); if (iov[i].iov_len == rem) { /* We have the exact amount - we are done */ - a[i] = MKBIN(env, iov[i].iov_base); + UDBG( ("SUTIL", "esock_encode_iov -> exact => done\r\n") ); + a[i] = MKBIN(env, &data[i]); + UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); + rem = 0; // Besserwisser done = TRUE; } else if (iov[i].iov_len < rem) { /* Filled another buffer - continue */ - a[i] = MKBIN(env, iov[i].iov_base); + UDBG( ("SUTIL", "esock_encode_iov -> filled => continue\r\n") ); + a[i] = MKBIN(env, &data[i]); + rem -= iov[i].iov_len; + UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); } else if (iov[i].iov_len > rem) { /* Partly filled buffer (=> split) - we are done */ - a[i] = MKBIN(env, iov[i].iov_base); - a[i] = MKSBIN(env, a[i], 0, rem); + ERL_NIF_TERM tmp; + UDBG( ("SUTIL", "esock_encode_iov -> split => done\r\n") ); + tmp = MKBIN(env, &data[i]); + a[i] = MKSBIN(env, tmp, 0, rem); + UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); + rem = 0; // Besserwisser done = TRUE; } } - *eIOV = MKLA(env, a, i+1); + UDBG( ("SUTIL", "esock_encode_iov -> create the IOV list (%d)\r\n", i) ); + + *eIOV = MKLA(env, a, i); + + UDBG( ("SUTIL", "esock_encode_msghdr -> done\r\n") ); return NULL; } @@ -201,11 +248,11 @@ char* esock_encode_iov(ErlNifEnv* env, * term (which can then in turn be turned into sub-binaries). * * We need the cmsgBufP (even though cmsgHdrP points to it) to be able - * to create sub-binaries (one for each HDR). + * to create sub-binaries (one for each cmsg hdr). * - * The TArray is created with the size of 128, which should be enough. - * But if its not, then it will be automatically realloc'ed during add. - * Once we are done adding hdr's to it, we convert it to a list. + * The TArray (term array) is created with the size of 128, which should + * be enough. But if its not, then it will be automatically realloc'ed during + * add. Once we are done adding hdr's to it, we convert the tarray to a list. */ extern @@ -219,10 +266,16 @@ char* esock_encode_cmsghdrs(ErlNifEnv* env, struct cmsghdr* firstP = CMSG_FIRSTHDR(msgHdrP); struct cmsghdr* currentP; + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> entry\r\n") ); + for (currentP = firstP; currentP != NULL; currentP = CMSG_NXTHDR(msgHdrP, currentP)) { + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> process cmsg header when" + "\r\n TArray Size: %d" + "\r\n", TARRAY_SZ(cmsghdrs)) ); + /* MUST check this since on Linux the returned "cmsg" may actually * go too far! */ @@ -248,6 +301,11 @@ char* esock_encode_cmsghdrs(ErlNifEnv* env, if (esock_encode_protocol(env, currentP->cmsg_level, &level) != NULL) level = MKI(env, currentP->cmsg_level); + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> " + "\r\n level: %T" + "\r\n type: %T" + "\r\n", level, type) ); + /* And finally create the 'cmsghdr' map - * and if successfull add it to the tarray. */ @@ -274,6 +332,10 @@ char* esock_encode_cmsghdrs(ErlNifEnv* env, } } + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> cmsg headers processed when" + "\r\n TArray Size: %d" + "\r\n", TARRAY_SZ(cmsghdrs)) ); + /* The tarray is populated - convert it to a list */ TARRAY_TOLIST(cmsghdrs, env, eCMsgHdr); @@ -294,6 +356,10 @@ char* esock_encode_mshghdr_flags(ErlNifEnv* env, int msgFlags, ERL_NIF_TERM* flags) { + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> entry with" + "\r\n msgFlags: %d (0x%lX)" + "\r\n", msgFlags, msgFlags) ); + if (msgFlags == 0) { *flags = MKEL(env); return NULL; @@ -315,6 +381,10 @@ char* esock_encode_mshghdr_flags(ErlNifEnv* env, if ((msgFlags & MSG_ERRQUEUE) == MSG_ERRQUEUE) TARRAY_ADD(ta, esock_atom_errqueue); + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> flags processed when" + "\r\n TArray size: %d" + "\r\n", TARRAY_SZ(ta)) ); + TARRAY_TOLIST(ta, env, flags); return NULL; diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index af0bf70d8f..5a79eb3d09 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -39,6 +39,7 @@ extern char* esock_encode_msghdr(ErlNifEnv* env, int read, struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, ErlNifBinary* ctrlBufP, ERL_NIF_TERM* eSockAddr); extern @@ -46,6 +47,7 @@ char* esock_encode_iov(ErlNifEnv* env, int read, struct iovec* iov, size_t len, + ErlNifBinary* data, ERL_NIF_TERM* eIOV); extern char* esock_encode_cmsghdrs(ErlNifEnv* env, -- cgit v1.2.3 From 25c38eff5c1e8d4dc6325afa62031874e23262dc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 31 Jul 2018 18:01:55 +0200 Subject: [socket-nif] Add more control message decoding Added decoding of the "known" control message options: pktinfo, recvtos, recvttl and recvorigdstaddr). OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 12 + erts/emulator/nifs/common/socket_nif.c | 878 +++++++++++++++++++++++++------- erts/emulator/nifs/common/socket_util.c | 259 +--------- erts/emulator/nifs/common/socket_util.h | 17 - 4 files changed, 715 insertions(+), 451 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 18e94e80ef..80a9eb0734 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -105,6 +105,7 @@ typedef unsigned int BOOLEAN_T; */ extern ERL_NIF_TERM esock_atom_addr; extern ERL_NIF_TERM esock_atom_any; +extern ERL_NIF_TERM esock_atom_credentials; extern ERL_NIF_TERM esock_atom_ctrl; extern ERL_NIF_TERM esock_atom_ctrunc; extern ERL_NIF_TERM esock_atom_data; @@ -117,6 +118,7 @@ extern ERL_NIF_TERM esock_atom_false; extern ERL_NIF_TERM esock_atom_family; extern ERL_NIF_TERM esock_atom_flags; extern ERL_NIF_TERM esock_atom_flowinfo; +extern ERL_NIF_TERM esock_atom_ifindex; extern ERL_NIF_TERM esock_atom_inet; extern ERL_NIF_TERM esock_atom_inet6; extern ERL_NIF_TERM esock_atom_iov; @@ -125,20 +127,30 @@ extern ERL_NIF_TERM esock_atom_ipv6; extern ERL_NIF_TERM esock_atom_level; extern ERL_NIF_TERM esock_atom_local; extern ERL_NIF_TERM esock_atom_loopback; +extern ERL_NIF_TERM esock_atom_lowdelay; +extern ERL_NIF_TERM esock_atom_mincost; extern ERL_NIF_TERM esock_atom_ok; extern ERL_NIF_TERM esock_atom_oob; +extern ERL_NIF_TERM esock_atom_origdstaddr; extern ERL_NIF_TERM esock_atom_path; +extern ERL_NIF_TERM esock_atom_pktinfo; extern ERL_NIF_TERM esock_atom_port; extern ERL_NIF_TERM esock_atom_protocol; extern ERL_NIF_TERM esock_atom_raw; extern ERL_NIF_TERM esock_atom_rdm; +extern ERL_NIF_TERM esock_atom_reliability; +extern ERL_NIF_TERM esock_atom_rights; extern ERL_NIF_TERM esock_atom_scope_id; extern ERL_NIF_TERM esock_atom_sctp; extern ERL_NIF_TERM esock_atom_seqpacket; +extern ERL_NIF_TERM esock_atom_spec_dst; extern ERL_NIF_TERM esock_atom_stream; extern ERL_NIF_TERM esock_atom_tcp; +extern ERL_NIF_TERM esock_atom_throughput; +extern ERL_NIF_TERM esock_atom_tos; extern ERL_NIF_TERM esock_atom_true; extern ERL_NIF_TERM esock_atom_trunc; +extern ERL_NIF_TERM esock_atom_ttl; extern ERL_NIF_TERM esock_atom_type; extern ERL_NIF_TERM esock_atom_udp; extern ERL_NIF_TERM esock_atom_undefined; diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 205b3335e1..4b4082062b 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -317,6 +317,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #include #include "socket_dbg.h" +#include "socket_tarray.h" #include "socket_int.h" #include "socket_util.h" @@ -1884,97 +1885,42 @@ static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, static ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, SocketDescriptor* descP); -/* -static char* decode_in_sockaddr(ErlNifEnv* env, - ERL_NIF_TERM eSockAddr, - SocketAddress* sockAddrP, - unsigned int* addrLenP); -static char* decode_in4_sockaddr(ErlNifEnv* env, - const ERL_NIF_TERM* eIn4SockAddr, - SocketAddress* sockAddrP, - unsigned int* addrLenP); -static char* decode_in4_sockaddr_atomaddr(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - int port, - SocketAddress* sockAddrP, - unsigned int* addrLenP); -static char* decode_in4_sockaddr_addr(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - int port, - SocketAddress* sockAddrP, - unsigned int* addrLenP); -#if defined(HAVE_IN6) && defined(AF_INET6) -static char* decode_in6_sockaddr(ErlNifEnv* env, - const ERL_NIF_TERM* eIn6SockAddr, - SocketAddress* sockAddrP, - unsigned int* addrLenP); -static char* 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 char* decode_in6_sockaddr_addr(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - int port, - unsigned int flowInfo, - unsigned int scopeId, - SocketAddress* sockAddrP, - unsigned int* addrLenP); -#endif -*/ -/* -static char* decode_laddress(ErlNifEnv* env, - int domain, - ERL_NIF_TERM localAddr, - SocketAddress* localP, - unsigned int* addrLenP); -static char* decode_laddress_binary(ErlNifEnv* env, - int domain, - ERL_NIF_TERM localAddr, - SocketAddress* localP, - unsigned int* addrLenP); -*/ -/* -static char* decode_address_tuple(ErlNifEnv* env, - int domain, - const ERL_NIF_TERM* addrt, - int port, - SocketAddress* localP, - unsigned int* addrLenP); -static char* decode_address_atom(ErlNifEnv* env, - int domain, - char* addr, - int addrLen, - 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, - ERL_NIF_TERM* fromDomainT, - ERL_NIF_TERM* fromSourceT); -*/ +extern char* encode_msghdr(ErlNifEnv* env, + SocketDescriptor* descP, + int read, + struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM* eSockAddr); +extern char* encode_cmsghdrs(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifBinary* cmsgBinP, + struct msghdr* msgHdrP, + ERL_NIF_TERM* eCMsgHdr); +static char* encode_cmsghdr_type(ErlNifEnv* env, + int level, + int type, + ERL_NIF_TERM* eType); +static char* encode_cmsghdr_data(ErlNifEnv* env, + ERL_NIF_TERM ctrlBuf, + int level, + int type, + unsigned char* dataP, + size_t dataPos, + size_t dataLen, + ERL_NIF_TERM* eCMsgHdrData); +static char* encode_cmsghdr_data_ip(ErlNifEnv* env, + ERL_NIF_TERM ctrlBuf, + int type, + unsigned char* dataP, + size_t dataPos, + size_t dataLen, + ERL_NIF_TERM* eCMsgHdrData); +extern char* encode_msghdr_flags(ErlNifEnv* env, + SocketDescriptor* descP, + int msgFlags, + ERL_NIF_TERM* flags); + static BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* valP); @@ -2215,11 +2161,6 @@ static char str_true[] = "true"; static char str_usec[] = "usec"; static char str_want[] = "want"; -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_eisconn[] = "eisconn"; static char str_enotclosing[] = "enotclosing"; @@ -2235,6 +2176,7 @@ static char str_exsend[] = "exsend"; // failed send /* *** "Global" Atoms *** */ ERL_NIF_TERM esock_atom_addr; ERL_NIF_TERM esock_atom_any; +ERL_NIF_TERM esock_atom_credentials; ERL_NIF_TERM esock_atom_ctrl; ERL_NIF_TERM esock_atom_ctrunc; ERL_NIF_TERM esock_atom_data; @@ -2247,6 +2189,7 @@ ERL_NIF_TERM esock_atom_false; ERL_NIF_TERM esock_atom_family; ERL_NIF_TERM esock_atom_flags; ERL_NIF_TERM esock_atom_flowinfo; +ERL_NIF_TERM esock_atom_ifindex; ERL_NIF_TERM esock_atom_inet; ERL_NIF_TERM esock_atom_inet6; ERL_NIF_TERM esock_atom_iov; @@ -2255,20 +2198,30 @@ ERL_NIF_TERM esock_atom_ipv6; ERL_NIF_TERM esock_atom_level; ERL_NIF_TERM esock_atom_local; ERL_NIF_TERM esock_atom_loopback; +ERL_NIF_TERM esock_atom_lowdelay; +ERL_NIF_TERM esock_atom_mincost; ERL_NIF_TERM esock_atom_ok; ERL_NIF_TERM esock_atom_oob; +ERL_NIF_TERM esock_atom_origdstaddr; ERL_NIF_TERM esock_atom_path; -ERL_NIF_TERM esock_atom_protocol; +ERL_NIF_TERM esock_atom_pktinfo; ERL_NIF_TERM esock_atom_port; +ERL_NIF_TERM esock_atom_protocol; ERL_NIF_TERM esock_atom_raw; ERL_NIF_TERM esock_atom_rdm; +ERL_NIF_TERM esock_atom_rights; +ERL_NIF_TERM esock_atom_reliability; ERL_NIF_TERM esock_atom_scope_id; ERL_NIF_TERM esock_atom_sctp; ERL_NIF_TERM esock_atom_seqpacket; +ERL_NIF_TERM esock_atom_spec_dst; ERL_NIF_TERM esock_atom_stream; ERL_NIF_TERM esock_atom_tcp; +ERL_NIF_TERM esock_atom_throughput; +ERL_NIF_TERM esock_atom_tos; ERL_NIF_TERM esock_atom_true; ERL_NIF_TERM esock_atom_trunc; +ERL_NIF_TERM esock_atom_ttl; ERL_NIF_TERM esock_atom_type; ERL_NIF_TERM esock_atom_udp; ERL_NIF_TERM esock_atom_undefined; @@ -2341,11 +2294,6 @@ static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_usec; static ERL_NIF_TERM atom_want; -static ERL_NIF_TERM atom_lowdelay; -static ERL_NIF_TERM atom_throughput; -static ERL_NIF_TERM atom_reliability; -static ERL_NIF_TERM atom_mincost; - static ERL_NIF_TERM atom_eisconn; static ERL_NIF_TERM atom_enotclosing; static ERL_NIF_TERM atom_enotconn; @@ -10683,9 +10631,9 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, * */ - if ((xres = esock_encode_msghdr(env, read, - msgHdrP, dataBufP, ctrlBufP, - &eMsgHdr)) != NULL) { + if ((xres = encode_msghdr(env, descP, + read, msgHdrP, dataBufP, ctrlBufP, + &eMsgHdr)) != NULL) { SSDBG( descP, ("SOCKET", @@ -10708,6 +10656,586 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, + +/* +++ encode_msghdr +++ + * + * Encode a msghdr (recvmsg). In erlang its represented as + * a map, which has a specific set of attributes: + * + * addr (source address) - sockaddr() + * iov - [binary()] + * ctrl - [cmsghdr()] + * flags - msghdr_flags() + */ + +extern +char* encode_msghdr(ErlNifEnv* env, + SocketDescriptor* descP, + int read, + struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM* eSockAddr) +{ + char* xres; + ERL_NIF_TERM addr, iov, ctrl, flags; + + SSDBG( descP, + ("SOCKET", "encode_msghdr -> entry with" + "\r\n read: %d" + "\r\n", read) ); + + if ((xres = esock_encode_sockaddr(env, + (SocketAddress*) msgHdrP->msg_name, + msgHdrP->msg_namelen, + &addr)) != NULL) + return xres; + + SSDBG( descP, ("SOCKET", "encode_msghdr -> try encode iov\r\n") ); + if ((xres = esock_encode_iov(env, + read, + msgHdrP->msg_iov, + msgHdrP->msg_iovlen, + dataBufP, + &iov)) != NULL) + return xres; + + SSDBG( descP, ("SOCKET", "encode_msghdr -> try encode cmsghdrs\r\n") ); + if ((xres = encode_cmsghdrs(env, descP, ctrlBufP, msgHdrP, &ctrl)) != NULL) + return xres; + + SSDBG( descP, ("SOCKET", "encode_msghdr -> try encode flags\r\n") ); + if ((xres = encode_msghdr_flags(env, descP, msgHdrP->msg_flags, &flags)) != NULL) + return xres; + + SSDBG( descP, + ("SOCKET", "encode_msghdr -> components encoded:" + "\r\n addr: %T" + "\r\n iov: %T" + "\r\n ctrl: %T" + "\r\n flags: %T" + "\r\n", addr, iov, ctrl, flags) ); + { + ERL_NIF_TERM keys[] = {esock_atom_addr, + esock_atom_iov, + esock_atom_ctrl, + esock_atom_flags}; + ERL_NIF_TERM vals[] = {addr, iov, ctrl, flags}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM tmp; + + ESOCK_ASSERT( (numKeys == numVals) ); + + SSDBG( descP, ("SOCKET", "encode_msghdr -> create msghdr map\r\n") ); + if (!MKMA(env, keys, vals, numKeys, &tmp)) + return ESOCK_STR_EINVAL; + + SSDBG( descP, ("SOCKET", "encode_msghdr -> msghdr: " + "\r\n %T" + "\r\n", tmp) ); + + *eSockAddr = tmp; + } + + SSDBG( descP, ("SOCKET", "encode_msghdr -> done\r\n") ); + + return NULL; +} + + + +/* +++ encode_cmsghdrs +++ + * + * Encode a list of cmsghdr(). There can be 0 or more cmsghdr "blocks". + * + * Our "problem" is that we have no idea how many control messages + * we have. + * + * The cmsgHdrP arguments points to the start of the control data buffer, + * an actual binary. Its the only way to create sub-binaries. So, what we + * need to continue processing this is to tern that into an binary erlang + * term (which can then in turn be turned into sub-binaries). + * + * We need the cmsgBufP (even though cmsgHdrP points to it) to be able + * to create sub-binaries (one for each cmsg hdr). + * + * The TArray (term array) is created with the size of 128, which should + * be enough. But if its not, then it will be automatically realloc'ed during + * add. Once we are done adding hdr's to it, we convert the tarray to a list. + */ + +extern +char* encode_cmsghdrs(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifBinary* cmsgBinP, + struct msghdr* msgHdrP, + ERL_NIF_TERM* eCMsgHdr) +{ + ERL_NIF_TERM ctrlBuf = MKBIN(env, cmsgBinP); // The *entire* binary + SocketTArray cmsghdrs = TARRAY_CREATE(128); + struct cmsghdr* firstP = CMSG_FIRSTHDR(msgHdrP); + struct cmsghdr* currentP; + + SSDBG( descP, ("SOCKET", "encode_cmsghdrs -> entry\r\n") ); + + for (currentP = firstP; + currentP != NULL; + currentP = CMSG_NXTHDR(msgHdrP, currentP)) { + + SSDBG( descP, + ("SOCKET", "encode_cmsghdrs -> process cmsg header when" + "\r\n TArray Size: %d" + "\r\n", TARRAY_SZ(cmsghdrs)) ); + + /* MUST check this since on Linux the returned "cmsg" may actually + * go too far! + */ + if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) > + msgHdrP->msg_controllen) { + /* Ouch, fatal error - give up + * We assume we cannot trust any data if this is wrong. + */ + TARRAY_DELETE(cmsghdrs); + return ESOCK_STR_EINVAL; + } else { + ERL_NIF_TERM level, type, data; + unsigned char* dataP = (unsigned char*) CMSG_DATA(currentP); + size_t dataPos = dataP - cmsgBinP->data; + size_t dataLen = currentP->cmsg_len - (CHARP(currentP)-CHARP(dataP)); + + SSDBG( descP, + ("SOCKET", "encode_cmsghdrs -> cmsg header data: " + "\r\n dataPos: %d" + "\r\n dataLen: %d" + "\r\n", dataPos, dataLen) ); + + /* We can't give up just because its an unknown protocol, + * so if its a protocol we don't know, we return its integer + * value and leave it to the user. + */ + if (esock_encode_protocol(env, currentP->cmsg_level, &level) != NULL) + level = MKI(env, currentP->cmsg_level); + + if (encode_cmsghdr_type(env, + currentP->cmsg_level, currentP->cmsg_type, + &type) != NULL) + type = MKI(env, currentP->cmsg_type); + + if (encode_cmsghdr_data(env, ctrlBuf, + currentP->cmsg_level, + currentP->cmsg_type, + dataP, dataPos, dataLen, + &data) != NULL) + data = MKSBIN(env, ctrlBuf, dataPos, dataLen); + + SSDBG( descP, + ("SOCKET", "encode_cmsghdrs -> " + "\r\n level: %T" + "\r\n type: %T" + "\r\n data: %T" + "\r\n", level, type, data) ); + + /* And finally create the 'cmsghdr' map - + * and if successfull add it to the tarray. + */ + { + ERL_NIF_TERM keys[] = {esock_atom_level, + esock_atom_type, + esock_atom_data}; + ERL_NIF_TERM vals[] = {level, type, data}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM cmsgHdr; + + /* Guard agains cut-and-paste errors */ + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &cmsgHdr)) { + TARRAY_DELETE(cmsghdrs); + return ESOCK_STR_EINVAL; + } + + /* And finally add it to the list... */ + TARRAY_ADD(cmsghdrs, cmsgHdr); + } + } + } + + SSDBG( descP, + ("SOCKET", "encode_cmsghdrs -> cmsg headers processed when" + "\r\n TArray Size: %d" + "\r\n", TARRAY_SZ(cmsghdrs)) ); + + /* The tarray is populated - convert it to a list */ + TARRAY_TOLIST(cmsghdrs, env, eCMsgHdr); + + return NULL; +} + + + +/* +++ encode_cmsghdr_type +++ + * + * Encode the type part of the cmsghdr(). + * + */ + +static +char* encode_cmsghdr_type(ErlNifEnv* env, + int level, + int type, + ERL_NIF_TERM* eType) +{ + char* xres = NULL; + + switch (level) { + case SOL_SOCKET: + switch (type) { +#if defined(SCM_RIGHTS) + case SCM_RIGHTS: + *eType = esock_atom_rights; + break; +#endif + +#if defined(SCM_CREDENTIALS) + case SCM_CREDENTIALS: + *eType = esock_atom_credentials; + break; +#endif + + default: + xres = ESOCK_STR_EINVAL; + break; + } + break; + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + switch (type) { +#if defined(IP_TOS) + case IP_TOS: + *eType = esock_atom_tos; + break; +#endif + +#if defined(IP_TTL) + case IP_TTL: + *eType = esock_atom_ttl; + break; +#endif + +#if defined(IP_PKTINFO) + case IP_PKTINFO: + *eType = esock_atom_pktinfo; + break; +#endif + +#if defined(IP_ORIGDSTADDR) + case IP_ORIGDSTADDR: + *eType = esock_atom_origdstaddr; + break; +#endif + + default: + xres = ESOCK_STR_EINVAL; + break; + } + break; + +#if defined(SOL_IPV6) + case SOL_IPV6: + switch (type) { + default: + xres = ESOCK_STR_EINVAL; + break; + } + break; +#endif + + case IPPROTO_TCP: + switch (type) { + default: + xres = ESOCK_STR_EINVAL; + break; + } + break; + + case IPPROTO_UDP: + switch (type) { + default: + xres = ESOCK_STR_EINVAL; + break; + } + break; + +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + switch (type) { + default: + xres = ESOCK_STR_EINVAL; + break; + } + break; +#endif + + default: + xres = ESOCK_STR_EINVAL; + break; + } + + return xres; +} + + + +/* +++ encode_cmsghdr_data +++ + * + * Encode the data part of the cmsghdr(). + * + */ + +static +char* encode_cmsghdr_data(ErlNifEnv* env, + ERL_NIF_TERM ctrlBuf, + int level, + int type, + unsigned char* dataP, + size_t dataPos, + size_t dataLen, + ERL_NIF_TERM* eCMsgHdrData) +{ + char* xres; + + switch (level) { + /* + #if defined(SOL_SOCKET) + case SOL_SOCKET: + xres = encode_cmsghdr_data_socket(env, type, dataP, eCMsgHdrData); + break; + #endif + */ + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + xres = encode_cmsghdr_data_ip(env, ctrlBuf, type, + dataP, dataPos, dataLen, + eCMsgHdrData); + break; + + /* + #if defined(SOL_IPV6) + case SOL_IPV6: + xres = encode_cmsghdr_data_ipv6(env, type, dataP, eCMsgHdrData); + break; + #endif + */ + + /* + case IPPROTO_TCP: + xres = encode_cmsghdr_data_tcp(env, type, dataP, eCMsgHdrData); + break; + */ + + /* + case IPPROTO_UDP: + xres = encode_cmsghdr_data_udp(env, type, dataP, eCMsgHdrData); + break; + */ + + /* + #if defined(HAVE_SCTP) + case IPPROTO_SCTP: + xres = encode_cmsghdr_data_sctp(env, type, dataP, eCMsgHdrData); + break; + #endif + */ + + default: + *eCMsgHdrData = MKSBIN(env, ctrlBuf, dataPos, dataLen); + xres = NULL; + break; + } + + return xres; +} + + + +/* +++ encode_cmsghdr_data_ip +++ + * + * Encode the data part when protocol = IP of the cmsghdr(). + * + */ + +static +char* encode_cmsghdr_data_ip(ErlNifEnv* env, + ERL_NIF_TERM ctrlBuf, + int type, + unsigned char* dataP, + size_t dataPos, + size_t dataLen, + ERL_NIF_TERM* eCMsgHdrData) +{ + char* xres; + + switch (type) { +#if defined(IP_TOS) + case IP_TOS: + { + unsigned char tos = IPTOS_TOS(*dataP); + switch (tos) { + case IPTOS_LOWDELAY: + *eCMsgHdrData = esock_atom_lowdelay; + break; + case IPTOS_THROUGHPUT: + *eCMsgHdrData = esock_atom_throughput; + break; + case IPTOS_RELIABILITY: + *eCMsgHdrData = esock_atom_reliability; + break; + case IPTOS_MINCOST: + *eCMsgHdrData = esock_atom_mincost; + break; + default: + *eCMsgHdrData = MKUI(env, tos); + break; + } + } + break; +#endif + +#if defined(IP_TTL) + case IP_TTL: + { + int ttl = *((int*) dataP); + *eCMsgHdrData = MKI(env, ttl); + } + break; +#endif + +#if defined(IP_PKTINFO) + case IP_PKTINFO: + { + struct in_pktinfo* pktInfoP = (struct in_pktinfo*) dataP; + ERL_NIF_TERM ifIndex = MKUI(env, pktInfoP->ipi_ifindex); + ERL_NIF_TERM specDst, addr; + + if ((xres = esock_encode_ip4_address(env, + &pktInfoP->ipi_spec_dst, + &specDst)) != NULL) { + *eCMsgHdrData = esock_atom_undefined; + return xres; + } + + if ((xres = esock_encode_ip4_address(env, + &pktInfoP->ipi_addr, + &addr)) != NULL) { + *eCMsgHdrData = esock_atom_undefined; + return xres; + } + + + { + ERL_NIF_TERM keys[] = {esock_atom_ifindex, + esock_atom_spec_dst, + esock_atom_addr}; + ERL_NIF_TERM vals[] = {ifIndex, specDst, addr}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, eCMsgHdrData)) { + *eCMsgHdrData = esock_atom_undefined; + return ESOCK_STR_EINVAL; + } + } + } + break; +#endif + +#if defined(IP_ORIGDSTADDR) + case IP_ORIGDSTADDR: + if ((xres = esock_encode_sockaddr_in4(env, + (struct sockaddr_in*) dataP, + dataLen, + eCMsgHdrData)) != NULL) { + *eCMsgHdrData = esock_atom_undefined; + return xres; + } + break; +#endif + + default: + *eCMsgHdrData = MKSBIN(env, ctrlBuf, dataPos, dataLen); + break; + } + + return NULL; +} + + + +/* +++ encode_msghdr_flags +++ + * + * Encode a list of msghdr_flag(). + * + * The following flags are handled: eor | trunc | ctrunc | oob | errqueue. + */ + +extern +char* encode_msghdr_flags(ErlNifEnv* env, + SocketDescriptor* descP, + int msgFlags, + ERL_NIF_TERM* flags) +{ + SSDBG( descP, + ("SOCKET", "encode_cmsghdrs_flags -> entry with" + "\r\n msgFlags: %d (0x%lX)" + "\r\n", msgFlags, msgFlags) ); + + if (msgFlags == 0) { + *flags = MKEL(env); + return NULL; + } else { + SocketTArray ta = TARRAY_CREATE(10); // Just to be on the safe side + + if ((msgFlags & MSG_EOR) == MSG_EOR) + TARRAY_ADD(ta, esock_atom_eor); + + if ((msgFlags & MSG_TRUNC) == MSG_TRUNC) + TARRAY_ADD(ta, esock_atom_trunc); + + if ((msgFlags & MSG_CTRUNC) == MSG_CTRUNC) + TARRAY_ADD(ta, esock_atom_ctrunc); + + if ((msgFlags & MSG_OOB) == MSG_OOB) + TARRAY_ADD(ta, esock_atom_oob); + + if ((msgFlags & MSG_ERRQUEUE) == MSG_ERRQUEUE) + TARRAY_ADD(ta, esock_atom_errqueue); + + SSDBG( descP, + ("SOCKET", "esock_encode_cmsghdrs -> flags processed when" + "\r\n TArray size: %d" + "\r\n", TARRAY_SZ(ta)) ); + + TARRAY_TOLIST(ta, env, flags); + + return NULL; + } +} + + + + /* +++ decode the linger value +++ * The (socket) linger option is provided as a two tuple: * @@ -10761,38 +11289,24 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) BOOLEAN_T result = FALSE; if (IS_ATOM(env, eVal)) { - unsigned int len; - char b[sizeof(str_reliability)+1]; // Just in case... - if (!(GET_ATOM_LEN(env, eVal, &len) && - (len > 0) && - (len <= (sizeof(str_reliability))))) { - *val = -1; - return FALSE; - } - - if (!GET_ATOM(env, eVal, b, sizeof(b))) { - *val = -1; - return FALSE; - } - - if (strncmp(b, str_lowdelay, len) == 0) { - *val = IPTOS_LOWDELAY; + if (COMPARE(eVal, esock_atom_lowdelay) == 0) { + *val = IPTOS_LOWDELAY; result = TRUE; - } else if (strncmp(b, str_throughput, len) == 0) { - *val = IPTOS_THROUGHPUT; + } else if (COMPARE(eVal, esock_atom_throughput) == 0) { + *val = IPTOS_THROUGHPUT; result = TRUE; - } else if (strncmp(b, str_reliability, len) == 0) { - *val = IPTOS_RELIABILITY; + } else if (COMPARE(eVal, esock_atom_reliability) == 0) { + *val = IPTOS_RELIABILITY; result = TRUE; - } else if (strncmp(b, str_mincost, len) == 0) { - *val = IPTOS_MINCOST; + } else if (COMPARE(eVal, esock_atom_mincost) == 0) { + *val = IPTOS_MINCOST; result = TRUE; } else { - *val = -1; + *val = -1; result = FALSE; } - + } else if (IS_NUM(env, eVal)) { if (GET_INT(env, eVal, val)) { @@ -11095,21 +11609,21 @@ ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val) { ERL_NIF_TERM result; - switch (val) { + switch (IPTOS_TOS(val)) { case IPTOS_LOWDELAY: - result = esock_make_ok2(env, atom_lowdelay); + result = esock_make_ok2(env, esock_atom_lowdelay); break; case IPTOS_THROUGHPUT: - result = esock_make_ok2(env, atom_throughput); + result = esock_make_ok2(env, esock_atom_throughput); break; case IPTOS_RELIABILITY: - result = esock_make_ok2(env, atom_reliability); + result = esock_make_ok2(env, esock_atom_reliability); break; case IPTOS_MINCOST: - result = esock_make_ok2(env, atom_mincost); + result = esock_make_ok2(env, esock_atom_mincost); break; default: @@ -12415,56 +12929,64 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_want = MKA(env, str_want); /* Global atom(s) */ - esock_atom_addr = MKA(env, "addr"); - esock_atom_any = MKA(env, "any"); - esock_atom_ctrl = MKA(env, "ctrl"); - esock_atom_ctrunc = MKA(env, "ctrunc"); - esock_atom_data = MKA(env, "data"); - esock_atom_debug = MKA(env, "debug"); - esock_atom_dgram = MKA(env, "dgram"); - esock_atom_eor = MKA(env, "eor"); - esock_atom_error = MKA(env, "error"); - esock_atom_errqueue = MKA(env, "errqueue"); - esock_atom_false = MKA(env, "false"); - esock_atom_family = MKA(env, "family"); - esock_atom_flags = MKA(env, "flags"); - esock_atom_flowinfo = MKA(env, "flowinfo"); - esock_atom_inet = MKA(env, "inet"); - esock_atom_inet6 = MKA(env, "inet6"); - esock_atom_iov = MKA(env, "iov"); - esock_atom_ip = MKA(env, "ip"); - esock_atom_ipv6 = MKA(env, "ipv6"); - esock_atom_level = MKA(env, "level"); - esock_atom_local = MKA(env, "local"); - esock_atom_loopback = MKA(env, "loopback"); - esock_atom_ok = MKA(env, "ok"); - esock_atom_path = MKA(env, "path"); - esock_atom_port = MKA(env, "port"); - esock_atom_protocol = MKA(env, "protocol"); - esock_atom_raw = MKA(env, "raw"); - esock_atom_rdm = MKA(env, "rdm"); - esock_atom_scope_id = MKA(env, "scope_id"); - esock_atom_sctp = MKA(env, "sctp"); - esock_atom_seqpacket = MKA(env, "seqpacket"); - esock_atom_stream = MKA(env, "stream"); - esock_atom_tcp = MKA(env, "tcp"); - esock_atom_true = MKA(env, "true"); - esock_atom_trunc = MKA(env, "trunc"); - esock_atom_type = MKA(env, "type"); - esock_atom_udp = MKA(env, "udp"); - esock_atom_undefined = MKA(env, "undefined"); - esock_atom_unknown = MKA(env, "unknown"); + esock_atom_addr = MKA(env, "addr"); + esock_atom_any = MKA(env, "any"); + esock_atom_credentials = MKA(env, "credentials"); + esock_atom_ctrl = MKA(env, "ctrl"); + esock_atom_ctrunc = MKA(env, "ctrunc"); + esock_atom_data = MKA(env, "data"); + esock_atom_debug = MKA(env, "debug"); + esock_atom_dgram = MKA(env, "dgram"); + esock_atom_eor = MKA(env, "eor"); + esock_atom_error = MKA(env, "error"); + esock_atom_errqueue = MKA(env, "errqueue"); + esock_atom_false = MKA(env, "false"); + esock_atom_family = MKA(env, "family"); + esock_atom_flags = MKA(env, "flags"); + esock_atom_flowinfo = MKA(env, "flowinfo"); + esock_atom_ifindex = MKA(env, "ifindex"); + esock_atom_inet = MKA(env, "inet"); + esock_atom_inet6 = MKA(env, "inet6"); + esock_atom_iov = MKA(env, "iov"); + esock_atom_ip = MKA(env, "ip"); + esock_atom_ipv6 = MKA(env, "ipv6"); + esock_atom_level = MKA(env, "level"); + esock_atom_local = MKA(env, "local"); + esock_atom_loopback = MKA(env, "loopback"); + esock_atom_lowdelay = MKA(env, "lowdelay"); + esock_atom_mincost = MKA(env, "mincost"); + esock_atom_ok = MKA(env, "ok"); + esock_atom_oob = MKA(env, "oob"); + esock_atom_origdstaddr = MKA(env, "origdstaddr"); + esock_atom_path = MKA(env, "path"); + esock_atom_pktinfo = MKA(env, "pktinfo"); + esock_atom_port = MKA(env, "port"); + esock_atom_protocol = MKA(env, "protocol"); + esock_atom_raw = MKA(env, "raw"); + esock_atom_rdm = MKA(env, "rdm"); + esock_atom_reliability = MKA(env, "reliability"); + esock_atom_rights = MKA(env, "rights"); + esock_atom_scope_id = MKA(env, "scope_id"); + esock_atom_sctp = MKA(env, "sctp"); + esock_atom_seqpacket = MKA(env, "seqpacket"); + esock_atom_spec_dst = MKA(env, "spec_dst"); + esock_atom_stream = MKA(env, "stream"); + esock_atom_tcp = MKA(env, "tcp"); + esock_atom_throughput = MKA(env, "throughput"); + esock_atom_tos = MKA(env, "tos"); + esock_atom_true = MKA(env, "true"); + esock_atom_trunc = MKA(env, "trunc"); + esock_atom_ttl = MKA(env, "ttl"); + esock_atom_type = MKA(env, "type"); + 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); esock_atom_eagain = MKA(env, ESOCK_STR_EAGAIN); esock_atom_einval = MKA(env, ESOCK_STR_EINVAL); - atom_lowdelay = MKA(env, str_lowdelay); - atom_throughput = MKA(env, str_throughput); - atom_reliability = MKA(env, str_reliability); - atom_mincost = MKA(env, str_mincost); - /* Error codes */ atom_eisconn = MKA(env, str_eisconn); atom_enotclosing = MKA(env, str_enotclosing); diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 50cc94c263..232e8200df 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -31,7 +31,6 @@ #include #include "socket_int.h" -#include "socket_tarray.h" #include "socket_util.h" #include "socket_dbg.h" #include "sys.h" @@ -40,7 +39,7 @@ * should use the compile debug flag, whatever that is... */ -#define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 +// #define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 #if defined(COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK) #define UTIL_DEBUG TRUE #else @@ -70,96 +69,6 @@ static char* make_sockaddr_un(ErlNifEnv* env, ERL_NIF_TERM* sa); -/* +++ esock_encode_msghdr +++ - * - * Encode a msghdr (recvmsg). In erlang its represented as - * a map, which has a specific set of attributes: - * - * addr (source address) - sockaddr() - * iov - [binary()] - * ctrl - [cmsghdr()] - * flags - msghdr_flags() - */ - -extern -char* esock_encode_msghdr(ErlNifEnv* env, - int read, - struct msghdr* msgHdrP, - ErlNifBinary* dataBufP, - ErlNifBinary* ctrlBufP, - ERL_NIF_TERM* eSockAddr) -{ - char* xres; - ERL_NIF_TERM addr, iov, ctrl, flags; - - UDBG( ("SUTIL", "esock_encode_msghdr -> entry with" - "\r\n read: %d" - "\r\n", read) ); - - if ((xres = esock_encode_sockaddr(env, - (SocketAddress*) msgHdrP->msg_name, - msgHdrP->msg_namelen, - &addr)) != NULL) - return xres; - - UDBG( ("SUTIL", "esock_encode_msghdr -> try encode iov\r\n") ); - if ((xres = esock_encode_iov(env, - read, - msgHdrP->msg_iov, - msgHdrP->msg_iovlen, - dataBufP, - &iov)) != NULL) - return xres; - - UDBG( ("SUTIL", "esock_encode_msghdr -> try encode cmsghdrs\r\n") ); - if ((xres = esock_encode_cmsghdrs(env, - ctrlBufP, - msgHdrP, - &ctrl)) != NULL) - return xres; - - UDBG( ("SUTIL", "esock_encode_msghdr -> try encode flags\r\n") ); - if ((xres = esock_encode_mshghdr_flags(env, - msgHdrP->msg_flags, - &flags)) != NULL) - return xres; - - UDBG( ("SUTIL", "esock_encode_msghdr -> components encoded:" - "\r\n addr: %T" - "\r\n iov: %T" - "\r\n ctrl: %T" - "\r\n flags: %T" - "\r\n", addr, iov, ctrl, flags) ); - { - ERL_NIF_TERM keys[] = {esock_atom_addr, - esock_atom_iov, - esock_atom_ctrl, - esock_atom_flags}; - ERL_NIF_TERM vals[] = {addr, iov, ctrl, flags}; - unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); - unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); - ERL_NIF_TERM tmp; - - ESOCK_ASSERT( (numKeys == numVals) ); - - UDBG( ("SUTIL", "esock_encode_msghdr -> create msghdr map\r\n") ); - if (!MKMA(env, keys, vals, numKeys, &tmp)) - return ESOCK_STR_EINVAL; - - UDBG( ("SUTIL", "esock_encode_msghdr -> msghdr: " - "\r\n %T" - "\r\n", tmp) ); - - *eSockAddr = tmp; - } - - UDBG( ("SUTIL", "esock_encode_msghdr -> done\r\n") ); - - return NULL; -} - - - /* +++ esock_encode_iov +++ * * Encode a IO Vector. In erlang we represented this as a list of binaries. @@ -203,23 +112,20 @@ char* esock_encode_iov(ErlNifEnv* env, /* We have the exact amount - we are done */ UDBG( ("SUTIL", "esock_encode_iov -> exact => done\r\n") ); a[i] = MKBIN(env, &data[i]); - UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); - rem = 0; // Besserwisser + rem = 0; // Besserwisser done = TRUE; } else if (iov[i].iov_len < rem) { /* Filled another buffer - continue */ UDBG( ("SUTIL", "esock_encode_iov -> filled => continue\r\n") ); a[i] = MKBIN(env, &data[i]); rem -= iov[i].iov_len; - UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); } else if (iov[i].iov_len > rem) { /* Partly filled buffer (=> split) - we are done */ ERL_NIF_TERM tmp; UDBG( ("SUTIL", "esock_encode_iov -> split => done\r\n") ); tmp = MKBIN(env, &data[i]); a[i] = MKSBIN(env, tmp, 0, rem); - UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); - rem = 0; // Besserwisser + rem = 0; // Besserwisser done = TRUE; } } @@ -235,165 +141,6 @@ char* esock_encode_iov(ErlNifEnv* env, -/* +++ esock_encode_cmsghdrs +++ - * - * Encode a list of cmsghdr(). The X can 0 or more cmsghdr blocks. - * - * Our problem is that we have no idea how many control messages - * we have. - * - * The cmsgHdrP arguments points to the start of the control data buffer, - * an actual binary. Its the only way to create sub-binaries. So, what we - * need to continue processing this is to tern that into an binary erlang - * term (which can then in turn be turned into sub-binaries). - * - * We need the cmsgBufP (even though cmsgHdrP points to it) to be able - * to create sub-binaries (one for each cmsg hdr). - * - * The TArray (term array) is created with the size of 128, which should - * be enough. But if its not, then it will be automatically realloc'ed during - * add. Once we are done adding hdr's to it, we convert the tarray to a list. - */ - -extern -char* esock_encode_cmsghdrs(ErlNifEnv* env, - ErlNifBinary* cmsgBinP, - struct msghdr* msgHdrP, - ERL_NIF_TERM* eCMsgHdr) -{ - ERL_NIF_TERM ctrlBuf = MKBIN(env, cmsgBinP); // The *entire* binary - SocketTArray cmsghdrs = TARRAY_CREATE(128); - struct cmsghdr* firstP = CMSG_FIRSTHDR(msgHdrP); - struct cmsghdr* currentP; - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> entry\r\n") ); - - for (currentP = firstP; - currentP != NULL; - currentP = CMSG_NXTHDR(msgHdrP, currentP)) { - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> process cmsg header when" - "\r\n TArray Size: %d" - "\r\n", TARRAY_SZ(cmsghdrs)) ); - - /* MUST check this since on Linux the returned "cmsg" may actually - * go too far! - */ - if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) > - msgHdrP->msg_controllen) { - /* Ouch, fatal error - give up - * We assume we cannot trust any data if this is wrong. - */ - TARRAY_DELETE(cmsghdrs); - return ESOCK_STR_EINVAL; - } else { - ERL_NIF_TERM level; - ERL_NIF_TERM type = MKI(env, currentP->cmsg_type); - unsigned char* dataP = (unsigned char*) CMSG_DATA(currentP); - size_t dataPos = dataP - cmsgBinP->data; - size_t dataLen = currentP->cmsg_len - (CHARP(currentP)-CHARP(dataP)); - ERL_NIF_TERM dataBin = MKSBIN(env, ctrlBuf, dataPos, dataLen); - - /* We can't give up just because its an unknown protocol, - * so if its a protocol we don't know, we return its integer - * value and leave it to the user. - */ - if (esock_encode_protocol(env, currentP->cmsg_level, &level) != NULL) - level = MKI(env, currentP->cmsg_level); - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> " - "\r\n level: %T" - "\r\n type: %T" - "\r\n", level, type) ); - - /* And finally create the 'cmsghdr' map - - * and if successfull add it to the tarray. - */ - { - ERL_NIF_TERM keys[] = {esock_atom_level, - esock_atom_type, - esock_atom_data}; - ERL_NIF_TERM vals[] = {level, type, dataBin}; - unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); - unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); - ERL_NIF_TERM cmsgHdr; - - /* Guard agains cut-and-paste errors */ - ESOCK_ASSERT( (numKeys == numVals) ); - - if (!MKMA(env, keys, vals, numKeys, &cmsgHdr)) { - TARRAY_DELETE(cmsghdrs); - return ESOCK_STR_EINVAL; - } - - /* And finally add it to the list... */ - TARRAY_ADD(cmsghdrs, cmsgHdr); - } - } - } - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> cmsg headers processed when" - "\r\n TArray Size: %d" - "\r\n", TARRAY_SZ(cmsghdrs)) ); - - /* The tarray is populated - convert it to a list */ - TARRAY_TOLIST(cmsghdrs, env, eCMsgHdr); - - return NULL; -} - - - -/* +++ esock_encode_mshghdr_flags +++ - * - * Encode a list of msghdr_flag(). - * - * The following flags are handled: eor | trunc | ctrunc | oob | errqueue. - */ - -extern -char* esock_encode_mshghdr_flags(ErlNifEnv* env, - int msgFlags, - ERL_NIF_TERM* flags) -{ - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> entry with" - "\r\n msgFlags: %d (0x%lX)" - "\r\n", msgFlags, msgFlags) ); - - if (msgFlags == 0) { - *flags = MKEL(env); - return NULL; - } else { - SocketTArray ta = TARRAY_CREATE(10); // Just to be on the safe side - - if ((msgFlags & MSG_EOR) == MSG_EOR) - TARRAY_ADD(ta, esock_atom_eor); - - if ((msgFlags & MSG_TRUNC) == MSG_TRUNC) - TARRAY_ADD(ta, esock_atom_trunc); - - if ((msgFlags & MSG_CTRUNC) == MSG_CTRUNC) - TARRAY_ADD(ta, esock_atom_ctrunc); - - if ((msgFlags & MSG_OOB) == MSG_OOB) - TARRAY_ADD(ta, esock_atom_oob); - - if ((msgFlags & MSG_ERRQUEUE) == MSG_ERRQUEUE) - TARRAY_ADD(ta, esock_atom_errqueue); - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> flags processed when" - "\r\n TArray size: %d" - "\r\n", TARRAY_SZ(ta)) ); - - TARRAY_TOLIST(ta, env, flags); - - return NULL; - } -} - - - - /* +++ esock_decode_sockaddr +++ * * Decode a socket address - sockaddr. In erlang its represented as diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index 5a79eb3d09..5d720936f3 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -35,13 +35,6 @@ #define ESOCK_ABORT(E) esock_abort(E, __func__, __FILE__, __LINE__) #define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0))) -extern -char* esock_encode_msghdr(ErlNifEnv* env, - int read, - struct msghdr* msgHdrP, - ErlNifBinary* dataBufP, - ErlNifBinary* ctrlBufP, - ERL_NIF_TERM* eSockAddr); extern char* esock_encode_iov(ErlNifEnv* env, int read, @@ -50,16 +43,6 @@ char* esock_encode_iov(ErlNifEnv* env, ErlNifBinary* data, ERL_NIF_TERM* eIOV); extern -char* esock_encode_cmsghdrs(ErlNifEnv* env, - ErlNifBinary* cmsgBinP, - struct msghdr* msgHdrP, - ERL_NIF_TERM* eCMsgHdr); - -extern -char* esock_encode_mshghdr_flags(ErlNifEnv* env, - int msgFlags, - ERL_NIF_TERM* flags); -extern char* esock_decode_sockaddr(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, SocketAddress* sockAddrP, -- cgit v1.2.3 From 90a150771faa3cf01e82919b0c17854de9987783 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 1 Aug 2018 19:42:32 +0200 Subject: [socket-nif] Processing of more cmsg headers Added processing or more cmsg headers (for more options). Now (also) supports: socket:timestamp. Also various fixes and cleanups. For some reason calling getopt(Sock, 0, {13, int}) (or similar) fails with badarg even though the nif-function (nif_getopt) actually returns a valid value (for instance: {ok, 0}). OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 5 + erts/emulator/nifs/common/socket_nif.c | 345 +++++++++++++++++++++----------- erts/emulator/nifs/common/socket_util.c | 73 +++++++ erts/emulator/nifs/common/socket_util.h | 6 + 4 files changed, 310 insertions(+), 119 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 80a9eb0734..3595c483d7 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -142,11 +142,14 @@ extern ERL_NIF_TERM esock_atom_reliability; extern ERL_NIF_TERM esock_atom_rights; extern ERL_NIF_TERM esock_atom_scope_id; extern ERL_NIF_TERM esock_atom_sctp; +extern ERL_NIF_TERM esock_atom_sec; extern ERL_NIF_TERM esock_atom_seqpacket; +extern ERL_NIF_TERM esock_atom_socket; extern ERL_NIF_TERM esock_atom_spec_dst; extern ERL_NIF_TERM esock_atom_stream; extern ERL_NIF_TERM esock_atom_tcp; extern ERL_NIF_TERM esock_atom_throughput; +extern ERL_NIF_TERM esock_atom_timestamp; extern ERL_NIF_TERM esock_atom_tos; extern ERL_NIF_TERM esock_atom_true; extern ERL_NIF_TERM esock_atom_trunc; @@ -154,6 +157,8 @@ extern ERL_NIF_TERM esock_atom_ttl; extern ERL_NIF_TERM esock_atom_type; extern ERL_NIF_TERM esock_atom_udp; extern ERL_NIF_TERM esock_atom_undefined; +extern ERL_NIF_TERM esock_atom_unknown; +extern ERL_NIF_TERM esock_atom_usec; /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 4b4082062b..4144341d71 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -421,6 +421,11 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048 #define SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024 +#define VT2S(__VT__) (((__VT__) == SOCKET_OPT_VALUE_TYPE_UNSPEC) ? "unspec" : \ + (((__VT__) == SOCKET_OPT_VALUE_TYPE_INT) ? "int" : \ + ((__VT__) == SOCKET_OPT_VALUE_TYPE_BOOL) ? "bool" : \ + "undef")) + #define SOCKET_OPT_VALUE_TYPE_UNSPEC 0 #define SOCKET_OPT_VALUE_TYPE_INT 1 #define SOCKET_OPT_VALUE_TYPE_BOOL 2 @@ -1468,7 +1473,7 @@ static ERL_NIF_TERM ngetopt(ErlNifEnv* env, BOOLEAN_T isEncoded, BOOLEAN_T isOTP, int level, - int eOpt); + ERL_NIF_TERM eOpt); static ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); @@ -1479,7 +1484,7 @@ static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, - int eOpt); + ERL_NIF_TERM eOpt); static ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -1897,6 +1902,9 @@ extern char* encode_cmsghdrs(ErlNifEnv* env, ErlNifBinary* cmsgBinP, struct msghdr* msgHdrP, ERL_NIF_TERM* eCMsgHdr); +static char* encode_cmsghdr_level(ErlNifEnv* env, + int level, + ERL_NIF_TERM* eLevel); static char* encode_cmsghdr_type(ErlNifEnv* env, int level, int type, @@ -1909,6 +1917,13 @@ static char* encode_cmsghdr_data(ErlNifEnv* env, size_t dataPos, size_t dataLen, ERL_NIF_TERM* eCMsgHdrData); +static char* encode_cmsghdr_data_socket(ErlNifEnv* env, + ERL_NIF_TERM ctrlBuf, + int type, + unsigned char* dataP, + size_t dataPos, + size_t dataLen, + ERL_NIF_TERM* eCMsgHdrData); static char* encode_cmsghdr_data_ip(ErlNifEnv* env, ERL_NIF_TERM ctrlBuf, int type, @@ -2104,6 +2119,7 @@ static char str_association[] = "association"; static char str_assoc_id[] = "assoc_id"; static char str_authentication[] = "authentication"; // static char str_any[] = "any"; +static char str_bool[] = "bool"; static char str_close[] = "close"; static char str_closed[] = "closed"; static char str_closing[] = "closing"; @@ -2118,6 +2134,7 @@ static char str_in4_sockaddr[] = "in4_sockaddr"; static char str_in6_sockaddr[] = "in6_sockaddr"; static char str_include[] = "include"; static char str_initial[] = "initial"; +static char str_int[] = "int"; static char str_interface[] = "interface"; static char str_iow[] = "iow"; static char str_local_rwnd[] = "local_rwnd"; @@ -2149,7 +2166,6 @@ static char str_partial_delivery[] = "partial_delivery"; static char str_peer_error[] = "peer_error"; static char str_peer_rwnd[] = "peer_rwnd"; static char str_probe[] = "probe"; -static char str_sec[] = "sec"; static char str_select[] = "select"; static char str_sender_dry[] = "sender_dry"; static char str_send_failure[] = "send_failure"; @@ -2158,7 +2174,6 @@ static char str_slist[] = "slist"; static char str_sourceaddr[] = "sourceaddr"; static char str_timeout[] = "timeout"; static char str_true[] = "true"; -static char str_usec[] = "usec"; static char str_want[] = "want"; /* (special) error string constants */ @@ -2213,11 +2228,14 @@ ERL_NIF_TERM esock_atom_rights; ERL_NIF_TERM esock_atom_reliability; ERL_NIF_TERM esock_atom_scope_id; ERL_NIF_TERM esock_atom_sctp; +ERL_NIF_TERM esock_atom_sec; ERL_NIF_TERM esock_atom_seqpacket; +ERL_NIF_TERM esock_atom_socket; ERL_NIF_TERM esock_atom_spec_dst; ERL_NIF_TERM esock_atom_stream; ERL_NIF_TERM esock_atom_tcp; ERL_NIF_TERM esock_atom_throughput; +ERL_NIF_TERM esock_atom_timestamp; ERL_NIF_TERM esock_atom_tos; ERL_NIF_TERM esock_atom_true; ERL_NIF_TERM esock_atom_trunc; @@ -2226,6 +2244,7 @@ ERL_NIF_TERM esock_atom_type; ERL_NIF_TERM esock_atom_udp; ERL_NIF_TERM esock_atom_undefined; ERL_NIF_TERM esock_atom_unknown; +ERL_NIF_TERM esock_atom_usec; /* *** "Global" error (=reason) atoms *** */ ERL_NIF_TERM esock_atom_eagain; @@ -2238,6 +2257,7 @@ static ERL_NIF_TERM atom_address; static ERL_NIF_TERM atom_association; static ERL_NIF_TERM atom_assoc_id; static ERL_NIF_TERM atom_authentication; +static ERL_NIF_TERM atom_bool; static ERL_NIF_TERM atom_close; static ERL_NIF_TERM atom_closed; static ERL_NIF_TERM atom_closing; @@ -2252,6 +2272,7 @@ static ERL_NIF_TERM atom_in4_sockaddr; static ERL_NIF_TERM atom_in6_sockaddr; static ERL_NIF_TERM atom_include; static ERL_NIF_TERM atom_initial; +static ERL_NIF_TERM atom_int; static ERL_NIF_TERM atom_interface; static ERL_NIF_TERM atom_iow; static ERL_NIF_TERM atom_local_rwnd; @@ -2282,7 +2303,6 @@ static ERL_NIF_TERM atom_partial_delivery; static ERL_NIF_TERM atom_peer_error; static ERL_NIF_TERM atom_peer_rwnd; static ERL_NIF_TERM atom_probe; -static ERL_NIF_TERM atom_sec; static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_sender_dry; static ERL_NIF_TERM atom_send_failure; @@ -2291,7 +2311,6 @@ static ERL_NIF_TERM atom_slist; static ERL_NIF_TERM atom_sourceaddr; static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_true; -static ERL_NIF_TERM atom_usec; static ERL_NIF_TERM atom_want; static ERL_NIF_TERM atom_eisconn; @@ -7424,42 +7443,18 @@ ERL_NIF_TERM nsetopt_timeval_opt(ErlNifEnv* env, ERL_NIF_TERM eVal) { ERL_NIF_TERM result; - ERL_NIF_TERM eSec, eUSec; struct timeval timeVal; int res; - size_t sz; + char* xres; SSDBG( descP, ("SOCKET", "nsetopt_timeval_opt -> entry with" "\r\n eVal: %T" "\r\n", eVal) ); - // It must be a map - if (!IS_MAP(env, eVal)) - return esock_make_error(env, esock_atom_einval); - - // It must have atleast ten attributes - if (!enif_get_map_size(env, eVal, &sz) || (sz < 2)) - return esock_make_error(env, esock_atom_einval); - - SSDBG( descP, - ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> extract attributes\r\n") ); - - if (!GET_MAP_VAL(env, eVal, atom_sec, &eSec)) - return esock_make_error(env, esock_atom_einval); - - if (!GET_MAP_VAL(env, eVal, atom_usec, &eUSec)) - return esock_make_error(env, esock_atom_einval); + if ((xres = esock_decode_timeval(env, eVal, &timeVal)) != NULL) + return esock_make_error_str(env, xres); - SSDBG( descP, - ("SOCKET", "nsetopt_timeval_opt -> decode attributes\r\n") ); - - if (!GET_LONG(env, eSec, &timeVal.tv_sec)) - return esock_make_error(env, esock_atom_einval); - - if (!GET_LONG(env, eUSec, &timeVal.tv_usec)) - return esock_make_error(env, esock_atom_einval); - SSDBG( descP, ("SOCKET", "nsetopt_timeval_opt -> set timeval option\r\n") ); @@ -7668,26 +7663,26 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, { SocketDescriptor* descP; int eLevel, level = -1; - int eOpt; - ERL_NIF_TERM eIsEncoded; + ERL_NIF_TERM eIsEncoded, eOpt; BOOLEAN_T isEncoded, isOTP; SGDBG( ("SOCKET", "nif_getopt -> entry with argc: %d\r\n", argc) ); if ((argc != 4) || !enif_get_resource(env, argv[0], sockets, (void**) &descP) || - !GET_INT(env, argv[2], &eLevel) || - !GET_INT(env, argv[3], &eOpt)) { + !GET_INT(env, argv[2], &eLevel)) { + SGDBG( ("SOCKET", "nif_getopt -> failed processing args\r\n") ); return enif_make_badarg(env); } eIsEncoded = argv[1]; + eOpt = argv[3]; // Is "normally" an int, but if raw mode: {Int, ValueSz} SSDBG( descP, ("SOCKET", "nif_getopt -> args when sock = %d:" "\r\n Socket: %T" "\r\n eIsEncoded: %T" "\r\n eLevel: %d" - "\r\n eOpt: %d" + "\r\n eOpt: %T" "\r\n", descP->sock, argv[0], eIsEncoded, eLevel, eOpt) ); isEncoded = esock_decode_bool(eIsEncoded); @@ -7706,27 +7701,34 @@ ERL_NIF_TERM ngetopt(ErlNifEnv* env, BOOLEAN_T isEncoded, BOOLEAN_T isOTP, int level, - int eOpt) + ERL_NIF_TERM eOpt) { ERL_NIF_TERM result; + int opt; SSDBG( descP, ("SOCKET", "ngetopt -> entry with" - "\r\n isEncoded: %d" - "\r\n isOTP: %d" + "\r\n isEncoded: %s" + "\r\n isOTP: %s" "\r\n level: %d" - "\r\n eOpt: %d" - "\r\n", isEncoded, isOTP, level, eOpt) ); + "\r\n eOpt: %T" + "\r\n", B2S(isEncoded), B2S(isOTP), level, eOpt) ); if (isOTP) { /* These are not actual socket options, * but options for our implementation. */ - result = ngetopt_otp(env, descP, eOpt); + if (GET_INT(env, eOpt, &opt)) + result = ngetopt_otp(env, descP, opt); + else + result = esock_make_error(env, esock_atom_einval); } else if (!isEncoded) { result = ngetopt_native(env, descP, level, eOpt); } else { - result = ngetopt_level(env, descP, level, eOpt); + if (GET_INT(env, eOpt, &opt)) + result = ngetopt_level(env, descP, level, opt); + else + result = esock_make_error(env, esock_atom_einval); } SSDBG( descP, @@ -7809,7 +7811,7 @@ static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, - int eOpt) + ERL_NIF_TERM eOpt) { ERL_NIF_TERM result = enif_make_badarg(env); int opt; @@ -7819,16 +7821,23 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "ngetopt_native -> entry with" "\r\n level: %d" - "\r\n eOpt: %d" + "\r\n eOpt: %T" "\r\n", level, eOpt) ); /* - * We should really make it possible to specify common specific types, + * We should really make it possible to specify more common specific types, * such as integer or boolean (instead of the size)... * */ if (decode_native_get_opt(env, eOpt, &opt, &valueType, (int*) &valueSz)) { + + SSDBG( descP, + ("SOCKET", "ngetopt_native -> decoded opt" + "\r\n valueType: %d (%s)" + "\r\n ValueSize: %d" + "\r\n", valueType, VT2S(valueType), valueSz) ); + switch (valueType) { case SOCKET_OPT_VALUE_TYPE_UNSPEC: result = ngetopt_native_unspec(env, descP, level, opt, valueSz); @@ -7863,7 +7872,7 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, int opt, SOCKOPTLEN_T valueSz) { - ERL_NIF_TERM result = enif_make_badarg(env); + ERL_NIF_TERM result = esock_make_error(env, esock_atom_einval); int res; SSDBG( descP, @@ -7880,19 +7889,33 @@ ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, else result = esock_atom_ok; } else { + SOCKOPTLEN_T vsz = valueSz; ErlNifBinary val; - if (ALLOC_BIN(valueSz, &val)) { - res = sock_getopt(descP->sock, level, opt, val.data, &valueSz); + SSDBG( descP, ("SOCKET", "ngetopt_native_unspec -> try alloc buffer\r\n") ); + + if (ALLOC_BIN(vsz, &val)) { + int saveErrno; + res = sock_getopt(descP->sock, level, opt, val.data, &vsz); if (res != 0) { - result = esock_make_error_errno(env, sock_errno()); + saveErrno = sock_errno(); + + result = esock_make_error_errno(env, saveErrno); } else { - if (valueSz < val.size) { - if (REALLOC_BIN(&val, valueSz)) { - result = esock_make_ok2(env, MKBIN(env, &val)); - } else { - result = enif_make_badarg(env); - } + + /* Did we use all of the buffer? */ + if (vsz == val.size) { + + result = esock_make_ok2(env, MKBIN(env, &val)); + + } else { + + ERL_NIF_TERM tmp; + + tmp = MKBIN(env, &val); + tmp = MKSBIN(env, tmp, 0, vsz); + + result = esock_make_ok2(env, tmp); } } } else { @@ -9959,18 +9982,12 @@ ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, result = esock_make_error_errno(env, sock_errno()); } else { ERL_NIF_TERM eTimeVal; - ERL_NIF_TERM keys[] = {atom_sec, atom_usec}; - ERL_NIF_TERM vals[] = {MKL(env, val.tv_sec), MKL(env, val.tv_usec)}; - - unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); - unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); - - ESOCK_ASSERT( (numKeys == numVals) ); - - if (!MKMA(env, keys, vals, numKeys, &eTimeVal)) - return esock_make_error(env, esock_atom_einval); - - result = esock_make_ok2(env, eTimeVal); + char* xres; + + if ((xres = esock_encode_timeval(env, &val, &eTimeVal)) != NULL) + result = esock_make_error_str(env, xres); + else + result = esock_make_ok2(env, eTimeVal); } SSDBG( descP, @@ -10562,6 +10579,28 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, "\r\n", read, saveErrno, recvRef) ); + /* + * + * We need to handle read = 0 for other type(s) (DGRAM) when + * its actually valid to read 0 bytes. + * + * + */ + + if ((read == 0) && (descP->type == SOCK_STREAM)) { + + /* + * When a stream socket peer has performed an orderly shutdown, the return + * value will be 0 (the traditional "end-of-file" return). + * + * *We* do never actually try to read 0 bytes from a stream socket! + */ + + return esock_make_error(env, atom_closed); + + } + + /* 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. @@ -10575,7 +10614,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, /* +++ Oups - closed +++ */ - SSDBG( descP, ("SOCKET", "recvfrom_check_result -> closed\r\n") ); + SSDBG( descP, ("SOCKET", "recvmsg_check_result -> closed\r\n") ); /* * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING @@ -10599,7 +10638,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, } else if ((saveErrno == ERRNO_BLOCK) || (saveErrno == EAGAIN)) { - SSDBG( descP, ("SOCKET", "recvfrom_check_result -> eagain\r\n") ); + SSDBG( descP, ("SOCKET", "recvmsg_check_result -> eagain\r\n") ); SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); @@ -10609,7 +10648,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "recvfrom_check_result -> errno: %d\r\n", saveErrno) ); + "recvmsg_check_result -> errno: %d\r\n", saveErrno) ); return esock_make_error_errno(env, saveErrno); } @@ -10637,7 +10676,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "recvfrom_check_result -> " + "recvmsg_check_result -> " "(msghdr) encode failed: %s\r\n", xres) ); return esock_make_error_str(env, xres); @@ -10645,7 +10684,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "recvfrom_check_result -> " + "recvmsg_check_result -> " "(msghdr) encode ok: %T\r\n", eMsgHdr) ); return esock_make_ok2(env, eMsgHdr); @@ -10684,12 +10723,19 @@ char* encode_msghdr(ErlNifEnv* env, ("SOCKET", "encode_msghdr -> entry with" "\r\n read: %d" "\r\n", read) ); - - if ((xres = esock_encode_sockaddr(env, - (SocketAddress*) msgHdrP->msg_name, - msgHdrP->msg_namelen, - &addr)) != NULL) - return xres; + + /* The address is not used if we are connected, + * so check (length = 0) before we try to encodel + */ + if (msgHdrP->msg_namelen != 0) { + if ((xres = esock_encode_sockaddr(env, + (SocketAddress*) msgHdrP->msg_name, + msgHdrP->msg_namelen, + &addr)) != NULL) + return xres; + } else { + addr = esock_atom_undefined; + } SSDBG( descP, ("SOCKET", "encode_msghdr -> try encode iov\r\n") ); if ((xres = esock_encode_iov(env, @@ -10814,7 +10860,7 @@ char* encode_cmsghdrs(ErlNifEnv* env, * so if its a protocol we don't know, we return its integer * value and leave it to the user. */ - if (esock_encode_protocol(env, currentP->cmsg_level, &level) != NULL) + if (encode_cmsghdr_level(env, currentP->cmsg_level, &level) != NULL) level = MKI(env, currentP->cmsg_level); if (encode_cmsghdr_type(env, @@ -10875,6 +10921,35 @@ char* encode_cmsghdrs(ErlNifEnv* env, +/* +++ encode_cmsghdr_level +++ + * + * Encode the type part of the cmsghdr(). + * + */ + +static +char* encode_cmsghdr_level(ErlNifEnv* env, + int level, + ERL_NIF_TERM* eLevel) +{ + char* xres; + + switch (level) { + case SOL_SOCKET: + *eLevel = esock_atom_socket; + xres = NULL; + break; + + default: + xres = esock_encode_protocol(env, level, eLevel); + break; + } + + return xres; +} + + + /* +++ encode_cmsghdr_type +++ * * Encode the type part of the cmsghdr(). @@ -10892,6 +10967,12 @@ char* encode_cmsghdr_type(ErlNifEnv* env, switch (level) { case SOL_SOCKET: switch (type) { +#if defined(SO_TIMESTAMP) + case SO_TIMESTAMP: + *eType = esock_atom_timestamp; + break; +#endif + #if defined(SCM_RIGHTS) case SCM_RIGHTS: *eType = esock_atom_rights; @@ -11011,13 +11092,13 @@ char* encode_cmsghdr_data(ErlNifEnv* env, char* xres; switch (level) { - /* - #if defined(SOL_SOCKET) - case SOL_SOCKET: - xres = encode_cmsghdr_data_socket(env, type, dataP, eCMsgHdrData); - break; - #endif - */ +#if defined(SOL_SOCKET) + case SOL_SOCKET: + xres = encode_cmsghdr_data_socket(env, ctrlBuf, type, + dataP, dataPos, dataLen, + eCMsgHdrData); + break; +#endif #if defined(SOL_IP) case SOL_IP: @@ -11068,6 +11149,45 @@ char* encode_cmsghdr_data(ErlNifEnv* env, +/* +++ encode_cmsghdr_data_socket +++ + * + * Encode the data part when "protocol" = socket of the cmsghdr(). + * + */ + +static +char* encode_cmsghdr_data_socket(ErlNifEnv* env, + ERL_NIF_TERM ctrlBuf, + int type, + unsigned char* dataP, + size_t dataPos, + size_t dataLen, + ERL_NIF_TERM* eCMsgHdrData) +{ + // char* xres; + + switch (type) { +#if defined(SO_TIMESTAMP) + case SO_TIMESTAMP: + { + struct timeval* timeP = (struct timeval*) dataP; + + if (esock_encode_timeval(env, timeP, eCMsgHdrData) != NULL) + *eCMsgHdrData = MKSBIN(env, ctrlBuf, dataPos, dataLen); + } + break; +#endif + + default: + *eCMsgHdrData = MKSBIN(env, ctrlBuf, dataPos, dataLen); + break; + } + + return NULL; +} + + + /* +++ encode_cmsghdr_data_ip +++ * * Encode the data part when protocol = IP of the cmsghdr(). @@ -11089,8 +11209,8 @@ char* encode_cmsghdr_data_ip(ErlNifEnv* env, #if defined(IP_TOS) case IP_TOS: { - unsigned char tos = IPTOS_TOS(*dataP); - switch (tos) { + unsigned char tos = *dataP; + switch (IPTOS_TOS(tos)) { case IPTOS_LOWDELAY: *eCMsgHdrData = esock_atom_lowdelay; break; @@ -11551,29 +11671,21 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, return FALSE; if (IS_ATOM(env, nativeOptT[1])) { - unsigned int len; - char t[16]; // Just in case - - if (!(GET_ATOM_LEN(env, nativeOptT[1], &len) && - (len > 0) && - (len <= (sizeof("bool"))))) - return FALSE; - if (!GET_ATOM(env, nativeOptT[1], t, sizeof(t))) - return FALSE; - - if (strncmp(t, "bool", len) == 0) { - *valueType = SOCKET_OPT_VALUE_TYPE_BOOL; - *valueSz = sizeof(int); // Just to be sure - } else if (strncmp(t, "int", len) == 0) { + if (COMPARE(nativeOptT[1], atom_int) == 0) { + SGDBG( ("SOCKET", "decode_native_get_opt -> int\r\n") ); *valueType = SOCKET_OPT_VALUE_TYPE_INT; *valueSz = sizeof(int); // Just to be sure + } else if (COMPARE(nativeOptT[1], atom_bool) == 0) { + SGDBG( ("SOCKET", "decode_native_get_opt -> bool\r\n") ); + *valueType = SOCKET_OPT_VALUE_TYPE_BOOL; + *valueSz = sizeof(int); // Just to be sure } else { return FALSE; } - } else if (IS_NUM(env, nativeOptT[1])) { if (GET_INT(env, nativeOptT[1], valueSz)) { + SGDBG( ("SOCKET", "decode_native_get_opt -> unspec\r\n") ); *valueType = SOCKET_OPT_VALUE_TYPE_UNSPEC; } else { return FALSE; @@ -11582,21 +11694,12 @@ BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, return FALSE; } + SGDBG( ("SOCKET", "decode_native_get_opt -> done\r\n") ); + return TRUE; } -/* -static -void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal) -{ - if (val) - *eVal = esock_atom_true; - else - *eVal = esock_atom_false; -} -*/ - /* +++ encode the ip socket option tos +++ * The (ip) option can be provide as: @@ -12872,6 +12975,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_association = MKA(env, str_association); atom_assoc_id = MKA(env, str_assoc_id); atom_authentication = MKA(env, str_authentication); + atom_bool = MKA(env, str_bool); atom_close = MKA(env, str_close); atom_closed = MKA(env, str_closed); atom_closing = MKA(env, str_closing); @@ -12886,6 +12990,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_in6_sockaddr = MKA(env, str_in6_sockaddr); atom_include = MKA(env, str_include); atom_initial = MKA(env, str_initial); + atom_int = MKA(env, str_int); atom_interface = MKA(env, str_interface); atom_iow = MKA(env, str_iow); atom_local_rwnd = MKA(env, str_local_rwnd); @@ -12916,7 +13021,6 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_peer_rwnd = MKA(env, str_peer_rwnd); atom_peer_error = MKA(env, str_peer_error); atom_probe = MKA(env, str_probe); - atom_sec = MKA(env, str_sec); atom_select = MKA(env, str_select); atom_sender_dry = MKA(env, str_sender_dry); atom_send_failure = MKA(env, str_send_failure); @@ -12925,7 +13029,6 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_sourceaddr = MKA(env, str_sourceaddr); atom_timeout = MKA(env, str_timeout); atom_true = MKA(env, str_true); - atom_usec = MKA(env, str_usec); atom_want = MKA(env, str_want); /* Global atom(s) */ @@ -12968,11 +13071,14 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_rights = MKA(env, "rights"); esock_atom_scope_id = MKA(env, "scope_id"); esock_atom_sctp = MKA(env, "sctp"); + esock_atom_sec = MKA(env, "sec"); esock_atom_seqpacket = MKA(env, "seqpacket"); + esock_atom_socket = MKA(env, "socket"); esock_atom_spec_dst = MKA(env, "spec_dst"); esock_atom_stream = MKA(env, "stream"); esock_atom_tcp = MKA(env, "tcp"); esock_atom_throughput = MKA(env, "throughput"); + esock_atom_timestamp = MKA(env, "timestamp"); esock_atom_tos = MKA(env, "tos"); esock_atom_true = MKA(env, "true"); esock_atom_trunc = MKA(env, "trunc"); @@ -12981,6 +13087,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_udp = MKA(env, "udp"); esock_atom_undefined = MKA(env, "undefined"); esock_atom_unknown = MKA(env, "unknown"); + esock_atom_usec = MKA(env, "usec"); /* Global error codes */ esock_atom_eafnosupport = MKA(env, ESOCK_STR_EAFNOSUPPORT); diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 232e8200df..59cd1a3408 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -227,6 +227,11 @@ char* esock_encode_sockaddr(ErlNifEnv* env, { char* xres; + UDBG( ("SUTIL", "esock_encode_sockaddr -> entry with" + "\r\n family: %d" + "\r\n addrLen: %d" + "\r\n", sockAddrP->sa.sa_family, addrLen) ); + switch (sockAddrP->sa.sa_family) { case AF_INET: xres = esock_encode_sockaddr_in4(env, &sockAddrP->in4, addrLen, eSockAddr); @@ -860,6 +865,74 @@ char* esock_encode_ip6_address(ErlNifEnv* env, +/* +++ esock_encode_timeval +++ + * + * Encode a timeval struct into its erlang form, a map with two fields: + * + * sec + * usec + * + */ +extern +char* esock_encode_timeval(ErlNifEnv* env, + struct timeval* timeP, + ERL_NIF_TERM* eTime) +{ + ERL_NIF_TERM keys[] = {esock_atom_sec, esock_atom_usec}; + ERL_NIF_TERM vals[] = {MKL(env, timeP->tv_sec), MKL(env, timeP->tv_usec)}; + + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, eTime)) + return ESOCK_STR_EINVAL; + + return NULL; +} + + + +/* +++ esock_decode_timeval +++ + * + * Decode a timeval in its erlang form (a map) into its native form, + * a timeval struct. + * + */ +extern +char* esock_decode_timeval(ErlNifEnv* env, + ERL_NIF_TERM eTime, + struct timeval* timeP) +{ + ERL_NIF_TERM eSec, eUSec; + size_t sz; + + // It must be a map + if (!IS_MAP(env, eTime)) + return ESOCK_STR_EINVAL; + + // It must have atleast two attributes + if (!enif_get_map_size(env, eTime, &sz) || (sz < 2)) + return ESOCK_STR_EINVAL; + + if (!GET_MAP_VAL(env, eTime, esock_atom_sec, &eSec)) + return ESOCK_STR_EINVAL; + + if (!GET_MAP_VAL(env, eTime, esock_atom_usec, &eUSec)) + return ESOCK_STR_EINVAL; + + if (!GET_LONG(env, eSec, &timeP->tv_sec)) + return ESOCK_STR_EINVAL; + + if (!GET_LONG(env, eUSec, &timeP->tv_usec)) + return ESOCK_STR_EINVAL; + + return NULL; +} + + + /* +++ esock_decode_domain +++ * * Decode the Erlang form of the 'domain' type, that is: diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index 5d720936f3..22eed77d6e 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -110,6 +110,12 @@ char* esock_encode_ip6_address(ErlNifEnv* env, ERL_NIF_TERM* eAddr); #endif +extern char* esock_encode_timeval(ErlNifEnv* env, + struct timeval* timeP, + ERL_NIF_TERM* eTime); +extern char* esock_decode_timeval(ErlNifEnv* env, + ERL_NIF_TERM eTime, + struct timeval* timeP); extern char* esock_decode_domain(ErlNifEnv* env, ERL_NIF_TERM eDomain, -- cgit v1.2.3 From d8b1eace1cfe3184497752f345e5f6bc5def9769 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 3 Aug 2018 12:33:22 +0200 Subject: [socket-nif] Add preliminary support for sendmsg Added function sendmsg/2,3,4. Actually worked on the first try. Something must be wrong... Still no supported cmsghdr's (only support headers where the data part is already a binary, which therefor does not require any processing). So if the cmsghdrs actually work is unclear. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 526 +++++++++++++++++++++++++++++++- erts/emulator/nifs/common/socket_util.c | 59 +++- erts/emulator/nifs/common/socket_util.h | 7 + 3 files changed, 583 insertions(+), 9 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 4144341d71..0d584306f1 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -397,9 +397,10 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define IS_CONNECTING(d) \ (((d)->state & SOCKET_FLAG_CON) == SOCKET_FLAG_CON) +/* #define IS_BUSY(d) \ (((d)->state & SOCKET_FLAG_BUSY) == SOCKET_FLAG_BUSY) - +*/ #define SOCKET_SEND_FLAG_CONFIRM 0 #define SOCKET_SEND_FLAG_DONTROUTE 1 @@ -420,6 +421,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048 #define SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024 +#define SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT 1024 #define VT2S(__VT__) (((__VT__) == SOCKET_OPT_VALUE_TYPE_UNSPEC) ? "unspec" : \ (((__VT__) == SOCKET_OPT_VALUE_TYPE_INT) ? "int" : \ @@ -676,6 +678,7 @@ static unsigned long one_value = 1; recvfrom((s),(buf),(blen),(flag),(addr),(alen)) #define sock_recvmsg(s,msghdr,flag) recvmsg((s),(msghdr),(flag)) #define sock_send(s,buf,len,flag) send((s), (buf), (len), (flag)) +#define sock_sendmsg(s,msghdr,flag) sendmsg((s),(msghdr),(flag)) #define sock_sendto(s,buf,blen,flag,addr,alen) \ sendto((s),(buf),(blen),(flag),(addr),(alen)) #define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln)) @@ -773,6 +776,7 @@ typedef struct { /* +++ Config & Misc stuff +++ */ size_t rBufSz; // Read buffer size (when data length = 0 is specified) size_t rCtrlSz; // Read control buffer size + size_t wCtrlSz; // Write control buffer size BOOLEAN_T iow; // Inform On Wrap BOOLEAN_T dbg; @@ -878,6 +882,9 @@ static ERL_NIF_TERM nif_send(ErlNifEnv* env, static ERL_NIF_TERM nif_sendto(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_recv(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -953,6 +960,11 @@ static ERL_NIF_TERM nsendto(ErlNifEnv* env, int flags, SocketAddress* toAddrP, unsigned int toAddrLen); +static ERL_NIF_TERM nsendmsg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sendRef, + ERL_NIF_TERM eMsgHdr, + int flags); static ERL_NIF_TERM nrecv(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM recvRef, @@ -1902,13 +1914,30 @@ extern char* encode_cmsghdrs(ErlNifEnv* env, ErlNifBinary* cmsgBinP, struct msghdr* msgHdrP, ERL_NIF_TERM* eCMsgHdr); +extern char* decode_cmsghdrs(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + void* cmsgHdrBufP, + size_t cmsgHdrBufLen); +extern char* decode_cmsghdr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + void** bufP, + size_t* rem); static char* encode_cmsghdr_level(ErlNifEnv* env, int level, ERL_NIF_TERM* eLevel); +static char* decode_cmsghdr_level(ErlNifEnv* env, + ERL_NIF_TERM eLevel, + int* level); static char* encode_cmsghdr_type(ErlNifEnv* env, int level, int type, ERL_NIF_TERM* eType); +static char* decode_cmsghdr_type(ErlNifEnv* env, + int level, + ERL_NIF_TERM eType, + int* type); static char* encode_cmsghdr_data(ErlNifEnv* env, ERL_NIF_TERM ctrlBuf, int level, @@ -2355,6 +2384,7 @@ static SocketData data; * nif_accept(LSock, Ref) * nif_send(Sock, SendRef, Data, Flags) * nif_sendto(Sock, SendRef, Data, Dest, Flags) + * nif_sendmsg(Sock, SendRef, MsgHdr, Flags) * nif_recv(Sock, RecvRef, Length, Flags) * nif_recvfrom(Sock, RecvRef, BufSz, Flags) * nif_recvmsg(Sock, RecvRef, BufSz, CtrlSz, Flags) @@ -3641,8 +3671,8 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, &remoteAddr, remoteAddrLen); SGDBG( ("SOCKET", "nif_sendto -> done with result: " - "\r\n %T" - "\r\n", res) ); + "\r\n %T" + "\r\n", res) ); return res; } @@ -3687,6 +3717,197 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * nif_sendmsg + * + * Description: + * Send a message on a socket + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * SendRef - A unique id for this (send) request. + * MsgHdr - Message Header - data and (maybe) control and dest + * Flags - Send flags. + */ + +static +ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM res, sendRef, eMsgHdr; + SocketDescriptor* descP; + unsigned int eflags; + int flags; + + SGDBG( ("SOCKET", "nif_sendmsg -> 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) || + !IS_MAP(env, argv[2]) || + !GET_UINT(env, argv[3], &eflags)) { + return enif_make_badarg(env); + } + sendRef = argv[1]; + eMsgHdr = argv[2]; + + SSDBG( descP, + ("SOCKET", "nif_sendmsg -> args when sock = %d:" + "\r\n Socket: %T" + "\r\n sendRef: %T" + "\r\n eflags: %d" + "\r\n", + descP->sock, argv[0], sendRef, eflags) ); + + /* THIS TEST IS NOT CORRECT!!! */ + if (!IS_OPEN(descP)) + return esock_make_error(env, esock_atom_einval); + + if (!esendflags2sendflags(eflags, &flags)) + return esock_make_error(env, esock_atom_einval); + + res = nsendmsg(env, descP, sendRef, eMsgHdr, flags); + + SGDBG( ("SOCKET", "nif_sendmsg -> done with result: " + "\r\n %T" + "\r\n", res) ); + + return res; +} + + +static +ERL_NIF_TERM nsendmsg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sendRef, + ERL_NIF_TERM eMsgHdr, + int flags) +{ + ERL_NIF_TERM res, eAddr, eIOV, eCtrl; + SocketAddress addr; + struct msghdr msgHdr; + ErlNifBinary* iovBins; + struct iovec* iov; + unsigned int iovLen; + void* ctrlBuf; + size_t ctrlBufLen; + int save_errno; + ssize_t written, dataSize; + char* xres; + + if (!descP->isWritable) + return enif_make_badarg(env); + + + /* Depending on if we are *connected* or not, we require + * different things in the msghdr map. + */ + if (IS_CONNECTED(descP)) { + + /* We don't need the address */ + + msgHdr.msg_name = NULL; + msgHdr.msg_namelen = 0; + + } else { + + /* We need the address */ + + msgHdr.msg_name = (void*) &addr; + msgHdr.msg_namelen = sizeof(addr); + sys_memzero((char *) msgHdr.msg_name, msgHdr.msg_namelen); + if (!GET_MAP_VAL(env, eMsgHdr, esock_atom_addr, &eAddr)) + return esock_make_error(env, esock_atom_einval); + if ((xres = esock_decode_sockaddr(env, eAddr, + msgHdr.msg_name, + &msgHdr.msg_namelen)) != NULL) + return esock_make_error_str(env, xres); + } + + + /* Extract the (other) attributes of the msghdr map: iov and maybe ctrl */ + + /* The *mandatory* iov, which must be a list */ + if (!GET_MAP_VAL(env, eMsgHdr, esock_atom_iov, &eIOV)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_LIST_LEN(env, eIOV, &iovLen) && (iovLen > 0)) + return esock_make_error(env, esock_atom_einval); + + iovBins = MALLOC(iovLen * sizeof(ErlNifBinary)); + ESOCK_ASSERT( (iovBins != NULL) ); + + iov = MALLOC(iovLen * sizeof(struct iovec)); + ESOCK_ASSERT( (iov != NULL) ); + + /* The *opional* ctrl */ + if (GET_MAP_VAL(env, eMsgHdr, esock_atom_ctrl, &eCtrl)) { + ctrlBufLen = descP->wCtrlSz; + ctrlBuf = MALLOC(ctrlBufLen); + ESOCK_ASSERT( (ctrlBuf != NULL) ); + } else { + eCtrl = esock_atom_undefined; + ctrlBufLen = 0; + ctrlBuf = NULL; + } + + + /* Decode the iov and initiate that part of the msghdr */ + if ((xres = esock_decode_iov(env, eIOV, + iovBins, iov, iovLen, &dataSize)) != NULL) { + FREE(iovBins); + FREE(iov); + if (ctrlBuf != NULL) FREE(ctrlBuf); + return esock_make_error_str(env, xres); + } + msgHdr.msg_iov = iov; + msgHdr.msg_iovlen = iovLen; + + + /* Decode the ctrl and initiate that part of the msghdr */ + if (ctrlBuf != NULL) { + if ((xres = decode_cmsghdrs(env, descP, + eCtrl, ctrlBuf, ctrlBufLen)) != NULL) { + FREE(iovBins); + FREE(iov); + if (ctrlBuf != NULL) FREE(ctrlBuf); + return esock_make_error_str(env, xres); + } + } + msgHdr.msg_control = ctrlBuf; + msgHdr.msg_controllen = ctrlBufLen; + + + /* The msg-flags field is not used when sending, but zero it just in case */ + msgHdr.msg_flags = 0; + + + /* We ignore the wrap for the moment. + * Maybe we should issue a wrap-message to controlling process... + */ + cnt_inc(&descP->writeTries, 1); + + /* And now, finally, try to send the message */ + written = sock_sendmsg(descP->sock, &msgHdr, flags); + + if (IS_SOCKET_ERROR(written)) + save_errno = sock_errno(); + else + save_errno = -1; // The value does not actually matter in this case + + res = send_check_result(env, descP, written, dataSize, save_errno, sendRef); + + FREE(iovBins); + FREE(iov); + if (ctrlBuf != NULL) FREE(ctrlBuf); + + return res; +} + + + /* ---------------------------------------------------------------------- * nif_writev / nif_sendv * @@ -10198,13 +10419,14 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, "\r\n saveErrno: %d" "\r\n", written, dataSize, saveErrno) ); - if (written == dataSize) { + if (written >= dataSize) { cnt_inc(&descP->writePkgCnt, 1); cnt_inc(&descP->writeByteCnt, written); SSDBG( descP, - ("SOCKET", "send_check_result -> everything written - done\r\n") ); + ("SOCKET", "send_check_result -> " + "everything written (%d,%d) - done\r\n", dataSize, written) ); return esock_atom_ok; @@ -10248,7 +10470,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "send_check_result -> not entire package written\r\n") ); - return esock_make_ok2(env, enif_make_int(env, written)); + return esock_make_ok2(env, MKI(env, written)); } @@ -10791,6 +11013,7 @@ char* encode_msghdr(ErlNifEnv* env, + /* +++ encode_cmsghdrs +++ * * Encode a list of cmsghdr(). There can be 0 or more cmsghdr "blocks". @@ -10921,9 +11144,157 @@ char* encode_cmsghdrs(ErlNifEnv* env, +/* +++ decode_cmsghdrs +++ + * + * Decode a list of cmsghdr(). There can be 0 or more cmsghdr "blocks". + * + * Each element can either be a (erlang) map that needds to be decoded, + * or a (erlang) binary that just needs to be appended to the control + * buffer. + * + * Our "problem" is that we have no idea much memory we actually need. + * + */ + +extern +char* decode_cmsghdrs(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + void* cmsgHdrBufP, + size_t cmsgHdrBufLen) +{ + ERL_NIF_TERM elem, tail, list; + void* bufP; + size_t rem; + unsigned int len; + int i; + char* xres; + + if (IS_LIST(env, eCMsgHdr) && GET_LIST_LEN(env, eCMsgHdr, &len)) { + for (i = 0, list = eCMsgHdr, rem = cmsgHdrBufLen, bufP = cmsgHdrBufP; + i < len; i++) { + + /* Extract the (current) head of the (cmsg hdr) list */ + if (!GET_LIST_ELEM(env, list, &elem, &tail)) + return ESOCK_STR_EINVAL; + + if ((xres = decode_cmsghdr(env, descP, elem, &bufP, &rem)) != NULL) + return xres; + + list = tail; + } + + xres = NULL; + } else { + xres = ESOCK_STR_EINVAL; + } + + return xres; +} + + +/* +++ decode_cmsghdr +++ + * + * Decode one cmsghdr(). Put the "result" into the buffer and advance the + * pointer (of the buffer) afterwards. Also update 'rem' accordingly. + * But before the actual decode, make sure that there is enough room in + * the buffer for the cmsg header (sizeof(*hdr) < rem). + * + * The eCMsgHdr should be a map with three fields: + * + * level :: cmsghdr_level() (socket | protocol() | integer()) + * type :: cmsghdr_type() (atom() | integer()) + * What values are valid depend on the level + * data :: cmsghdr_data() (term() | binary()) + * The type of the data depends on + * level and type, but can be a binary, + * which means that the data already coded. + */ +extern +char* decode_cmsghdr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + void** bufP, + size_t* rem) +{ + if (IS_MAP(env, eCMsgHdr)) { + ERL_NIF_TERM eLevel, eType, eData; + int level, type; + char* xres; + + /* First extract all three attributes (as terms) */ + + if (!GET_MAP_VAL(env, eCMsgHdr, esock_atom_level, &eLevel)) + return ESOCK_STR_EINVAL; + + if (!GET_MAP_VAL(env, eCMsgHdr, esock_atom_type, &eType)) + return ESOCK_STR_EINVAL; + + if (!GET_MAP_VAL(env, eCMsgHdr, esock_atom_data, &eData)) + return ESOCK_STR_EINVAL; + + /* Second, decode level */ + if ((xres = decode_cmsghdr_level(env, eLevel, &level)) != NULL) + return xres; + + /* third, decode type */ + if ((xres = decode_cmsghdr_type(env, level, eType, &type)) != NULL) + return xres; + + /* And finally data + * If its a binary, we are done. Otherwise, we need to check + * level and type to know what kind of data to expect. + * + * + * + * At the moment, the only data we support is a binary... + * + * + */ + + if (IS_BIN(env, eData)) { + ErlNifBinary bin; + size_t currentRem = *rem; + + if (!GET_BIN(env, eData, &bin)) { + return ESOCK_STR_EINVAL; + } else { + int len = CMSG_LEN(bin.size); // The cmsghdr + int space = CMSG_SPACE(bin.size); // With padding + /* Make sure it fits before we copy */ + if (currentRem >= space) { + struct cmsghdr* cmsgP = (struct cmsghdr*) bufP; + + /* The header */ + cmsgP->cmsg_len = len; + cmsgP->cmsg_level = level; + cmsgP->cmsg_type = type; + + /* And the data */ + sys_memcpy(CMSG_DATA(cmsgP), bin.data, bin.size); + *bufP += space; + *rem -= space; + } else { + return ESOCK_STR_EINVAL; + } + } + } else { + + /* Here is where we should have the proper data decode */ + + return ESOCK_STR_EINVAL; + } + } else { + return ESOCK_STR_EINVAL; + } + + return NULL; +} + + /* +++ encode_cmsghdr_level +++ * - * Encode the type part of the cmsghdr(). + * Encode the level part of the cmsghdr(). * */ @@ -10950,6 +11321,38 @@ char* encode_cmsghdr_level(ErlNifEnv* env, +/* +++ decode_cmsghdr_level +++ + * + * Decode the level part of the cmsghdr(). + * + */ + +static +char* decode_cmsghdr_level(ErlNifEnv* env, + ERL_NIF_TERM eLevel, + int* level) +{ + char* xres = NULL; + + if (IS_ATOM(env, eLevel)) { + if (COMPARE(eLevel, esock_atom_socket) == 0) { + *level = SOL_SOCKET; + xres = NULL; + } else { + xres = esock_decode_protocol(env, eLevel, level); + } + } else if (IS_NUM(env, eLevel)) { + if (!GET_INT(env, eLevel, level)) + xres = ESOCK_STR_EINVAL; + } else { + xres = ESOCK_STR_EINVAL; + } + + return xres; +} + + + /* +++ encode_cmsghdr_type +++ * * Encode the type part of the cmsghdr(). @@ -11073,6 +11476,113 @@ char* encode_cmsghdr_type(ErlNifEnv* env, +/* +++ decode_cmsghdr_type +++ + * + * Decode the type part of the cmsghdr(). + * + */ + +static +char* decode_cmsghdr_type(ErlNifEnv* env, + int level, + ERL_NIF_TERM eType, + int* type) +{ + char* xres = NULL; + + switch (level) { + case SOL_SOCKET: + if (COMPARE(eType, esock_atom_timestamp) == 0) { +#if defined(SO_TIMESTAMP) + *type = SO_TIMESTAMP; +#else + xres = ESOCK_STR_EINVAL; +#endif + } else if (COMPARE(eType, esock_atom_rights) == 0) { +#if defined(SCM_RIGHTS) + *type = SCM_RIGHTS; +#else + xres = ESOCK_STR_EINVAL; +#endif + } else if (COMPARE(eType, esock_atom_credentials) == 0) { +#if defined(SCM_CREDENTIALS) + *type = SCM_CREDENTIALS; +#else + xres = ESOCK_STR_EINVAL; +#endif + } else { + xres = ESOCK_STR_EINVAL; + } + break; + + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + if (COMPARE(eType, esock_atom_tos) == 0) { +#if defined(IP_TOS) + *type = IP_TOS; +#else + xres = ESOCK_STR_EINVAL; +#endif + } else if (COMPARE(eType, esock_atom_ttl) == 0) { +#if defined(IP_TTL) + *type = IP_TTL; +#else + xres = ESOCK_STR_EINVAL; +#endif + } else if (COMPARE(eType, esock_atom_pktinfo) == 0) { +#if defined(IP_PKTINFO) + *type = IP_PKTINFO; +#else + xres = ESOCK_STR_EINVAL; +#endif + } else if (COMPARE(eType, esock_atom_origdstaddr) == 0) { +#if defined(IP_ORIGDSTADDR) + *type = IP_ORIGDSTADDR; +#else + xres = ESOCK_STR_EINVAL; +#endif + } else { + xres = ESOCK_STR_EINVAL; + } + break; + +#if defined(SOL_IPV6) + case SOL_IPV6: + xres = ESOCK_STR_EINVAL; + break; +#endif + + case IPPROTO_TCP: + xres = ESOCK_STR_EINVAL; + break; + break; + + case IPPROTO_UDP: + xres = ESOCK_STR_EINVAL; + break; + break; + +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + xres = ESOCK_STR_EINVAL; + break; + break; +#endif + + default: + xres = ESOCK_STR_EINVAL; + break; + } + + return xres; +} + + + /* +++ encode_cmsghdr_data +++ * * Encode the data part of the cmsghdr(). @@ -11787,6 +12297,7 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->rBufSz = SOCKET_RECV_BUFFER_SIZE_DEFAULT; descP->rCtrlSz = SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT; + descP->wCtrlSz = SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT; descP->iow = FALSE; descP->dbg = SOCKET_DEBUG_DEFAULT; @@ -12895,6 +13406,7 @@ ErlNifFunc socket_funcs[] = {"nif_accept", 2, nif_accept, 0}, {"nif_send", 4, nif_send, 0}, {"nif_sendto", 5, nif_sendto, 0}, + {"nif_sendmsg", 4, nif_sendmsg, 0}, {"nif_recv", 4, nif_recv, 0}, {"nif_recvfrom", 4, nif_recvfrom, 0}, {"nif_recvmsg", 5, nif_recvmsg, 0}, diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 59cd1a3408..a73b40cd29 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -71,7 +71,7 @@ static char* make_sockaddr_un(ErlNifEnv* env, /* +++ esock_encode_iov +++ * - * Encode a IO Vector. In erlang we represented this as a list of binaries. + * Encode an IO Vector. In erlang we represented this as a list of binaries. * * We iterate through the IO vector, and as long as the remaining (rem) * number of bytes is greater than the size of the current buffer, we @@ -141,6 +141,61 @@ char* esock_encode_iov(ErlNifEnv* env, +/* +++ esock_decode_iov +++ + * + * Decode an IO Vector. In erlang we represented this as a list of binaries. + * + * We assume that we have already figured out how long the iov (actually + * eIOV) is (len), and therefor allocated an array of bins and iov to be + * used. + */ + +extern +char* esock_decode_iov(ErlNifEnv* env, + ERL_NIF_TERM eIOV, + ErlNifBinary* bufs, + struct iovec* iov, + size_t len, + ssize_t* totSize) +{ + uint16_t i; + ssize_t sz; + ERL_NIF_TERM elem, tail, list; + + UDBG( ("SUTIL", "esock_decode_iov -> entry with" + "\r\n (IOV) len: %d" + "\r\n", read, len) ); + + for (i = 0, list = eIOV, sz = 0; (i < len); i++) { + + UDBG( ("SUTIL", "esock_decode_iov -> " + "\r\n iov[%d].iov_len: %d" + "\r\n rem: %d" + "\r\n", i) ); + + if (!GET_LIST_ELEM(env, list, &elem, &tail)) + return ESOCK_STR_EINVAL; + + if (IS_BIN(env, elem) && GET_BIN(env, elem, &bufs[i])) { + iov[i].iov_base = bufs[i].data; + iov[i].iov_len = bufs[i].size; + sz += bufs[i].size; + } else { + return ESOCK_STR_EINVAL; + } + + list = tail; + } + + *totSize = sz; + + UDBG( ("SUTIL", "esock_decode_msghdr -> done (%d)\r\n", sz) ); + + return NULL; +} + + + /* +++ esock_decode_sockaddr +++ * * Decode a socket address - sockaddr. In erlang its represented as @@ -1100,7 +1155,7 @@ char* esock_encode_type(ErlNifEnv* env, -/* +++ esock_decode_protocol +++ +/* +++ esock_encode_protocol +++ * * Encode the native protocol to the Erlang form, that is: * diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index 22eed77d6e..d0b3076df1 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -43,6 +43,13 @@ char* esock_encode_iov(ErlNifEnv* env, ErlNifBinary* data, ERL_NIF_TERM* eIOV); extern +char* esock_decode_iov(ErlNifEnv* env, + ERL_NIF_TERM eIOV, + ErlNifBinary* bufs, + struct iovec* iov, + size_t len, + ssize_t* totSize); +extern char* esock_decode_sockaddr(ErlNifEnv* env, ERL_NIF_TERM eSockAddr, SocketAddress* sockAddrP, -- cgit v1.2.3 From 01601a4db44b3adccfbcc07129a4584649252736 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 3 Aug 2018 15:08:30 +0200 Subject: [socket-nif] Add more data types (with doc) and (C) debug Add more debug printouts for the new sendmsg. Also added new (erlang) types with doc. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 0d584306f1..90b727eff7 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -3770,7 +3770,8 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, res = nsendmsg(env, descP, sendRef, eMsgHdr, flags); - SGDBG( ("SOCKET", "nif_sendmsg -> done with result: " + SSDBG( descP, + ("SOCKET", "nif_sendmsg -> done with result: " "\r\n %T" "\r\n", res) ); @@ -3808,6 +3809,8 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, /* We don't need the address */ + SSDBG( descP, ("SOCKET", "nsendmsg -> connected: no address\r\n") ); + msgHdr.msg_name = NULL; msgHdr.msg_namelen = 0; @@ -3820,6 +3823,11 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, sys_memzero((char *) msgHdr.msg_name, msgHdr.msg_namelen); if (!GET_MAP_VAL(env, eMsgHdr, esock_atom_addr, &eAddr)) return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, ("SOCKET", "nsendmsg -> not connected: " + "\r\n %T" + "\r\n", eAddr) ); + if ((xres = esock_decode_sockaddr(env, eAddr, msgHdr.msg_name, &msgHdr.msg_namelen)) != NULL) @@ -3836,6 +3844,8 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, if (!GET_LIST_LEN(env, eIOV, &iovLen) && (iovLen > 0)) return esock_make_error(env, esock_atom_einval); + SSDBG( descP, ("SOCKET", "nsendmsg -> iov length: %d\r\n", iovLen) ); + iovBins = MALLOC(iovLen * sizeof(ErlNifBinary)); ESOCK_ASSERT( (iovBins != NULL) ); @@ -3866,6 +3876,10 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, msgHdr.msg_iovlen = iovLen; + SSDBG( descP, ("SOCKET", + "nsendmsg -> total (iov) data size: %d\r\n", dataSize) ); + + /* Decode the ctrl and initiate that part of the msghdr */ if (ctrlBuf != NULL) { if ((xres = decode_cmsghdrs(env, descP, -- cgit v1.2.3 From ee2eadd1c61d4237ee4044260665c82edf559228 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 3 Aug 2018 18:55:30 +0200 Subject: [socket-nif] Add support for (recvmsg) control message ipv6_pktinfo Added support for (recvmsg) control message ipv6_pktinfo, for level = ipv6 and type = pktinfo. This is enabled by setting the socket option: recvpktinfo for level ipv6. Not yet tested! OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 99 +++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 12 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 90b727eff7..f7e59678bb 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -1960,6 +1960,15 @@ static char* encode_cmsghdr_data_ip(ErlNifEnv* env, size_t dataPos, size_t dataLen, ERL_NIF_TERM* eCMsgHdrData); +#if defined(SOL_IPV6) +static char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, + ERL_NIF_TERM ctrlBuf, + int type, + unsigned char* dataP, + size_t dataPos, + size_t dataLen, + ERL_NIF_TERM* eCMsgHdrData); +#endif extern char* encode_msghdr_flags(ErlNifEnv* env, SocketDescriptor* descP, int msgFlags, @@ -11419,19 +11428,19 @@ char* encode_cmsghdr_type(ErlNifEnv* env, *eType = esock_atom_tos; break; #endif - + #if defined(IP_TTL) case IP_TTL: *eType = esock_atom_ttl; break; #endif - + #if defined(IP_PKTINFO) case IP_PKTINFO: *eType = esock_atom_pktinfo; break; #endif - + #if defined(IP_ORIGDSTADDR) case IP_ORIGDSTADDR: *eType = esock_atom_origdstaddr; @@ -11443,17 +11452,23 @@ char* encode_cmsghdr_type(ErlNifEnv* env, break; } break; - + #if defined(SOL_IPV6) case SOL_IPV6: switch (type) { +#if defined(IPV6_PKTINFO) + case IPV6_PKTINFO: + *eType = esock_atom_pktinfo; + break; +#endif + default: xres = ESOCK_STR_EINVAL; break; } break; #endif - + case IPPROTO_TCP: switch (type) { default: @@ -11634,13 +11649,13 @@ char* encode_cmsghdr_data(ErlNifEnv* env, eCMsgHdrData); break; - /* - #if defined(SOL_IPV6) - case SOL_IPV6: - xres = encode_cmsghdr_data_ipv6(env, type, dataP, eCMsgHdrData); - break; - #endif - */ +#if defined(SOL_IPV6) + case SOL_IPV6: + xres = encode_cmsghdr_data_ipv6(env, ctrlBuf, type, + dataP, dataPos, dataLen, + eCMsgHdrData); + break; +#endif /* case IPPROTO_TCP: @@ -11827,6 +11842,66 @@ char* encode_cmsghdr_data_ip(ErlNifEnv* env, +/* +++ encode_cmsghdr_data_ipv6 +++ + * + * Encode the data part when protocol = IPv6 of the cmsghdr(). + * + */ +#if defined(SOL_IPV6) +static +char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, + ERL_NIF_TERM ctrlBuf, + int type, + unsigned char* dataP, + size_t dataPos, + size_t dataLen, + ERL_NIF_TERM* eCMsgHdrData) +{ + char* xres; + + switch (type) { +#if defined(IPV6_PKTINFO) + case IPV6_PKTINFO: + { + struct in6_pktinfo* pktInfoP = (struct in6_pktinfo*) dataP; + ERL_NIF_TERM ifIndex = MKI(env, pktInfoP->ipi6_ifindex); + ERL_NIF_TERM addr; + + if ((xres = esock_encode_ip6_address(env, + &pktInfoP->ipi6_addr, + &addr)) != NULL) { + *eCMsgHdrData = esock_atom_undefined; + return xres; + } + + { + ERL_NIF_TERM keys[] = {esock_atom_addr, esock_atom_ifindex}; + ERL_NIF_TERM vals[] = {addr, ifIndex}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, eCMsgHdrData)) { + *eCMsgHdrData = esock_atom_undefined; + return ESOCK_STR_EINVAL; + } + } + } + break; +#endif + + default: + *eCMsgHdrData = MKSBIN(env, ctrlBuf, dataPos, dataLen); + break; + } + + return NULL; +} +#endif + + + /* +++ encode_msghdr_flags +++ * * Encode a list of msghdr_flag(). -- cgit v1.2.3 From 929ae46220f402d6f36072c46fe27ba39ad48d1b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 13 Sep 2018 15:44:21 +0200 Subject: [socket-nif] CMsgHdr and various doc related changes Updated the (send) cmsghdr type and the handling of it (in the nif code). Still not tested! Removed the is_loaded nif function. Tried to get fix the doc build problem (socket.erl *i think*), which causes socket.html generation to fail with: "cannot find module exporting type" To solve this I tried to run dialyzer on preloaded, and ran into problems with enc_setopt_value. Update various specs and types to "solve" this (which did not work). Updated the nif-stub functions to make dialyzer happy. --- erts/emulator/nifs/common/net_nif.c | 27 +-- erts/emulator/nifs/common/socket_nif.c | 326 +++++++++++++++++++++------------ 2 files changed, 209 insertions(+), 144 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 309ad05a36..9905d99a04 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -241,9 +241,6 @@ static NetData data; extern char* erl_errno_id(int error); -static ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -439,8 +436,8 @@ static ErlNifResourceTypeInit netInit = { * * Utility and admin functions: * ---------------------------- - * nif_is_loaded/0 * nif_info/0 + * nif_command/1 * * The "proper" net functions: * ------------------------------ @@ -454,27 +451,6 @@ static ErlNifResourceTypeInit netInit = { */ -/* ---------------------------------------------------------------------- - * nif_is_loaded - * - * Description: - * This functions only purpose is to return the atom 'true'. - * This will happen *if* the (socket) nif library is loaded. - * If its not, the erlang (nif_is_loaded) will instead return - * 'false'. - */ -static -ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) -{ - if (argc != 0) - return enif_make_badarg(env); - - return esock_atom_true; -} - - /* ---------------------------------------------------------------------- * nif_info * @@ -1522,7 +1498,6 @@ static ErlNifFunc net_funcs[] = { // Some utility functions - {"nif_is_loaded", 0, nif_is_loaded, 0}, {"nif_info", 0, nif_info, 0}, {"nif_command", 1, nif_command, 0}, // Shall we let this be dirty? diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f7e59678bb..68a9730d0c 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -848,9 +848,6 @@ typedef struct { -static ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -1973,7 +1970,18 @@ extern char* encode_msghdr_flags(ErlNifEnv* env, SocketDescriptor* descP, int msgFlags, ERL_NIF_TERM* flags); - +static char* decode_cmsghdr_data(ErlNifEnv* env, + void** bufP, + size_t* rem, + int level, + int type, + ERL_NIF_TERM eData); +static char* decode_cmsghdr_final(void** bufP, + size_t* rem, + int level, + int type, + char* data, + int sz); static BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* valP); @@ -2380,7 +2388,6 @@ static SocketData data; * * Utility and admin functions: * ---------------------------- - * nif_is_loaded/0 * nif_info/0 * (nif_debug/1) * @@ -2416,27 +2423,6 @@ static SocketData data; */ -/* ---------------------------------------------------------------------- - * nif_is_loaded - * - * Description: - * This functions only purpose is to return the atom 'true'. - * This will happen *if* the (socket) nif library is loaded. - * If its not, the erlang (nif_is_loaded) will instead return - * 'false'. - */ -static -ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) -{ - if (argc != 0) - return enif_make_badarg(env); - - return atom_true; -} - - /* ---------------------------------------------------------------------- * nif_info * @@ -11046,7 +11032,7 @@ char* encode_msghdr(ErlNifEnv* env, * * The cmsgHdrP arguments points to the start of the control data buffer, * an actual binary. Its the only way to create sub-binaries. So, what we - * need to continue processing this is to tern that into an binary erlang + * need to continue processing this is to turn that into an binary erlang * term (which can then in turn be turned into sub-binaries). * * We need the cmsgBufP (even though cmsgHdrP points to it) to be able @@ -11171,7 +11157,7 @@ char* encode_cmsghdrs(ErlNifEnv* env, * * Decode a list of cmsghdr(). There can be 0 or more cmsghdr "blocks". * - * Each element can either be a (erlang) map that needds to be decoded, + * Each element can either be a (erlang) map that needs to be decoded, * or a (erlang) binary that just needs to be appended to the control * buffer. * @@ -11267,46 +11253,121 @@ char* decode_cmsghdr(ErlNifEnv* env, /* And finally data * If its a binary, we are done. Otherwise, we need to check * level and type to know what kind of data to expect. - * - * - * - * At the moment, the only data we support is a binary... - * - * */ + + return decode_cmsghdr_data(env, bufP, rem, level, type, eData); + + } else { + return ESOCK_STR_EINVAL; + } + + return NULL; +} + + +/* *** decode_cmsghdr_data *** + * + * For all combinations of level and type we accept a binary as data, + * so we begin by testing for that. If its not a binary, then we check + * level (ip) and type (tos or ttl), in which case the data *must* be + * an integer (we have already taken care of the binary). + */ +static +char* decode_cmsghdr_data(ErlNifEnv* env, + void** bufP, + size_t* rem, + int level, + int type, + ERL_NIF_TERM eData) +{ + char* xres = ESOCK_STR_EINVAL; + + if (IS_BIN(env, eData)) { + ErlNifBinary bin; - if (IS_BIN(env, eData)) { - ErlNifBinary bin; - size_t currentRem = *rem; + if (GET_BIN(env, eData, &bin)) { + return decode_cmsghdr_final(bufP, rem, level, type, + (char*) bin.data, bin.size); + } + } else { - if (!GET_BIN(env, eData, &bin)) { - return ESOCK_STR_EINVAL; - } else { - int len = CMSG_LEN(bin.size); // The cmsghdr - int space = CMSG_SPACE(bin.size); // With padding - /* Make sure it fits before we copy */ - if (currentRem >= space) { - struct cmsghdr* cmsgP = (struct cmsghdr*) bufP; - - /* The header */ - cmsgP->cmsg_len = len; - cmsgP->cmsg_level = level; - cmsgP->cmsg_type = type; - - /* And the data */ - sys_memcpy(CMSG_DATA(cmsgP), bin.data, bin.size); - *bufP += space; - *rem -= space; - } else { - return ESOCK_STR_EINVAL; + /* Its *not* a binary so we need to look at what level and type + * we have and treat them individually. + */ + + switch (level) { +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + switch (type) { +#if defined(IP_TOS) + case IP_TOS: + { + int data; + if (decode_ip_tos(env, eData, &data)) { + return decode_cmsghdr_final(bufP, rem, level, type, + (char*) &data, + sizeof(data)); + } + } + break; +#endif + +#if defined(IP_TTL) + case IP_TTL: + { + int data; + if (GET_INT(env, eData, &data)) { + return decode_cmsghdr_final(bufP, rem, level, type, + (char*) &data, + sizeof(data)); + } } + break; +#endif + } - } else { + break; + + default: + break; + } - /* Here is where we should have the proper data decode */ + } - return ESOCK_STR_EINVAL; - } + return xres; +} + + +/* *** decode_cmsghdr_final *** + * + * This does the final create of the cmsghdr (including the data copy). + */ +static +char* decode_cmsghdr_final(void** bufP, + size_t* rem, + int level, + int type, + char* data, + int sz) +{ + int currentRem = *rem; + int len = CMSG_LEN(sz); + int space = CMSG_SPACE(sz); + + if (currentRem >= space) { + struct cmsghdr* cmsgP = (struct cmsghdr*) bufP; + + /* The header */ + cmsgP->cmsg_len = len; + cmsgP->cmsg_level = level; + cmsgP->cmsg_type = type; + + sys_memcpy(CMSG_DATA(cmsgP), &data, sz); + *bufP += space; + *rem -= space; } else { return ESOCK_STR_EINVAL; } @@ -11334,8 +11395,30 @@ char* encode_cmsghdr_level(ErlNifEnv* env, xres = NULL; break; +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + *eLevel = esock_atom_ip; + xres = NULL; + break; + +#if defined(SOL_IPV6) + case SOL_IPV6: + *eLevel = esock_atom_ip; + xres = NULL; + break; +#endif + + case IPPROTO_UDP: + *eLevel = esock_atom_udp; + xres = NULL; + break; + default: - xres = esock_encode_protocol(env, level, eLevel); + *eLevel = MKI(env, level); + xres = NULL; break; } @@ -11358,17 +11441,35 @@ char* decode_cmsghdr_level(ErlNifEnv* env, char* xres = NULL; if (IS_ATOM(env, eLevel)) { + if (COMPARE(eLevel, esock_atom_socket) == 0) { *level = SOL_SOCKET; xres = NULL; + } else if (COMPARE(eLevel, esock_atom_ip) == 0) { +#if defined(SOL_IP) + *level = SOL_IP; +#else + *level = IPPROTO_IP; +#endif + xres = NULL; +#if defined(SOL_IPV6) + } else if (COMPARE(eLevel, esock_atom_ipv6) == 0) { + *level = SOL_IPV6; + xres = NULL; +#endif + } else if (COMPARE(eLevel, esock_atom_udp) == 0) { + *level = IPPROTO_UDP; + xres = NULL; } else { - xres = esock_decode_protocol(env, eLevel, level); + *level = -1; + xres = ESOCK_STR_EINVAL; } } else if (IS_NUM(env, eLevel)) { if (!GET_INT(env, eLevel, level)) xres = ESOCK_STR_EINVAL; } else { - xres = ESOCK_STR_EINVAL; + *level = -1; + xres = ESOCK_STR_EINVAL; } return xres; @@ -11521,25 +11622,13 @@ char* decode_cmsghdr_type(ErlNifEnv* env, switch (level) { case SOL_SOCKET: - if (COMPARE(eType, esock_atom_timestamp) == 0) { -#if defined(SO_TIMESTAMP) - *type = SO_TIMESTAMP; -#else - xres = ESOCK_STR_EINVAL; -#endif - } else if (COMPARE(eType, esock_atom_rights) == 0) { -#if defined(SCM_RIGHTS) - *type = SCM_RIGHTS; -#else - xres = ESOCK_STR_EINVAL; -#endif - } else if (COMPARE(eType, esock_atom_credentials) == 0) { -#if defined(SCM_CREDENTIALS) - *type = SCM_CREDENTIALS; -#else - xres = ESOCK_STR_EINVAL; -#endif + if (IS_NUM(env, eType)) { + if (!GET_INT(env, eType, type)) { + *type = -1; + xres = ESOCK_STR_EINVAL; + } } else { + *type = -1; xres = ESOCK_STR_EINVAL; } break; @@ -11550,60 +11639,62 @@ char* decode_cmsghdr_type(ErlNifEnv* env, #else case IPPROTO_IP: #endif - if (COMPARE(eType, esock_atom_tos) == 0) { + if (IS_ATOM(env, eType)) { + if (COMPARE(eType, esock_atom_tos) == 0) { #if defined(IP_TOS) - *type = IP_TOS; + *type = IP_TOS; #else - xres = ESOCK_STR_EINVAL; + xres = ESOCK_STR_EINVAL; #endif - } else if (COMPARE(eType, esock_atom_ttl) == 0) { + } else if (COMPARE(eType, esock_atom_ttl) == 0) { #if defined(IP_TTL) - *type = IP_TTL; + *type = IP_TTL; #else - xres = ESOCK_STR_EINVAL; -#endif - } else if (COMPARE(eType, esock_atom_pktinfo) == 0) { -#if defined(IP_PKTINFO) - *type = IP_PKTINFO; -#else - xres = ESOCK_STR_EINVAL; -#endif - } else if (COMPARE(eType, esock_atom_origdstaddr) == 0) { -#if defined(IP_ORIGDSTADDR) - *type = IP_ORIGDSTADDR; -#else - xres = ESOCK_STR_EINVAL; + xres = ESOCK_STR_EINVAL; #endif + } else { + xres = ESOCK_STR_EINVAL; + } + } else if (IS_NUM(env, eType)) { + if (!GET_INT(env, eType, type)) { + *type = -1; + xres = ESOCK_STR_EINVAL; + } } else { - xres = ESOCK_STR_EINVAL; + *type = -1; + xres = ESOCK_STR_EINVAL; } break; #if defined(SOL_IPV6) case SOL_IPV6: - xres = ESOCK_STR_EINVAL; + if (IS_NUM(env, eType)) { + if (!GET_INT(env, eType, type)) { + *type = -1; + xres = ESOCK_STR_EINVAL; + } + } else { + *type = -1; + xres = ESOCK_STR_EINVAL; + } break; #endif - case IPPROTO_TCP: - xres = ESOCK_STR_EINVAL; - break; - break; - case IPPROTO_UDP: - xres = ESOCK_STR_EINVAL; - break; - break; - -#if defined(HAVE_SCTP) - case IPPROTO_SCTP: - xres = ESOCK_STR_EINVAL; - break; + if (IS_NUM(env, eType)) { + if (!GET_INT(env, eType, type)) { + *type = -1; + xres = ESOCK_STR_EINVAL; + } + } else { + *type = -1; + xres = ESOCK_STR_EINVAL; + } break; -#endif default: - xres = ESOCK_STR_EINVAL; + *type = -1; + xres = ESOCK_STR_EINVAL; break; } @@ -13481,7 +13572,6 @@ static ErlNifFunc socket_funcs[] = { // Some utility functions - {"nif_is_loaded", 0, nif_is_loaded, 0}, {"nif_info", 0, nif_info, 0}, // {"nif_debug", 1, nif_debug_, 0}, -- cgit v1.2.3 From 93ed18d0b5e46c9637ff50052e1d12a66d5d40e1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 18 Sep 2018 12:38:08 +0200 Subject: [socket-nif] Encoding of cmsg headers for sendmsg Fixed various issues with regard to encode of CMsgHdrs during sendmsg. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 222 +++++++++++++++++++++++--------- erts/emulator/nifs/common/socket_util.h | 11 +- 2 files changed, 167 insertions(+), 66 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 68a9730d0c..5fca0eb58b 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -1914,13 +1914,15 @@ extern char* encode_cmsghdrs(ErlNifEnv* env, extern char* decode_cmsghdrs(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eCMsgHdr, - void* cmsgHdrBufP, - size_t cmsgHdrBufLen); + char* cmsgHdrBufP, + size_t cmsgHdrBufLen, + size_t* cmsgHdrBufUsed); extern char* decode_cmsghdr(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eCMsgHdr, - void** bufP, - size_t* rem); + char* bufP, + size_t rem, + size_t* used); static char* encode_cmsghdr_level(ErlNifEnv* env, int level, ERL_NIF_TERM* eLevel); @@ -1970,18 +1972,22 @@ extern char* encode_msghdr_flags(ErlNifEnv* env, SocketDescriptor* descP, int msgFlags, ERL_NIF_TERM* flags); -static char* decode_cmsghdr_data(ErlNifEnv* env, - void** bufP, - size_t* rem, - int level, - int type, - ERL_NIF_TERM eData); -static char* decode_cmsghdr_final(void** bufP, - size_t* rem, - int level, - int type, - char* data, - int sz); +static char* decode_cmsghdr_data(ErlNifEnv* env, + SocketDescriptor* descP, + char* bufP, + size_t rem, + int level, + int type, + ERL_NIF_TERM eData, + size_t* used); +static char* decode_cmsghdr_final(SocketDescriptor* descP, + char* bufP, + size_t rem, + int level, + int type, + char* data, + int sz, + size_t* used); static BOOLEAN_T decode_sock_linger(ErlNifEnv* env, ERL_NIF_TERM eVal, struct linger* valP); @@ -3787,8 +3793,8 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, ErlNifBinary* iovBins; struct iovec* iov; unsigned int iovLen; - void* ctrlBuf; - size_t ctrlBufLen; + char* ctrlBuf; + size_t ctrlBufLen, ctrlBufUsed; int save_errno; ssize_t written, dataSize; char* xres; @@ -3820,7 +3826,7 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, return esock_make_error(env, esock_atom_einval); SSDBG( descP, ("SOCKET", "nsendmsg -> not connected: " - "\r\n %T" + "\r\n address: %T" "\r\n", eAddr) ); if ((xres = esock_decode_sockaddr(env, eAddr, @@ -3850,15 +3856,18 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, /* The *opional* ctrl */ if (GET_MAP_VAL(env, eMsgHdr, esock_atom_ctrl, &eCtrl)) { ctrlBufLen = descP->wCtrlSz; - ctrlBuf = MALLOC(ctrlBufLen); + ctrlBuf = (char*) MALLOC(ctrlBufLen); ESOCK_ASSERT( (ctrlBuf != NULL) ); } else { eCtrl = esock_atom_undefined; ctrlBufLen = 0; ctrlBuf = NULL; } - - + SSDBG( descP, ("SOCKET", "nsendmsg -> optional ctrl: " + "\r\n ctrlBuf: 0x%lX" + "\r\n ctrlBufLen: %d" + "\r\n eCtrl: %T\r\n", ctrlBuf, ctrlBufLen, eCtrl) ); + /* Decode the iov and initiate that part of the msghdr */ if ((xres = esock_decode_iov(env, eIOV, iovBins, iov, iovLen, &dataSize)) != NULL) { @@ -3875,10 +3884,12 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, "nsendmsg -> total (iov) data size: %d\r\n", dataSize) ); - /* Decode the ctrl and initiate that part of the msghdr */ + /* Decode the ctrl and initiate that part of the msghdr. + */ if (ctrlBuf != NULL) { if ((xres = decode_cmsghdrs(env, descP, - eCtrl, ctrlBuf, ctrlBufLen)) != NULL) { + eCtrl, + ctrlBuf, ctrlBufLen, &ctrlBufUsed)) != NULL) { FREE(iovBins); FREE(iov); if (ctrlBuf != NULL) FREE(ctrlBuf); @@ -3886,7 +3897,7 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, } } msgHdr.msg_control = ctrlBuf; - msgHdr.msg_controllen = ctrlBufLen; + msgHdr.msg_controllen = ctrlBufUsed; /* The msg-flags field is not used when sending, but zero it just in case */ @@ -11169,35 +11180,64 @@ extern char* decode_cmsghdrs(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eCMsgHdr, - void* cmsgHdrBufP, - size_t cmsgHdrBufLen) + char* cmsgHdrBufP, + size_t cmsgHdrBufLen, + size_t* cmsgHdrBufUsed) { ERL_NIF_TERM elem, tail, list; - void* bufP; - size_t rem; + char* bufP; + size_t rem, used, totUsed = 0; unsigned int len; int i; char* xres; + SSDBG( descP, ("SOCKET", "decode_cmsghdrs -> entry with" + "\r\n cmsgHdrBufP: 0x%lX" + "\r\n cmsgHdrBufLen: %d" + "\r\n", cmsgHdrBufP, cmsgHdrBufLen) ); + if (IS_LIST(env, eCMsgHdr) && GET_LIST_LEN(env, eCMsgHdr, &len)) { - for (i = 0, list = eCMsgHdr, rem = cmsgHdrBufLen, bufP = cmsgHdrBufP; + + SSDBG( descP, ("SOCKET", "decode_cmsghdrs -> list length: %d\r\n", len) ); + + for (i = 0, list = eCMsgHdr, rem = cmsgHdrBufLen, bufP = cmsgHdrBufP; i < len; i++) { + SSDBG( descP, ("SOCKET", "decode_cmsghdrs -> process elem %d:" + "\r\n (buffer) rem: %u" + "\r\n (buffer) totUsed: %u" + "\r\n", i, rem, totUsed) ); + /* Extract the (current) head of the (cmsg hdr) list */ if (!GET_LIST_ELEM(env, list, &elem, &tail)) return ESOCK_STR_EINVAL; - if ((xres = decode_cmsghdr(env, descP, elem, &bufP, &rem)) != NULL) + used = 0; // Just in case... + if ((xres = decode_cmsghdr(env, descP, elem, bufP, rem, &used)) != NULL) return xres; - - list = tail; + + bufP = CHARP( ULONG(bufP) + used ); + rem = SZT( rem - used ); + list = tail; + totUsed += used; + } + SSDBG( descP, ("SOCKET", + "decode_cmsghdrs -> all %d ctrl headers processed\r\n", + len) ); + xres = NULL; } else { xres = ESOCK_STR_EINVAL; } + *cmsgHdrBufUsed = totUsed; + + SSDBG( descP, ("SOCKET", "decode_cmsghdrs -> done with %s when" + "\r\n totUsed = %u\r\n", + ((xres != NULL) ? xres : "NULL"), totUsed) ); + return xres; } @@ -11217,15 +11257,20 @@ char* decode_cmsghdrs(ErlNifEnv* env, * data :: cmsghdr_data() (term() | binary()) * The type of the data depends on * level and type, but can be a binary, - * which means that the data already coded. + * which means that the data is already coded. */ extern char* decode_cmsghdr(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eCMsgHdr, - void** bufP, - size_t* rem) + char* bufP, + size_t rem, + size_t* used) { + SSDBG( descP, ("SOCKET", "decode_cmsghdr -> entry with" + "\r\n eCMsgHdr: %T" + "\r\n", eCMsgHdr) ); + if (IS_MAP(env, eCMsgHdr)) { ERL_NIF_TERM eLevel, eType, eData; int level, type; @@ -11236,28 +11281,42 @@ char* decode_cmsghdr(ErlNifEnv* env, if (!GET_MAP_VAL(env, eCMsgHdr, esock_atom_level, &eLevel)) return ESOCK_STR_EINVAL; + SSDBG( descP, ("SOCKET", "decode_cmsghdr -> eLevel: %T" + "\r\n", eLevel) ); + if (!GET_MAP_VAL(env, eCMsgHdr, esock_atom_type, &eType)) return ESOCK_STR_EINVAL; + SSDBG( descP, ("SOCKET", "decode_cmsghdr -> eType: %T" + "\r\n", eType) ); + if (!GET_MAP_VAL(env, eCMsgHdr, esock_atom_data, &eData)) return ESOCK_STR_EINVAL; + SSDBG( descP, ("SOCKET", "decode_cmsghdr -> eData: %T" + "\r\n", eData) ); + /* Second, decode level */ if ((xres = decode_cmsghdr_level(env, eLevel, &level)) != NULL) return xres; + SSDBG( descP, ("SOCKET", "decode_cmsghdr -> level: %d\r\n", level) ); + /* third, decode type */ if ((xres = decode_cmsghdr_type(env, level, eType, &type)) != NULL) return xres; + SSDBG( descP, ("SOCKET", "decode_cmsghdr -> type: %d\r\n", type) ); + /* And finally data * If its a binary, we are done. Otherwise, we need to check * level and type to know what kind of data to expect. */ - return decode_cmsghdr_data(env, bufP, rem, level, type, eData); + return decode_cmsghdr_data(env, descP, bufP, rem, level, type, eData, used); } else { + *used = 0; return ESOCK_STR_EINVAL; } @@ -11270,24 +11329,36 @@ char* decode_cmsghdr(ErlNifEnv* env, * For all combinations of level and type we accept a binary as data, * so we begin by testing for that. If its not a binary, then we check * level (ip) and type (tos or ttl), in which case the data *must* be - * an integer (we have already taken care of the binary). + * an integer and ip_tos() respectively. */ static -char* decode_cmsghdr_data(ErlNifEnv* env, - void** bufP, - size_t* rem, - int level, - int type, - ERL_NIF_TERM eData) +char* decode_cmsghdr_data(ErlNifEnv* env, + SocketDescriptor* descP, + char* bufP, + size_t rem, + int level, + int type, + ERL_NIF_TERM eData, + size_t* used) { - char* xres = ESOCK_STR_EINVAL; + char* xres; + + SSDBG( descP, ("SOCKET", "decode_cmsghdr_data -> entry with" + "\r\n eData: %T" + "\r\n", eData) ); if (IS_BIN(env, eData)) { ErlNifBinary bin; if (GET_BIN(env, eData, &bin)) { - return decode_cmsghdr_final(bufP, rem, level, type, - (char*) bin.data, bin.size); + SSDBG( descP, ("SOCKET", "decode_cmsghdr_data -> " + "do final decode with binary\r\n") ); + return decode_cmsghdr_final(descP, bufP, rem, level, type, + (char*) bin.data, bin.size, + used); + } else { + *used = 0; + xres = ESOCK_STR_EINVAL; } } else { @@ -11307,9 +11378,15 @@ char* decode_cmsghdr_data(ErlNifEnv* env, { int data; if (decode_ip_tos(env, eData, &data)) { - return decode_cmsghdr_final(bufP, rem, level, type, + SSDBG( descP, ("SOCKET", "decode_cmsghdr_data -> " + "do final decode with tos\r\n") ); + return decode_cmsghdr_final(descP, bufP, rem, level, type, (char*) &data, - sizeof(data)); + sizeof(data), + used); + } else { + *used = 0; + xres = ESOCK_STR_EINVAL; } } break; @@ -11320,9 +11397,15 @@ char* decode_cmsghdr_data(ErlNifEnv* env, { int data; if (GET_INT(env, eData, &data)) { - return decode_cmsghdr_final(bufP, rem, level, type, + SSDBG( descP, ("SOCKET", "decode_cmsghdr_data -> " + "do final decode with ttl\r\n") ); + return decode_cmsghdr_final(descP, bufP, rem, level, type, (char*) &data, - sizeof(data)); + sizeof(data), + used); + } else { + *used = 0; + xres = ESOCK_STR_EINVAL; } } break; @@ -11332,6 +11415,8 @@ char* decode_cmsghdr_data(ErlNifEnv* env, break; default: + *used = 0; + xres = ESOCK_STR_EINVAL; break; } @@ -11346,18 +11431,25 @@ char* decode_cmsghdr_data(ErlNifEnv* env, * This does the final create of the cmsghdr (including the data copy). */ static -char* decode_cmsghdr_final(void** bufP, - size_t* rem, - int level, - int type, - char* data, - int sz) +char* decode_cmsghdr_final(SocketDescriptor* descP, + char* bufP, + size_t rem, + int level, + int type, + char* data, + int sz, + size_t* used) { - int currentRem = *rem; - int len = CMSG_LEN(sz); - int space = CMSG_SPACE(sz); + int len = CMSG_LEN(sz); + int space = CMSG_SPACE(sz); - if (currentRem >= space) { + SSDBG( descP, ("SOCKET", "decode_cmsghdr_data -> entry when" + "\r\n level: %d" + "\r\n type: %d" + "\r\n sz: %d => %d, %d" + "\r\n", level, type, sz, len, space) ); + + if (rem >= space) { struct cmsghdr* cmsgP = (struct cmsghdr*) bufP; /* The header */ @@ -11365,13 +11457,15 @@ char* decode_cmsghdr_final(void** bufP, cmsgP->cmsg_level = level; cmsgP->cmsg_type = type; - sys_memcpy(CMSG_DATA(cmsgP), &data, sz); - *bufP += space; - *rem -= space; + sys_memcpy(CMSG_DATA(cmsgP), data, sz); + *used = space; } else { + *used = 0; return ESOCK_STR_EINVAL; } + SSDBG( descP, ("SOCKET", "decode_cmsghdr_final -> done\r\n") ); + return NULL; } diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index d0b3076df1..a38453e238 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -29,8 +29,15 @@ #include #include "socket_int.h" -#define VOIDP(P) ((void*)P) -#define CHARP(P) ((char*)P) +#define CHAR(C) ((char) (C)) +#define UCHAR(C) ((unsigned char) (C)) +#define INT(I) ((int) (I)) +#define UINT(U) ((unsigned int) (U)) +#define LONG(L) ((long) (L)) +#define ULONG(L) ((unsigned long) (L)) +#define SZT(I) ((size_t) (I)) +#define VOIDP(P) ((void*) (P)) +#define CHARP(P) ((char*) (P)) #define ESOCK_ABORT(E) esock_abort(E, __func__, __FILE__, __LINE__) #define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0))) -- cgit v1.2.3 From e01a856c993b55c3fbc76fd429783d4aad5bfc80 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 19 Sep 2018 18:21:47 +0200 Subject: [socket-nif] Add proper connect and accept timeout handling Added proper connect and accept timeout handling. Made use of the enif_select(mode = cancel) feature. Each time a timeout expires, the previous operation (connect or accept) has to be cancelled (actually its the select operation that has to be cancelled). Only partial implementation of cancel for now (connect and accept). More to follow... OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 400 +++++++++++++++++++++++++++++++-- 1 file changed, 383 insertions(+), 17 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 5fca0eb58b..fde2349234 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -737,6 +737,7 @@ typedef struct { SocketAddress remote; unsigned int addrLen; + ErlNifEnv* env; /* +++ Controller (owner) process +++ */ ErlNifPid ctrlPid; @@ -777,7 +778,7 @@ typedef struct { size_t rBufSz; // Read buffer size (when data length = 0 is specified) size_t rCtrlSz; // Read control buffer size size_t wCtrlSz; // Write control buffer size - BOOLEAN_T iow; // Inform On Wrap + BOOLEAN_T iow; // Inform On (counter) Wrap BOOLEAN_T dbg; /* +++ Close stuff +++ */ @@ -915,11 +916,9 @@ 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, @@ -983,7 +982,6 @@ static ERL_NIF_TERM nclose(ErlNifEnv* env, static ERL_NIF_TERM nshutdown(ErlNifEnv* env, SocketDescriptor* descP, int how); - static ERL_NIF_TERM nsetopt(ErlNifEnv* env, SocketDescriptor* descP, BOOLEAN_T isEncoded, @@ -1823,6 +1821,38 @@ static ERL_NIF_TERM nsockname(ErlNifEnv* env, SocketDescriptor* descP); static ERL_NIF_TERM npeername(ErlNifEnv* env, SocketDescriptor* descP); +static ERL_NIF_TERM ncancel(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM op, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_connect(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_accept(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_send(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_write_select(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef, + int smode, + int rmode); static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, SocketDescriptor* descP, @@ -2089,6 +2119,9 @@ static BOOLEAN_T acceptor_pop(ErlNifEnv* env, ErlNifPid* pid, ErlNifMonitor* mon, ERL_NIF_TERM* ref); +static BOOLEAN_T acceptor_unqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid); static BOOLEAN_T qsearch4pid(ErlNifEnv* env, SocketRequestQueue* q, @@ -2241,8 +2274,10 @@ static char str_exsend[] = "exsend"; // failed send /* *** "Global" Atoms *** */ +ERL_NIF_TERM esock_atom_accept; ERL_NIF_TERM esock_atom_addr; ERL_NIF_TERM esock_atom_any; +ERL_NIF_TERM esock_atom_connect; ERL_NIF_TERM esock_atom_credentials; ERL_NIF_TERM esock_atom_ctrl; ERL_NIF_TERM esock_atom_ctrunc; @@ -2267,6 +2302,7 @@ ERL_NIF_TERM esock_atom_local; ERL_NIF_TERM esock_atom_loopback; ERL_NIF_TERM esock_atom_lowdelay; ERL_NIF_TERM esock_atom_mincost; +ERL_NIF_TERM esock_atom_not_found; ERL_NIF_TERM esock_atom_ok; ERL_NIF_TERM esock_atom_oob; ERL_NIF_TERM esock_atom_origdstaddr; @@ -2276,11 +2312,18 @@ ERL_NIF_TERM esock_atom_port; ERL_NIF_TERM esock_atom_protocol; ERL_NIF_TERM esock_atom_raw; ERL_NIF_TERM esock_atom_rdm; -ERL_NIF_TERM esock_atom_rights; +ERL_NIF_TERM esock_atom_recv; +ERL_NIF_TERM esock_atom_recvfrom; +ERL_NIF_TERM esock_atom_recvmsg; ERL_NIF_TERM esock_atom_reliability; +ERL_NIF_TERM esock_atom_rights; ERL_NIF_TERM esock_atom_scope_id; ERL_NIF_TERM esock_atom_sctp; ERL_NIF_TERM esock_atom_sec; +ERL_NIF_TERM esock_atom_select_sent; +ERL_NIF_TERM esock_atom_send; +ERL_NIF_TERM esock_atom_sendmsg; +ERL_NIF_TERM esock_atom_sendto; ERL_NIF_TERM esock_atom_seqpacket; ERL_NIF_TERM esock_atom_socket; ERL_NIF_TERM esock_atom_spec_dst; @@ -3228,7 +3271,8 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, &descP->currentAcceptor.mon) > 0) return esock_make_error(env, atom_exmon); - descP->currentAcceptor.ref = ref; + descP->currentAcceptor.ref = enif_make_copy(descP->env, ref); + descP->currentAcceptorP = &descP->currentAcceptor; SELECT(env, descP->sock, @@ -3326,7 +3370,8 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, /* *** naccept_accepting *** * We have an active acceptor and possibly acceptors waiting in queue. - * At the moment the queue is *not* implemented. + * If the pid of the calling process is not the pid of the "current process", + * push the requester onto the queue. */ static ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, @@ -3353,11 +3398,8 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, "\r\n", caller, descP->currentAcceptor.pid) ); if (!compare_pids(env, &descP->currentAcceptor.pid, &caller)) { - /* This will have to do until we implement the queue. - * When we have the queue, we should simply push this request, - * and instead return with eagain (the caller will then wait - * for the select message). - */ + + /* Not the "current caller", so (maybe) push onto queue */ SSDBG( descP, ("SOCKET", "naccept_accepting -> not (active) acceptor\r\n") ); @@ -10419,6 +10461,304 @@ ERL_NIF_TERM npeername(ErlNifEnv* env, +/* ---------------------------------------------------------------------- + * nif_cancel + * + * Description: + * Cancel a previous select! + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * Operation (atom) - What kind of operation (accept, send, ...) is to be cancelled + * Ref (ref) - Unique id for the operation + */ +static +ERL_NIF_TERM nif_cancel(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + SocketDescriptor* descP; + ERL_NIF_TERM op, opRef, result; + + SGDBG( ("SOCKET", "nif_cancel -> entry with argc: %d\r\n", argc) ); + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 3) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + op = argv[1]; + opRef = argv[2]; + + SSDBG( descP, + ("SOCKET", "nif_cancel -> args when sock = %d:" + "\r\n op: %T" + "\r\n opRef: %T" + "\r\n", descP->sock, op, opRef) ); + + result = ncancel(env, descP, op, opRef); + + SSDBG( descP, + ("SOCKET", "nif_cancel -> done with result: " + "\r\n %T" + "\r\n", result) ); + + return result; + +} + + +static +ERL_NIF_TERM ncancel(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM op, + ERL_NIF_TERM opRef) +{ + /* + * + * Do we really need all these variants? Should it not be enough with: + * + * connect | accept | send | recv + * + * + */ + if (COMPARE(op, esock_atom_connect) == 0) { + return ncancel_connect(env, descP, opRef); + } else if (COMPARE(op, esock_atom_accept) == 0) { + return ncancel_accept(env, descP, opRef); + } else if (COMPARE(op, esock_atom_send) == 0) { + return ncancel_send(env, descP, opRef); + } else if (COMPARE(op, esock_atom_sendto) == 0) { + return ncancel_send(env, descP, opRef); + } else if (COMPARE(op, esock_atom_sendmsg) == 0) { + return ncancel_send(env, descP, opRef); + } else if (COMPARE(op, esock_atom_recv) == 0) { + return ncancel_recv(env, descP, opRef); + } else if (COMPARE(op, esock_atom_recvfrom) == 0) { + return ncancel_recv(env, descP, opRef); + } else if (COMPARE(op, esock_atom_recvmsg) == 0) { + return ncancel_recv(env, descP, opRef); + } else { + return esock_make_error(env, esock_atom_einval); + } +} + + + +/* *** ncancel_connect *** + * + * + */ +static +ERL_NIF_TERM ncancel_connect(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef) +{ + return ncancel_write_select(env, descP, opRef); +} + + +/* *** ncancel_accept *** + * + * We have two different cases: + * *) Its the current acceptor + * Cancel the select! + * We need to activate one of the waiting acceptors. + * *) Its one of the acceptors ("waiting") in the queue + * Simply remove the acceptor from the queue. + * + */ +static +ERL_NIF_TERM ncancel_accept(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef) +{ + ERL_NIF_TERM res; + + SSDBG( descP, + ("SOCKET", "ncancel_accept -> entry with" + "\r\n opRef: %T" + "\r\n %s" + "\r\n", opRef, + ((descP->currentAcceptorP == NULL) ? "without acceptor" : "with acceptor")) ); + + MLOCK(descP->accMtx); + + if (descP->currentAcceptorP != NULL) { + if (COMPARE(opRef, descP->currentAcceptor.ref) == 0) { + res = ncancel_accept_current(env, descP); + } else { + res = ncancel_accept_waiting(env, descP, opRef); + } + } else { + /* Or badarg? */ + res = esock_make_error(env, esock_atom_einval); + } + + MUNLOCK(descP->accMtx); + + SSDBG( descP, + ("SOCKET", "ncancel_accept -> done with result:" + "\r\n %T" + "\r\n", res) ); + + return res; +} + + +/* The current process has an ongoing select we first must + * cancel. Then we must re-activate the "first" (the first + * in the acceptor queue). + */ +static +ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM res; + + SSDBG( descP, ("SOCKET", "ncancel_accept_current -> entry\r\n") ); + + res = ncancel_read_select(env, descP, descP->currentAcceptor.ref); + + SSDBG( descP, ("SOCKET", "ncancel_accept_current -> cancel res: %T\r\n", res) ); + + if (acceptor_pop(env, descP, + &descP->currentAcceptor.pid, + &descP->currentAcceptor.mon, + &descP->currentAcceptor.ref)) { + + /* There was another one */ + + SSDBG( descP, ("SOCKET", "ncancel_accept_current -> new (active) acceptor: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentAcceptor.pid, + descP->currentAcceptor.ref) ); + + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, &descP->currentAcceptor.pid, descP->currentAcceptor.ref); + + } else { + SSDBG( descP, ("SOCKET", "ncancel_accept_current -> no more acceptors\r\n") ); + descP->currentAcceptorP = NULL; + descP->state = SOCKET_STATE_LISTENING; + } + + SSDBG( descP, ("SOCKET", "ncancel_accept_current -> done with result:" + "\r\n %T" + "\r\n", res) ); + + return res; +} + + +/* These processes have not performed a select, so we can simply + * remove them from the acceptor queue. + */ +static +ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef) +{ + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) + return esock_make_error(env, atom_exself); + + /* unqueue request from (acceptor) queue */ + + if (acceptor_unqueue(env, descP, &caller)) { + return esock_atom_ok; + } else { + /* Race? */ + return esock_make_error(env, esock_atom_not_found); + } +} + + + +/* *** ncancel_send *** + * + * + */ +static +ERL_NIF_TERM ncancel_send(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef) +{ + return esock_make_error(env, esock_atom_einval); +} + + + +/* *** ncancel_recv *** + * + * + */ +static +ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef) +{ + return esock_make_error(env, esock_atom_einval); +} + + + +static +ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef) +{ + return ncancel_mode_select(env, descP, opRef, + ERL_NIF_SELECT_READ, + ERL_NIF_SELECT_READ_CANCELLED); +} + + +static +ERL_NIF_TERM ncancel_write_select(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef) +{ + return ncancel_mode_select(env, descP, opRef, + ERL_NIF_SELECT_WRITE, + ERL_NIF_SELECT_WRITE_CANCELLED); +} + + +static +ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef, + int smode, + int rmode) +{ + int selectRes = enif_select(env, descP->sock, + (ERL_NIF_SELECT_CANCEL | smode), + descP, NULL, opRef); + + if (selectRes & rmode) { + /* Was cancelled */ + return esock_atom_ok; + } else if (selectRes > 0) { + /* Has already sent the message */ + return esock_make_error(env, esock_atom_select_sent); + } else { + /* Stopped? */ + SSDBG( descP, ("SOCKET", "ncancel_mode_select -> failed: %d (0x%lX)" + "\r\n", selectRes, selectRes) ); + return esock_make_error(env, esock_atom_einval); + } + +} + + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -10537,8 +10877,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, } /* 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. + * zero (0) (only for type =/= stream). + * That means that we reads as much as we can, using the default + * read buffer size. */ if (bufP->size == read) { @@ -12537,6 +12878,8 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) if ((descP = enif_alloc_resource(sockets, sizeof(SocketDescriptor))) != NULL) { char buf[64]; /* Buffer used for building the mutex name */ + descP->env = enif_alloc_env(); + sprintf(buf, "socket[w,%d]", sock); descP->writeMtx = MCREATE(buf); descP->currentWriterP = NULL; // currentWriter not used @@ -13161,7 +13504,7 @@ ERL_NIF_TERM acceptor_push(ErlNifEnv* env, SocketRequestor* reqP = &e->data; reqP->pid = pid; - reqP->ref = ref; + reqP->ref = enif_make_copy(descP->env, ref); if (MONP(env, descP, &pid, &reqP->mon) > 0) { FREE(reqP); @@ -13205,6 +13548,19 @@ BOOLEAN_T acceptor_pop(ErlNifEnv* env, } +/* *** acceptor unqueue *** + * + * Remove an acceptor from the acceptor queue. + */ +static +BOOLEAN_T acceptor_unqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid) +{ + return qunqueue(env, &descP->acceptorsQ, pid); +} + + static BOOLEAN_T qsearch4pid(ErlNifEnv* env, SocketRequestQueue* q, @@ -13647,7 +14003,7 @@ void socket_down(ErlNifEnv* env, "socket_down -> " "not current acceptor - maybe a waiting acceptor\r\n") ); - qunqueue(env, &descP->acceptorsQ, pid); + acceptor_unqueue(env, descP, pid); } } @@ -13697,7 +14053,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", 3, nif_cancel, 0}, {"nif_finalize_close", 1, nif_finalize_close, ERL_NIF_DIRTY_JOB_IO_BOUND} }; @@ -13817,8 +14173,10 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_want = MKA(env, str_want); /* Global atom(s) */ + esock_atom_accept = MKA(env, "accept"); esock_atom_addr = MKA(env, "addr"); esock_atom_any = MKA(env, "any"); + esock_atom_connect = MKA(env, "connect"); esock_atom_credentials = MKA(env, "credentials"); esock_atom_ctrl = MKA(env, "ctrl"); esock_atom_ctrunc = MKA(env, "ctrunc"); @@ -13843,6 +14201,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_loopback = MKA(env, "loopback"); esock_atom_lowdelay = MKA(env, "lowdelay"); esock_atom_mincost = MKA(env, "mincost"); + esock_atom_not_found = MKA(env, "not_found"); esock_atom_ok = MKA(env, "ok"); esock_atom_oob = MKA(env, "oob"); esock_atom_origdstaddr = MKA(env, "origdstaddr"); @@ -13852,11 +14211,18 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_protocol = MKA(env, "protocol"); esock_atom_raw = MKA(env, "raw"); esock_atom_rdm = MKA(env, "rdm"); + esock_atom_recv = MKA(env, "recv"); + esock_atom_recvfrom = MKA(env, "recvfrom"); + esock_atom_recvmsg = MKA(env, "recvmsg"); esock_atom_reliability = MKA(env, "reliability"); esock_atom_rights = MKA(env, "rights"); esock_atom_scope_id = MKA(env, "scope_id"); esock_atom_sctp = MKA(env, "sctp"); esock_atom_sec = MKA(env, "sec"); + esock_atom_select_sent = MKA(env, "select_sent"); + esock_atom_send = MKA(env, "send"); + esock_atom_sendmsg = MKA(env, "sendmsg"); + esock_atom_sendto = MKA(env, "sendto"); esock_atom_seqpacket = MKA(env, "seqpacket"); esock_atom_socket = MKA(env, "socket"); esock_atom_spec_dst = MKA(env, "spec_dst"); -- cgit v1.2.3 From 13d10bc60a41f98647d802524ea8ef8fa9af6b39 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 20 Sep 2018 15:37:37 +0200 Subject: [socket-nif] Add proper send timeout handling Added proper send timeout handling. Made use of the enif_select(mode = cancel) feature. Each time a timeout expires, the "active" send (the surrent write select) has to be cancelled. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 384 +++++++++++++++++++++++++++++++-- 1 file changed, 363 insertions(+), 21 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index fde2349234..04c3609b32 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -1839,6 +1839,11 @@ static ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, static ERL_NIF_TERM ncancel_send(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef); static ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM opRef); @@ -1894,6 +1899,10 @@ static ERL_NIF_TERM ngetopt_timeval_opt(ErlNifEnv* env, int level, int opt); +static BOOLEAN_T send_check_writer(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ERL_NIF_TERM* checkResult); static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, ssize_t written, @@ -2123,6 +2132,22 @@ static BOOLEAN_T acceptor_unqueue(ErlNifEnv* env, SocketDescriptor* descP, const ErlNifPid* pid); +static BOOLEAN_T writer_search4pid(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid); +static ERL_NIF_TERM writer_push(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid pid, + ERL_NIF_TERM ref); +static BOOLEAN_T writer_pop(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid, + ErlNifMonitor* mon, + ERL_NIF_TERM* ref); +static BOOLEAN_T writer_unqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid); + static BOOLEAN_T qsearch4pid(ErlNifEnv* env, SocketRequestQueue* q, ErlNifPid* pid); @@ -3399,7 +3424,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, if (!compare_pids(env, &descP->currentAcceptor.pid, &caller)) { - /* Not the "current caller", so (maybe) push onto queue */ + /* Not the "current acceptor", so (maybe) push onto queue */ SSDBG( descP, ("SOCKET", "naccept_accepting -> not (active) acceptor\r\n") ); @@ -3459,6 +3484,8 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "naccept_accepting -> accept success\r\n") ); + DEMONP(env, descP, &descP->currentAcceptor.mon); + if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) { save_errno = sock_errno(); while ((sock_close(accSock) == INVALID_SOCKET) && @@ -3499,10 +3526,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, accDescP->state = SOCKET_STATE_CONNECTED; - /* Here we should have the test if we have something in the queue. - * And if so, pop it and copy the (waiting) acceptor, and then - * make a new select with that info). - */ + /* Check if there are waiting acceptors (popping the acceptor queue) */ if (acceptor_pop(env, descP, &descP->currentAcceptor.pid, @@ -3607,12 +3631,12 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, } -/* What do we do when another process tries to write - * when the current writer has a select already waiting? - * Queue it? And what about simultaneous read and write? - * Queue up all operations towards the socket? +/* *** nsend *** * - * We (may) need a currentOp field and an ops queue field. + * Do the actual send. + * Do some initial writer checks, do the actual send and then + * analyze the result. If we are done, another writer may be + * scheduled (if there is one in the writer queue). */ static ERL_NIF_TERM nsend(ErlNifEnv* env, @@ -3621,12 +3645,17 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, ErlNifBinary* sndDataP, int flags) { - int save_errno; - ssize_t written; + int save_errno; + ssize_t written; + ERL_NIF_TERM writerCheck; if (!descP->isWritable) return enif_make_badarg(env); + /* Check if there is already a current writer and if its us */ + if (!send_check_writer(env, descP, sendRef, &writerCheck)) + return writerCheck; + /* We ignore the wrap for the moment. * Maybe we should issue a wrap-message to controlling process... */ @@ -3645,6 +3674,7 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, } + /* ---------------------------------------------------------------------- * nif_sendto * @@ -3710,9 +3740,13 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, &remoteAddrLen)) != NULL) return esock_make_error_str(env, xres); + MLOCK(descP->writeMtx); + res = nsendto(env, descP, sendRef, &sndData, flags, &remoteAddr, remoteAddrLen); + MUNLOCK(descP->writeMtx); + SGDBG( ("SOCKET", "nif_sendto -> done with result: " "\r\n %T" "\r\n", res) ); @@ -3730,12 +3764,17 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, SocketAddress* toAddrP, unsigned int toAddrLen) { - int save_errno; - ssize_t written; + int save_errno; + ssize_t written; + ERL_NIF_TERM writerCheck; if (!descP->isWritable) return enif_make_badarg(env); + /* Check if there is already a current writer and if its us */ + if (!send_check_writer(env, descP, sendRef, &writerCheck)) + return writerCheck; + /* We ignore the wrap for the moment. * Maybe we should issue a wrap-message to controlling process... */ @@ -3811,8 +3850,12 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, if (!esendflags2sendflags(eflags, &flags)) return esock_make_error(env, esock_atom_einval); + MLOCK(descP->writeMtx); + res = nsendmsg(env, descP, sendRef, eMsgHdr, flags); + MUNLOCK(descP->writeMtx); + SSDBG( descP, ("SOCKET", "nif_sendmsg -> done with result: " "\r\n %T" @@ -3839,12 +3882,16 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, size_t ctrlBufLen, ctrlBufUsed; int save_errno; ssize_t written, dataSize; + ERL_NIF_TERM writerCheck; char* xres; if (!descP->isWritable) return enif_make_badarg(env); - + /* Check if there is already a current writer and if its us */ + if (!send_check_writer(env, descP, sendRef, &writerCheck)) + return writerCheck; + /* Depending on if we are *connected* or not, we require * different things in the msghdr map. */ @@ -10607,7 +10654,7 @@ ERL_NIF_TERM ncancel_accept(ErlNifEnv* env, } -/* The current process has an ongoing select we first must +/* The current acceptor process has an ongoing select we first must * cancel. Then we must re-activate the "first" (the first * in the acceptor queue). */ @@ -10664,7 +10711,7 @@ ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM opRef) { - ErlNifPid caller; + ErlNifPid caller; if (enif_self(env, &caller) == NULL) return esock_make_error(env, atom_exself); @@ -10683,14 +10730,117 @@ ERL_NIF_TERM ncancel_accept_waiting(ErlNifEnv* env, /* *** ncancel_send *** * - * + * Cancel a send operation. + * Its either the current writer or one of the waiting writers. */ static ERL_NIF_TERM ncancel_send(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM opRef) { - return esock_make_error(env, esock_atom_einval); + ERL_NIF_TERM res; + + SSDBG( descP, + ("SOCKET", "ncancel_send -> entry with" + "\r\n opRef: %T" + "\r\n %s" + "\r\n", opRef, + ((descP->currentWriterP == NULL) ? "without writer" : "with writer")) ); + + MLOCK(descP->writeMtx); + + if (descP->currentWriterP != NULL) { + if (COMPARE(opRef, descP->currentWriter.ref) == 0) { + res = ncancel_send_current(env, descP); + } else { + res = ncancel_send_waiting(env, descP, opRef); + } + } else { + /* Or badarg? */ + res = esock_make_error(env, esock_atom_einval); + } + + MUNLOCK(descP->writeMtx); + + SSDBG( descP, + ("SOCKET", "ncancel_send -> done with result:" + "\r\n %T" + "\r\n", res) ); + + return res; +} + + + +/* The current writer process has an ongoing select we first must + * cancel. Then we must re-activate the "first" (the first + * in the writer queue). + */ +static +ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM res; + + SSDBG( descP, ("SOCKET", "ncancel_send_current -> entry\r\n") ); + + res = ncancel_write_select(env, descP, descP->currentWriter.ref); + + SSDBG( descP, ("SOCKET", "ncancel_send_current -> cancel res: %T\r\n", res) ); + + if (writer_pop(env, descP, + &descP->currentWriter.pid, + &descP->currentWriter.mon, + &descP->currentWriter.ref)) { + + /* There was another one */ + + SSDBG( descP, ("SOCKET", "ncancel_send_current -> new (active) writer: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentWriter.pid, + descP->currentWriter.ref) ); + + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_WRITE), + descP, &descP->currentWriter.pid, descP->currentWriter.ref); + + } else { + SSDBG( descP, ("SOCKET", "ncancel_send_current -> no more writers\r\n") ); + descP->currentWriterP = NULL; + } + + SSDBG( descP, ("SOCKET", "ncancel_send_current -> done with result:" + "\r\n %T" + "\r\n", res) ); + + return res; +} + + +/* These processes have not performed a select, so we can simply + * remove them from the writer queue. + */ +static +ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef) +{ + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) + return esock_make_error(env, atom_exself); + + /* unqueue request from (writer) queue */ + + if (writer_unqueue(env, descP, &caller)) { + return esock_atom_ok; + } else { + /* Race? */ + return esock_make_error(env, esock_atom_not_found); + } } @@ -10764,6 +10914,66 @@ ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, * ---------------------------------------------------------------------- */ +/* *** send_check_writer *** + * + * Checks if we have a current writer and if that is us. If not, then we must + * be made to wait for our turn. This is done by pushing us unto the writer queue. + */ +static +BOOLEAN_T send_check_writer(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ERL_NIF_TERM* checkResult) +{ + if (descP->currentWriterP != NULL) { + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) { + *checkResult = esock_make_error(env, atom_exself); + return FALSE; + } + + if (!compare_pids(env, &descP->currentWriter.pid, &caller)) { + /* Not the "current writer", so (maybe) push onto queue */ + + SSDBG( descP, + ("SOCKET", "send_check_writer -> not (current) writer\r\n") ); + + if (!writer_search4pid(env, descP, &caller)) + *checkResult = writer_push(env, descP, caller, ref); + else + *checkResult = esock_make_error(env, esock_atom_eagain); + + SSDBG( descP, + ("SOCKET", + "nsend -> queue (push) result: %T\r\n", checkResult) ); + + return FALSE; + + } + + } + + *checkResult = esock_atom_ok; // Does not actually matter in this case, but ... + + return TRUE; +} + + + +/* *** send_check_result *** + * + * Check the result of a socket send (send, sendto and sendmsg) call. + * If a "complete" send has been made, the next (waiting) writer will be + * scheduled (if there is one). + * If we did not manage to send the entire package, make another select, + * so that we can be informed when we can make another try (to send the rest), + * and return with the amount we actually managed to send (its up to the caller + * (that is the erlang code) to figure out hust much is left to send). + * If the write fail, we give up and return with the appropriate error code. + * + * What about the remaining writers!! + */ static ERL_NIF_TERM send_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -10783,24 +10993,67 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, cnt_inc(&descP->writePkgCnt, 1); cnt_inc(&descP->writeByteCnt, written); + DEMONP(env, descP, &descP->currentWriter.mon); SSDBG( descP, ("SOCKET", "send_check_result -> " "everything written (%d,%d) - done\r\n", dataSize, written) ); + /* Ok, this write is done maybe activate the next (if any) */ + + if (writer_pop(env, descP, + &descP->currentWriter.pid, + &descP->currentWriter.mon, + &descP->currentWriter.ref)) { + + /* There was another one */ + + SSDBG( descP, ("SOCKET", "send_check_result -> new (active) writer: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentWriter.pid, + descP->currentWriter.ref) ); + + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_WRITE), + descP, &descP->currentWriter.pid, descP->currentWriter.ref); + + } else { + descP->currentWriterP = NULL; + } + return esock_atom_ok; } else if (written < 0) { - /* Ouch, check what kind of failure */ + /* Some kind of send failure - check what kind */ + if ((saveErrno != EAGAIN) && (saveErrno != EINTR)) { + ErlNifPid pid; + ErlNifMonitor mon; + ERL_NIF_TERM ref, res; + + /* + * An actual failure - we (and everyone waiting) give up + */ cnt_inc(&descP->writeFails, 1); SSDBG( descP, ("SOCKET", "send_check_result -> error: %d\r\n", saveErrno) ); - return esock_make_error_errno(env, saveErrno); + res = esock_make_error_errno(env, saveErrno); + + while (writer_pop(env, descP, &pid, &mon, &ref)) { + SSDBG( descP, + ("SOCKET", "send_check_result -> abort %T\r\n", pid) ); + send_msg_nif_abort(env, ref, res, &pid); + DEMONP(env, descP, &mon); + } + + return res; } else { @@ -13561,6 +13814,95 @@ BOOLEAN_T acceptor_unqueue(ErlNifEnv* env, } + +/* *** writer search for pid *** + * + * Search for a pid in the writer queue. + */ +static +BOOLEAN_T writer_search4pid(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid) +{ + return qsearch4pid(env, &descP->writersQ, pid); +} + + +/* *** writer push *** + * + * Push an writer onto the writer queue. + * This happens when we already have atleast one current writer. + */ +static +ERL_NIF_TERM writer_push(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid pid, + ERL_NIF_TERM ref) +{ + SocketRequestQueueElement* e = MALLOC(sizeof(SocketRequestQueueElement)); + SocketRequestor* reqP = &e->data; + + reqP->pid = pid; + reqP->ref = enif_make_copy(descP->env, ref); + + if (MONP(env, descP, &pid, &reqP->mon) > 0) { + FREE(reqP); + return esock_make_error(env, atom_exmon); + } + + qpush(&descP->writersQ, e); + + // THIS IS OK => MAKES THE CALLER WAIT FOR ITS TURN + return esock_make_error(env, esock_atom_eagain); +} + + +/* *** writer pop *** + * + * Pop an writer from the writer queue. + */ +static +BOOLEAN_T writer_pop(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid, + ErlNifMonitor* mon, + ERL_NIF_TERM* ref) +{ + SocketRequestQueueElement* e = qpop(&descP->writersQ); + + if (e != NULL) { + *pid = e->data.pid; + *mon = e->data.mon; + *ref = e->data.ref; // At this point the ref has already been copied (env) + FREE(e); + return TRUE; + } else { + /* (acceptors) Queue was empty */ + // *pid = NULL; we have no null value for pids + // *mon = NULL; we have no null value for monitors + *ref = esock_atom_undefined; // Just in case + return FALSE; + } + +} + + +/* *** writer unqueue *** + * + * Remove an writer from the writer queue. + */ +static +BOOLEAN_T writer_unqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid) +{ + return qunqueue(env, &descP->writersQ, pid); +} + + + + + static BOOLEAN_T qsearch4pid(ErlNifEnv* env, SocketRequestQueue* q, -- cgit v1.2.3 From e3ace52f8e7d893d170ff5eb60d22c27ab9ec1e5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 20 Sep 2018 17:42:03 +0200 Subject: [socket-nif] Various fixes related to FreeBSD environment --- erts/emulator/nifs/common/socket_nif.c | 61 +++++++++++++++++++++++++++------ erts/emulator/nifs/common/socket_util.c | 4 +++ 2 files changed, 55 insertions(+), 10 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 5fca0eb58b..c98ab1e514 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -362,6 +362,15 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; /* *** Misc macros and defines *** */ +/* This macro exist on some (linux) platforms */ +#if !defined(IPTOS_TOS_MASK) +#define IPTOS_TOS_MASK 0x1E +#endif +#if !defined(IPTOS_TOS) +#define IPTOS_TOS(tos) ((tos)&IPTOS_TOS_MASK) +#endif + + #if defined(TCP_CA_NAME_MAX) #define SOCKET_OPT_TCP_CONGESTION_NAME_MAX TCP_CA_NAME_MAX #else @@ -1158,7 +1167,7 @@ static ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); #endif -#if defined(IP_MSFILTER) +#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) static ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); @@ -1641,7 +1650,7 @@ static ERL_NIF_TERM ngetopt_lvl_ip_nodefrag(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_pktinfo(ErlNifEnv* env, SocketDescriptor* descP); #endif -#if defined(IP_RECVDSTADDRS) +#if defined(IP_RECVDSTADDR) static ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(ErlNifEnv* env, SocketDescriptor* descP); #endif @@ -5461,7 +5470,7 @@ ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, break; #endif -#if defined(IP_MSFILTER) +#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) case SOCKET_OPT_IP_MSFILTER: result = nsetopt_lvl_ip_msfilter(env, descP, eVal); break; @@ -5777,7 +5786,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_minttl(ErlNifEnv* env, * * The value can be *either* the atom 'null' or a map of type ip_msfilter(). */ -#if defined(IP_MSFILTER) +#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) static ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, SocketDescriptor* descP, @@ -7219,7 +7228,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nsetopt_lvl_sctp_associnfo -> decode attributes\r\n") ); - if (!GET_INT(env, eAssocId, &assocParams.sasoc_assoc_id)) + if (!GET_UINT(env, eAssocId, &assocParams.sasoc_assoc_id)) return esock_make_error(env, esock_atom_einval); /* @@ -7547,7 +7556,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> decode attributes\r\n") ); - if (!GET_INT(env, eAssocId, &rtoInfo.srto_assoc_id)) + if (!GET_UINT(env, eAssocId, &rtoInfo.srto_assoc_id)) return esock_make_error(env, esock_atom_einval); if (!GET_UINT(env, eInitial, &rtoInfo.srto_initial)) @@ -7744,7 +7753,7 @@ BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, #if defined(SOL_IP) *level = SOL_IP; #else - *level = IPROTO_IP; + *level = IPPROTO_IP; #endif result = TRUE; break; @@ -11927,7 +11936,7 @@ char* encode_cmsghdr_data_ip(ErlNifEnv* env, size_t dataLen, ERL_NIF_TERM* eCMsgHdrData) { - char* xres; + char* xres = NULL; switch (type) { #if defined(IP_TOS) @@ -12022,7 +12031,7 @@ char* encode_cmsghdr_data_ip(ErlNifEnv* env, break; } - return NULL; + return xres; } @@ -12111,20 +12120,30 @@ char* encode_msghdr_flags(ErlNifEnv* env, } else { SocketTArray ta = TARRAY_CREATE(10); // Just to be on the safe side +#if defined(MSG_EOR) if ((msgFlags & MSG_EOR) == MSG_EOR) TARRAY_ADD(ta, esock_atom_eor); - +#endif + +#if defined(MSG_TRUNC) if ((msgFlags & MSG_TRUNC) == MSG_TRUNC) TARRAY_ADD(ta, esock_atom_trunc); +#endif +#if defined(MSG_CTRUNC) if ((msgFlags & MSG_CTRUNC) == MSG_CTRUNC) TARRAY_ADD(ta, esock_atom_ctrunc); +#endif +#if defined(MSG_OOB) if ((msgFlags & MSG_OOB) == MSG_OOB) TARRAY_ADD(ta, esock_atom_oob); +#endif +#if defined(MSG_ERRQUEUE) if ((msgFlags & MSG_ERRQUEUE) == MSG_ERRQUEUE) TARRAY_ADD(ta, esock_atom_errqueue); +#endif SSDBG( descP, ("SOCKET", "esock_encode_cmsghdrs -> flags processed when" @@ -12905,35 +12924,47 @@ BOOLEAN_T esendflags2sendflags(unsigned int eflags, int* flags) for (ef = SOCKET_SEND_FLAG_LOW; ef <= SOCKET_SEND_FLAG_HIGH; ef++) { switch (ef) { +#if defined(MSG_CONFIRM) case SOCKET_SEND_FLAG_CONFIRM: if ((1 << SOCKET_SEND_FLAG_CONFIRM) & eflags) tmp |= MSG_CONFIRM; break; +#endif +#if defined(MSG_DONTROUTE) case SOCKET_SEND_FLAG_DONTROUTE: if ((1 << SOCKET_SEND_FLAG_DONTROUTE) & eflags) tmp |= MSG_DONTROUTE; break; +#endif +#if defined(MSG_EOR) case SOCKET_SEND_FLAG_EOR: if ((1 << SOCKET_SEND_FLAG_EOR) & eflags) tmp |= MSG_EOR; break; +#endif +#if defined(MSG_MORE) case SOCKET_SEND_FLAG_MORE: if ((1 << SOCKET_SEND_FLAG_MORE) & eflags) tmp |= MSG_MORE; break; +#endif +#if defined(MSG_NOSIGNAL) case SOCKET_SEND_FLAG_NOSIGNAL: if ((1 << SOCKET_SEND_FLAG_NOSIGNAL) & eflags) tmp |= MSG_NOSIGNAL; break; +#endif +#if defined(MSG_OOB) case SOCKET_SEND_FLAG_OOB: if ((1 << SOCKET_SEND_FLAG_OOB) & eflags) tmp |= MSG_OOB; break; +#endif default: return FALSE; @@ -12969,20 +13000,26 @@ BOOLEAN_T erecvflags2recvflags(unsigned int eflags, int* flags) "\r\n", ef, tmp) ); switch (ef) { +#if defined(MSG_CMSG_CLOEXEC) case SOCKET_RECV_FLAG_CMSG_CLOEXEC: if ((1 << SOCKET_RECV_FLAG_CMSG_CLOEXEC) & eflags) tmp |= MSG_CMSG_CLOEXEC; break; +#endif +#if defined(MSG_ERRQUEUE) case SOCKET_RECV_FLAG_ERRQUEUE: if ((1 << SOCKET_RECV_FLAG_ERRQUEUE) & eflags) tmp |= MSG_ERRQUEUE; break; +#endif +#if defined(MSG_OOB) case SOCKET_RECV_FLAG_OOB: if ((1 << SOCKET_RECV_FLAG_OOB) & eflags) tmp |= MSG_OOB; break; +#endif /* * @@ -12991,15 +13028,19 @@ BOOLEAN_T erecvflags2recvflags(unsigned int eflags, int* flags) * * */ +#if defined(MSG_PEEK) case SOCKET_RECV_FLAG_PEEK: if ((1 << SOCKET_RECV_FLAG_PEEK) & eflags) tmp |= MSG_PEEK; break; +#endif +#if defined(MSG_TRUNC) case SOCKET_RECV_FLAG_TRUNC: if ((1 << SOCKET_RECV_FLAG_TRUNC) & eflags) tmp |= MSG_TRUNC; break; +#endif default: return FALSE; diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index a73b40cd29..8bb725fb5b 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1238,7 +1238,11 @@ char* esock_decode_protocol(ErlNifEnv* env, *proto = IPPROTO_IP; #endif } else if (COMPARE(esock_atom_ipv6, eProto) == 0) { +#if defined(SOL_IPV6) *proto = SOL_IPV6; +#else + *proto = IPPROTO_IPV6; +#endif } else if (COMPARE(esock_atom_tcp, eProto) == 0) { *proto = IPPROTO_TCP; } else if (COMPARE(esock_atom_udp, eProto) == 0) { -- cgit v1.2.3 From 37704e1eccc60e2077be13ec0a4c3344d9c04b30 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 21 Sep 2018 12:00:05 +0200 Subject: [socket-nif] More constant if-def and header include moving --- erts/emulator/nifs/common/net_nif.c | 40 +++++++++++++++++++++++++++++++++- erts/emulator/nifs/common/socket_nif.c | 17 +-------------- 2 files changed, 40 insertions(+), 17 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index 9905d99a04..f7eeee45ac 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #ifdef HAVE_UNISTD_H @@ -121,6 +120,7 @@ #include #endif +#include #include #include #include @@ -698,37 +698,53 @@ ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, } break; +#if defined(EAI_AGAIN) case EAI_AGAIN: result = esock_make_error(env, esock_atom_eagain); break; +#endif +#if defined(EAI_BADFLAGS) case EAI_BADFLAGS: result = esock_make_error(env, atom_ebadflags); break; +#endif +#if defined(EAI_FAIL) case EAI_FAIL: result = esock_make_error(env, atom_efail); break; +#endif +#if defined(EAI_FAMILY) case EAI_FAMILY: result = esock_make_error(env, atom_efamily); break; +#endif +#if defined(EAI_MEMORY) case EAI_MEMORY: result = esock_make_error(env, atom_emem); break; +#endif +#if defined(EAI_NONAME) case EAI_NONAME: result = esock_make_error(env, atom_enoname); break; +#endif +#if defined(EAI_OVERFLOW) case EAI_OVERFLOW: result = esock_make_error(env, atom_eoverflow); break; +#endif +#if defined(EAI_SYSTEM) case EAI_SYSTEM: result = esock_make_error_errno(env, get_errno()); break; +#endif default: result = esock_make_error(env, esock_atom_einval); @@ -842,49 +858,71 @@ ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, } break; +#if defined(EAI_ADDRFAMILY) case EAI_ADDRFAMILY: result = esock_make_error(env, atom_eaddrfamily); break; +#endif +#if defined(EAI_AGAIN) case EAI_AGAIN: result = esock_make_error(env, esock_atom_eagain); break; +#endif +#if defined(EAI_BADFLAGS) case EAI_BADFLAGS: result = esock_make_error(env, atom_ebadflags); break; +#endif +#if defined(EAI_FAIL) case EAI_FAIL: result = esock_make_error(env, atom_efail); break; +#endif +#if defined(EAI_FAMILY) case EAI_FAMILY: result = esock_make_error(env, atom_efamily); break; +#endif +#if defined(EAI_MEMORY) case EAI_MEMORY: result = esock_make_error(env, atom_emem); break; +#endif +#if defined(EAI_NODATA) case EAI_NODATA: result = esock_make_error(env, atom_enodata); break; +#endif +#if defined(EAI_NONAME) case EAI_NONAME: result = esock_make_error(env, atom_enoname); break; +#endif +#if defined(EAI_SERVICE) case EAI_SERVICE: result = esock_make_error(env, atom_eservice); break; +#endif +#if defined(EAI_SOCKTYPE) case EAI_SOCKTYPE: result = esock_make_error(env, atom_esocktype); break; +#endif +#if defined(EAI_SYSTEM) case EAI_SYSTEM: result = esock_make_error(env, atom_esystem); break; +#endif default: result = esock_make_error(env, esock_atom_einval); diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index c98ab1e514..6564c3c82f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -25,21 +25,6 @@ #define STATIC_ERLANG_NIF 1 -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ - #ifdef HAVE_CONFIG_H #include "config.h" @@ -61,7 +46,6 @@ #include #include #include -#include #include #ifdef HAVE_UNISTD_H @@ -135,6 +119,7 @@ #include #endif +#include #include #include #include -- cgit v1.2.3 From ceb5ac40d519cc475c6c3252a2e4f87cdcd5b6f0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 21 Sep 2018 12:11:21 +0200 Subject: [socket-nif] Moved include file (FreeBSD) --- erts/emulator/nifs/common/socket_nif.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 04c3609b32..a3281f040d 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -25,22 +25,6 @@ #define STATIC_ERLANG_NIF 1 -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ -/* #include */ - - #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -61,7 +45,6 @@ #include #include #include -#include #include #ifdef HAVE_UNISTD_H @@ -120,6 +103,14 @@ #undef WANT_NONBLOCKING #include "sys.h" + + + +/* AND HERE WE MAY HAVE A BUNCH OF DEFINES....SEE INET DRIVER.... */ + + + + #else /* !__WIN32__ */ #include @@ -135,6 +126,7 @@ #include #endif +#include #include #include #include @@ -312,7 +304,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #endif #include "sys.h" -#endif +#endif /* !__WIN32__ */ #include -- cgit v1.2.3 From 742a210ace9625646386eb71b8d33e9938eeaec1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 25 Sep 2018 16:34:55 +0200 Subject: [socket-nif] Add proper recv timeout handling Added proper recv timeout handling. Made use of the enif_select(mode = cancel) feature. Each time a timeout expires, the "active" recv (the surrent reader select) has to be cancelled. Not yet tested...something for the new test suite... Also, added support for getopt(controlling_pprocess) that, for some reason, was not yet implemented. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 1 + erts/emulator/nifs/common/socket_nif.c | 803 +++++++++++++++++++++++++++++---- 2 files changed, 725 insertions(+), 79 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 3595c483d7..c3595e495d 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -184,6 +184,7 @@ extern ERL_NIF_TERM esock_atom_einval; #define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) #define MKEL(E) enif_make_list((E), 0) #define MKMA(E,KA,VA,L,M) enif_make_map_from_arrays((E), (KA), (VA), (L), (M)) +#define MKPID(E, P) enif_make_pid((E), (P)) #define MKREF(E) enif_make_ref((E)) #define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) #define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index c48d6eab00..a6940f788c 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -1490,6 +1490,8 @@ static ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, + SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -1849,6 +1851,11 @@ static ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env, static ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef); static ERL_NIF_TERM ncancel_read_select(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM opRef); @@ -1911,6 +1918,18 @@ static ERL_NIF_TERM send_check_result(ErlNifEnv* env, ssize_t dataSize, int saveErrno, ERL_NIF_TERM sendRef); +static BOOLEAN_T recv_check_reader(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ERL_NIF_TERM* checkResult); +static char* recv_init_current_reader(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref); +static ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, + SocketDescriptor* descP); +static void recv_error_current_reader(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM reason); static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SocketDescriptor* descP, int read, @@ -2150,6 +2169,22 @@ static BOOLEAN_T writer_unqueue(ErlNifEnv* env, SocketDescriptor* descP, const ErlNifPid* pid); +static BOOLEAN_T reader_search4pid(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid); +static ERL_NIF_TERM reader_push(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid pid, + ERL_NIF_TERM ref); +static BOOLEAN_T reader_pop(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid, + ErlNifMonitor* mon, + ERL_NIF_TERM* ref); +static BOOLEAN_T reader_unqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid); + static BOOLEAN_T qsearch4pid(ErlNifEnv* env, SocketRequestQueue* q, ErlNifPid* pid); @@ -2159,7 +2194,6 @@ static SocketRequestQueueElement* qpop(SocketRequestQueue* q); static BOOLEAN_T qunqueue(ErlNifEnv* env, SocketRequestQueue* q, const ErlNifPid* pid); - /* #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) static size_t my_strnlen(const char *s, size_t maxlen); @@ -2175,6 +2209,15 @@ static void socket_down(ErlNifEnv* env, void* obj, const ErlNifPid* pid, const ErlNifMonitor* mon); +static void socket_down_acceptor(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid); +static void socket_down_writer(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid); +static void socket_down_reader(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid); /* static char* send_msg_error_closed(ErlNifEnv* env, @@ -3986,6 +4029,8 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, if (ctrlBuf != NULL) FREE(ctrlBuf); return esock_make_error_str(env, xres); } + } else { + ctrlBufUsed = 0; } msgHdr.msg_control = ctrlBuf; msgHdr.msg_controllen = ctrlBufUsed; @@ -4168,6 +4213,7 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, { ssize_t read; ErlNifBinary buf; + ERL_NIF_TERM readerCheck; int save_errno; int bufSz = (len ? len : descP->rBufSz); @@ -4179,6 +4225,10 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, if (!descP->isReadable) return enif_make_badarg(env); + /* Check if there is already a current reader and if its us */ + if (!recv_check_reader(env, descP, recvRef, &readerCheck)) + return readerCheck; + /* Allocate a buffer: * Either as much as we want to read or (if zero (0)) use the "default" * size (what has been configured). @@ -4315,6 +4365,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, ssize_t read; int save_errno; ErlNifBinary buf; + ERL_NIF_TERM readerCheck; int bufSz = (len ? len : descP->rBufSz); SSDBG( descP, ("SOCKET", "nrecvfrom -> entry with" @@ -4325,6 +4376,10 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, if (!descP->isReadable) return enif_make_badarg(env); + /* Check if there is already a current reader and if its us */ + if (!recv_check_reader(env, descP, recvRef, &readerCheck)) + return readerCheck; + /* Allocate a buffer: * Either as much as we want to read or (if zero (0)) use the "default" * size (what has been configured). @@ -4476,6 +4531,7 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, struct iovec iov[1]; // Shall we always use 1? ErlNifBinary data[1]; // Shall we always use 1? ErlNifBinary ctrl; + ERL_NIF_TERM readerCheck; SocketAddress addr; SSDBG( descP, ("SOCKET", "nrecvmsg -> entry with" @@ -4487,6 +4543,10 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, if (!descP->isReadable) return enif_make_badarg(env); + /* Check if there is already a current reader and if its us */ + if (!recv_check_reader(env, descP, recvRef, &readerCheck)) + return readerCheck; + /* for (i = 0; i < sizeof(buf); i++) { if (!ALLOC_BIN(bifSz, &buf[i])) @@ -8093,7 +8153,7 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, ERL_NIF_TERM result; SSDBG( descP, - ("SOCKET", "ngetopt_opt -> entry with" + ("SOCKET", "ngetopt_otp -> entry with" "\r\n eOpt: %d" "\r\n", eOpt) ); @@ -8106,13 +8166,17 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, result = ngetopt_otp_iow(env, descP); break; + case SOCKET_OPT_OTP_CTRL_PROC: + result = ngetopt_otp_ctrl_proc(env, descP); + break; + default: result = esock_make_error(env, esock_atom_einval); break; } SSDBG( descP, - ("SOCKET", "ngetopt_opt -> done when" + ("SOCKET", "ngetopt_otp -> done when" "\r\n result: %T" "\r\n", result) ); @@ -8144,6 +8208,18 @@ ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, } +/* ngetopt_otp_ctrl_proc - Handle the OTP (level) controlling_process options + */ +static +ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = MKPID(env, &descP->ctrlPid); + + return esock_make_ok2(env, eVal); +} + + /* The option has *not* been encoded. Instead it has been provided * in "native mode" (option is provided as is). In this case it will have the @@ -10752,6 +10828,8 @@ ERL_NIF_TERM ncancel_send(ErlNifEnv* env, { ERL_NIF_TERM res; + MLOCK(descP->writeMtx); + SSDBG( descP, ("SOCKET", "ncancel_send -> entry with" "\r\n opRef: %T" @@ -10759,8 +10837,6 @@ ERL_NIF_TERM ncancel_send(ErlNifEnv* env, "\r\n", opRef, ((descP->currentWriterP == NULL) ? "without writer" : "with writer")) ); - MLOCK(descP->writeMtx); - if (descP->currentWriterP != NULL) { if (COMPARE(opRef, descP->currentWriter.ref) == 0) { res = ncancel_send_current(env, descP); @@ -10859,14 +10935,116 @@ ERL_NIF_TERM ncancel_send_waiting(ErlNifEnv* env, /* *** ncancel_recv *** * - * + * Cancel a read operation. + * Its either the current reader or one of the waiting readers. */ static ERL_NIF_TERM ncancel_recv(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM opRef) { - return esock_make_error(env, esock_atom_einval); + ERL_NIF_TERM res; + + MLOCK(descP->readMtx); + + SSDBG( descP, + ("SOCKET", "ncancel_recv -> entry with" + "\r\n opRef: %T" + "\r\n %s" + "\r\n", opRef, + ((descP->currentReaderP == NULL) ? "without reader" : "with reader")) ); + + if (descP->currentReaderP != NULL) { + if (COMPARE(opRef, descP->currentReader.ref) == 0) { + res = ncancel_recv_current(env, descP); + } else { + res = ncancel_recv_waiting(env, descP, opRef); + } + } else { + /* Or badarg? */ + res = esock_make_error(env, esock_atom_einval); + } + + MUNLOCK(descP->readMtx); + + SSDBG( descP, + ("SOCKET", "ncancel_recv -> done with result:" + "\r\n %T" + "\r\n", res) ); + + return res; +} + + +/* The current reader process has an ongoing select we first must + * cancel. Then we must re-activate the "first" (the first + * in the reader queue). + */ +static +ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM res; + + SSDBG( descP, ("SOCKET", "ncancel_recv_current -> entry\r\n") ); + + res = ncancel_read_select(env, descP, descP->currentReader.ref); + + SSDBG( descP, ("SOCKET", "ncancel_recv_current -> cancel res: %T\r\n", res) ); + + if (reader_pop(env, descP, + &descP->currentReader.pid, + &descP->currentReader.mon, + &descP->currentReader.ref)) { + + /* There was another one */ + + SSDBG( descP, ("SOCKET", "ncancel_recv_current -> new (active) reader: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentReader.pid, + descP->currentReader.ref) ); + + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, &descP->currentReader.pid, descP->currentReader.ref); + + } else { + SSDBG( descP, ("SOCKET", "ncancel_recv_current -> no more readers\r\n") ); + descP->currentReaderP = NULL; + } + + SSDBG( descP, ("SOCKET", "ncancel_recv_current -> done with result:" + "\r\n %T" + "\r\n", res) ); + + return res; +} + + +/* These processes have not performed a select, so we can simply + * remove them from the reader queue. + */ +static +ERL_NIF_TERM ncancel_recv_waiting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM opRef) +{ + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) + return esock_make_error(env, atom_exself); + + /* unqueue request from (reader) queue */ + + if (reader_unqueue(env, descP, &caller)) { + return esock_atom_ok; + } else { + /* Race? */ + return esock_make_error(env, esock_atom_not_found); + } } @@ -10958,7 +11136,8 @@ BOOLEAN_T send_check_writer(ErlNifEnv* env, SSDBG( descP, ("SOCKET", - "nsend -> queue (push) result: %T\r\n", checkResult) ); + "send_check_writer -> queue (push) result: %T\r\n", + checkResult) ); return FALSE; @@ -11005,7 +11184,8 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, cnt_inc(&descP->writePkgCnt, 1); cnt_inc(&descP->writeByteCnt, written); - DEMONP(env, descP, &descP->currentWriter.mon); + if (descP->currentWriterP != NULL) + DEMONP(env, descP, &descP->currentWriter.mon); SSDBG( descP, ("SOCKET", "send_check_result -> " @@ -11058,11 +11238,15 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, res = esock_make_error_errno(env, saveErrno); - while (writer_pop(env, descP, &pid, &mon, &ref)) { - SSDBG( descP, - ("SOCKET", "send_check_result -> abort %T\r\n", pid) ); - send_msg_nif_abort(env, ref, res, &pid); - DEMONP(env, descP, &mon); + if (descP->currentWriterP != NULL) { + DEMONP(env, descP, &descP->currentWriter.mon); + + while (writer_pop(env, descP, &pid, &mon, &ref)) { + SSDBG( descP, + ("SOCKET", "send_check_result -> abort %T\r\n", pid) ); + send_msg_nif_abort(env, ref, res, &pid); + DEMONP(env, descP, &mon); + } } return res; @@ -11087,6 +11271,20 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, * so schedule the rest for later. */ + if (descP->currentWriterP == NULL) { + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) + return esock_make_error(env, atom_exself); + descP->currentWriter.pid = caller; + if (MONP(env, descP, + &descP->currentWriter.pid, + &descP->currentWriter.mon) > 0) + return esock_make_error(env, atom_exmon); + descP->currentWriter.ref = enif_make_copy(descP->env, sendRef); + descP->currentWriterP = &descP->currentWriter; + } + cnt_inc(&descP->writeWaits, 1); SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), @@ -11100,6 +11298,167 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, } + +/* *** recv_check_reader *** + * + * Checks if we have a current reader and if that is us. If not, then we must + * be made to wait for our turn. This is done by pushing us unto the reader queue. + */ +static +BOOLEAN_T recv_check_reader(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ERL_NIF_TERM* checkResult) +{ + if (descP->currentReaderP != NULL) { + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) { + *checkResult = esock_make_error(env, atom_exself); + return FALSE; + } + + if (!compare_pids(env, &descP->currentReader.pid, &caller)) { + /* Not the "current reader", so (maybe) push onto queue */ + + SSDBG( descP, + ("SOCKET", "recv_check_reader -> not (current) reader\r\n") ); + + if (!reader_search4pid(env, descP, &caller)) + *checkResult = reader_push(env, descP, caller, ref); + else + *checkResult = esock_make_error(env, esock_atom_eagain); + + SSDBG( descP, + ("SOCKET", + "recv_check_reader -> queue (push) result: %T\r\n", + checkResult) ); + + return FALSE; + + } + + } + + *checkResult = esock_atom_ok; // Does not actually matter in this case, but ... + + return TRUE; +} + + + +/* *** recv_init_current_reader *** + * + * Initiate (maybe) the currentReader structure of the descriptor. + * Including monitoring the calling process. + */ +static +char* recv_init_current_reader(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM recvRef) +{ + if (descP->currentReaderP == NULL) { + ErlNifPid caller; + + if (enif_self(env, &caller) == NULL) + return str_exself; + + descP->currentReader.pid = caller; + if (MONP(env, descP, + &descP->currentReader.pid, + &descP->currentReader.mon) > 0) { + return str_exmon; + } + descP->currentReader.ref = enif_make_copy(descP->env, recvRef); + descP->currentReaderP = &descP->currentReader; + } + + return NULL; +} + + + +/* *** recv_update_current_reader *** + * + * Demonitors the current reader process and pop's the reader queue. + * If there is a waiting (reader) process, then it will be assigned + * as the new current reader and a new (read) select will be done. + */ + +static +ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, + SocketDescriptor* descP) +{ + if (descP->currentReaderP != NULL) { + + DEMONP(env, descP, &descP->currentReader.mon); + + if (reader_pop(env, descP, + &descP->currentReader.pid, + &descP->currentReader.mon, + &descP->currentReader.ref)) { + + /* There was another one */ + + SSDBG( descP, + ("SOCKET", "recv_update_current_reader -> new (active) reader: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentReader.pid, + descP->currentReader.ref) ); + + SELECT(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, + &descP->currentReader.pid, + descP->currentReader.ref); + + } else { + descP->currentWriterP = NULL; + } + } + + return esock_atom_ok; +} + + + +/* *** recv_error_current_reader *** + * + * Process the current reader and any waiting readers + * when a read (fatal) error has occured. + * All waiting readers will be "aborted", that is a + * nif_abort message will be sent (with reaf and reason). + */ +static +void recv_error_current_reader(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM reason) +{ + if (descP->currentReaderP != NULL) { + ErlNifPid pid; + ErlNifMonitor mon; + ERL_NIF_TERM ref; + + DEMONP(env, descP, &descP->currentReader.mon); + + while (reader_pop(env, descP, &pid, &mon, &ref)) { + SSDBG( descP, + ("SOCKET", "recv_error_current_reader -> abort %T\r\n", pid) ); + send_msg_nif_abort(env, ref, reason, &pid); + DEMONP(env, descP, &mon); + } + } +} + + + +/* *** recv_check_result *** + * + * Process the result of a call to recv. + */ static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -11109,6 +11468,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, ErlNifBinary* bufP, ERL_NIF_TERM recvRef) { + char* xres; ERL_NIF_TERM data; SSDBG( descP, @@ -11129,15 +11489,20 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, */ if ((read == 0) && (descP->type == SOCK_STREAM)) { + ERL_NIF_TERM res = esock_make_error(env, atom_closed); /* * When a stream socket peer has performed an orderly shutdown, the return * value will be 0 (the traditional "end-of-file" return). * * *We* do never actually try to read 0 bytes from a stream socket! + * + * We must also notify any waiting readers! */ - return esock_make_error(env, atom_closed); + recv_error_current_reader(env, descP, res); + + return res; } @@ -11173,6 +11538,11 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * => We choose alt 1 for now. */ + cnt_inc(&descP->readByteCnt, read); + + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) + return esock_make_error_str(env, xres); + data = MKBIN(env, bufP); SSDBG( descP, @@ -11188,16 +11558,24 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, /* * WE NEED TO INFORM ANY WAITING READERS + * + * DEMONP of the current reader! + * * */ - data = MKBIN(env, bufP); + cnt_inc(&descP->readPkgCnt, 1); + cnt_inc(&descP->readByteCnt, read); SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] " "we got exactly what we could fit\r\n", toRead) ); + recv_update_current_reader(env, descP); + + data = MKBIN(env, bufP); + return esock_make_ok3(env, atom_true, data); } @@ -11207,6 +11585,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, /* +++ Error handling +++ */ if (saveErrno == ECONNRESET) { + ERL_NIF_TERM res = esock_make_error(env, atom_closed); /* +++ Oups - closed +++ */ @@ -11231,12 +11610,14 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, descP->closeLocal = FALSE; descP->state = SOCKET_STATE_CLOSING; + recv_error_current_reader(env, descP, res); + SELECT(env, descP->sock, (ERL_NIF_SELECT_STOP), descP, NULL, recvRef); - return esock_make_error(env, atom_closed); + return res; } else if ((saveErrno == ERRNO_BLOCK) || (saveErrno == EAGAIN)) { @@ -11248,9 +11629,14 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, return esock_make_error(env, esock_atom_eagain); } else { + ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); + SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] errno: %d\r\n", toRead, saveErrno) ); - return esock_make_error_errno(env, saveErrno); + + recv_error_current_reader(env, descP, res); + + return res; } } else { @@ -11265,19 +11651,24 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, if (toRead == 0) { - /* +++ We got a chunk of data but +++ - * +++ since we did not fill the +++ - * +++ buffer, we must split it +++ - * +++ into a sub-binary. +++ + /* +++ We got it all, but since we +++ + * +++ did not fill the buffer, we +++ + * +++ must split it into a sub-binary. +++ */ SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] split buffer\r\n", toRead) ); + cnt_inc(&descP->readPkgCnt, 1); + cnt_inc(&descP->readByteCnt, read); + + recv_update_current_reader(env, descP); + data = MKBIN(env, bufP); data = MKSBIN(env, data, 0, read); - SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] done\r\n", toRead) ); + SSDBG( descP, + ("SOCKET", "recv_check_result -> [%d] done\r\n", toRead) ); return esock_make_ok3(env, atom_true, data); @@ -11289,6 +11680,8 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] " "only part of message - expect more\r\n", toRead) ); + cnt_inc(&descP->readByteCnt, read); + return esock_make_ok3(env, atom_false, MKBIN(env, bufP)); } } @@ -13907,10 +14300,10 @@ ERL_NIF_TERM writer_push(ErlNifEnv* env, */ static BOOLEAN_T writer_pop(ErlNifEnv* env, - SocketDescriptor* descP, - ErlNifPid* pid, - ErlNifMonitor* mon, - ERL_NIF_TERM* ref) + SocketDescriptor* descP, + ErlNifPid* pid, + ErlNifMonitor* mon, + ERL_NIF_TERM* ref) { SocketRequestQueueElement* e = qpop(&descP->writersQ); @@ -13921,7 +14314,7 @@ BOOLEAN_T writer_pop(ErlNifEnv* env, FREE(e); return TRUE; } else { - /* (acceptors) Queue was empty */ + /* (writers) Queue was empty */ // *pid = NULL; we have no null value for pids // *mon = NULL; we have no null value for monitors *ref = esock_atom_undefined; // Just in case @@ -13945,6 +14338,92 @@ BOOLEAN_T writer_unqueue(ErlNifEnv* env, +/* *** reader search for pid *** + * + * Search for a pid in the reader queue. + */ +static +BOOLEAN_T reader_search4pid(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid) +{ + return qsearch4pid(env, &descP->readersQ, pid); +} + + +/* *** reader push *** + * + * Push an reader onto the raeder queue. + * This happens when we already have atleast one current reader. + */ +static +ERL_NIF_TERM reader_push(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid pid, + ERL_NIF_TERM ref) +{ + SocketRequestQueueElement* e = MALLOC(sizeof(SocketRequestQueueElement)); + SocketRequestor* reqP = &e->data; + + reqP->pid = pid; + reqP->ref = enif_make_copy(descP->env, ref); + + if (MONP(env, descP, &pid, &reqP->mon) > 0) { + FREE(reqP); + return esock_make_error(env, atom_exmon); + } + + qpush(&descP->readersQ, e); + + // THIS IS OK => MAKES THE CALLER WAIT FOR ITS TURN + return esock_make_error(env, esock_atom_eagain); +} + + +/* *** reader pop *** + * + * Pop an writer from the reader queue. + */ +static +BOOLEAN_T reader_pop(ErlNifEnv* env, + SocketDescriptor* descP, + ErlNifPid* pid, + ErlNifMonitor* mon, + ERL_NIF_TERM* ref) +{ + SocketRequestQueueElement* e = qpop(&descP->readersQ); + + if (e != NULL) { + *pid = e->data.pid; + *mon = e->data.mon; + *ref = e->data.ref; // At this point the ref has already been copied (env) + FREE(e); + return TRUE; + } else { + /* (readers) Queue was empty */ + // *pid = NULL; we have no null value for pids + // *mon = NULL; we have no null value for monitors + *ref = esock_atom_undefined; // Just in case + return FALSE; + } + +} + + +/* *** reader unqueue *** + * + * Remove an reader from the reader queue. + */ +static +BOOLEAN_T reader_unqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid) +{ + return qunqueue(env, &descP->readersQ, pid); +} + + + static @@ -14324,77 +14803,243 @@ void socket_down(ErlNifEnv* env, "\r\n pid: %T" "\r\n", descP->sock, *pid) ); - /* Eventually we should go through the other queues also, - * the process can be one of them... - * - * Currently only the accteptors actuallu use the queues. - */ + + if (compare_pids(env, &descP->ctrlPid, pid)) { + /* We don't bother with the queue cleanup here - + * we leave it to the stop callback function. + */ - if (descP->currentAcceptorP != NULL) { + descP->state = SOCKET_STATE_CLOSING; + descP->closeLocal = TRUE; + descP->closeRef = MKREF(env); + enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), + descP, NULL, descP->closeRef); - /* - * We have acceptor(s) (atleast one) - * - * Check first if its the current acceptor, - * and if not check the queue. - */ + } else { + + /* check all operation queue(s): acceptor, writer and reader. */ + + MLOCK(descP->accMtx); + if (descP->currentAcceptorP != NULL) + socket_down_acceptor(env, descP, pid); + MUNLOCK(descP->accMtx); + + MLOCK(descP->writeMtx); + if (descP->currentWriterP != NULL) + socket_down_writer(env, descP, pid); + MUNLOCK(descP->writeMtx); + + MLOCK(descP->readMtx); + if (descP->currentReaderP != NULL) + socket_down_reader(env, descP, pid); + MUNLOCK(descP->readMtx); + + } + + SSDBG( descP, ("SOCKET", "socket_down -> done\r\n") ); + +} - if (compare_pids(env, &descP->currentAcceptor.pid, pid)) { + +/* *** socket_down_acceptor *** + * + * Check and then handle a downed acceptor process. + * + */ +static +void socket_down_acceptor(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid) +{ + if (compare_pids(env, &descP->currentAcceptor.pid, pid)) { + + SSDBG( descP, ("SOCKET", + "socket_down_acceptor -> " + "current acceptor - try pop the queue\r\n") ); + + if (acceptor_pop(env, descP, + &descP->currentAcceptor.pid, + &descP->currentAcceptor.mon, + &descP->currentAcceptor.ref)) { + int res; + + /* There was another one, so we will still be in accepting state */ + SSDBG( descP, ("SOCKET", - "socket_down -> " - "current acceptor - try pop the queue\r\n") ); + "socket_down_acceptor -> new (active) acceptor: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentAcceptor.pid, + descP->currentAcceptor.ref) ); - if (acceptor_pop(env, descP, - &descP->currentAcceptor.pid, - &descP->currentAcceptor.mon, - &descP->currentAcceptor.ref)) { - int res; + if ((res = enif_select(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, + &descP->currentAcceptor.pid, + descP->currentAcceptor.ref) < 0)) { - /* There was another one, so we will still be in accepting state */ + esock_warning_msg("Failed select (%d) for new acceptor " + "after current (%T) died\r\n", + res, *pid); - SSDBG( descP, ("SOCKET", "socket_down -> new (active) acceptor: " - "\r\n pid: %T" - "\r\n ref: %T" - "\r\n", - descP->currentAcceptor.pid, - descP->currentAcceptor.ref) ); + } + + } else { + + SSDBG( descP, ("SOCKET", + "socket_down_acceptor -> no active acceptor\r\n") ); + + descP->currentAcceptorP = NULL; + descP->state = SOCKET_STATE_LISTENING; + } + + } else { + + /* Maybe unqueue one of the waiting acceptors */ + + SSDBG( descP, ("SOCKET", + "socket_down_acceptor -> " + "not current acceptor - maybe a waiting acceptor\r\n") ); + + acceptor_unqueue(env, descP, pid); + } +} + + + + +/* *** socket_down_writer *** + * + * Check and then handle a downed writer process. + * + */ +static +void socket_down_writer(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid) +{ + if (compare_pids(env, &descP->currentWriter.pid, pid)) { + + SSDBG( descP, ("SOCKET", + "socket_down_writer -> " + "current writer - try pop the queue\r\n") ); + + if (writer_pop(env, descP, + &descP->currentWriter.pid, + &descP->currentWriter.mon, + &descP->currentWriter.ref)) { + int res; + + /* There was another one */ + + SSDBG( descP, ("SOCKET", "socket_down_writer -> new (current) writer: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentWriter.pid, + descP->currentWriter.ref) ); + + if ((res = enif_select(env, + descP->sock, + (ERL_NIF_SELECT_WRITE), + descP, + &descP->currentWriter.pid, + descP->currentWriter.ref) < 0)) { - if ((res = enif_select(env, - descP->sock, - (ERL_NIF_SELECT_READ), - descP, - &descP->currentAcceptor.pid, - descP->currentAcceptor.ref) < 0)) { - - esock_warning_msg("Failed select (%d) for new acceptor " - "after current (%T) died\r\n", - res, *pid); - - } + esock_warning_msg("Failed select (%d) for new writer " + "after current (%T) died\r\n", + res, *pid); - } else { + } + + } else { + + SSDBG( descP, ("SOCKET", + "socket_down_writer -> no active writer\r\n") ); + + descP->currentWriterP = NULL; + } + + } else { + + /* Maybe unqueue one of the waiting writer(s) */ + + SSDBG( descP, ("SOCKET", + "socket_down_writer -> " + "not current writer - maybe a waiting writer\r\n") ); + + writer_unqueue(env, descP, pid); + } +} + + + + +/* *** socket_down_reader *** + * + * Check and then handle a downed reader process. + * + */ +static +void socket_down_reader(ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid) +{ + if (compare_pids(env, &descP->currentReader.pid, pid)) { + + SSDBG( descP, ("SOCKET", + "socket_down_reader -> " + "current reader - try pop the queue\r\n") ); + + if (reader_pop(env, descP, + &descP->currentReader.pid, + &descP->currentReader.mon, + &descP->currentReader.ref)) { + int res; + + /* There was another one */ + + SSDBG( descP, ("SOCKET", "socket_down_reader -> new (current) reader: " + "\r\n pid: %T" + "\r\n ref: %T" + "\r\n", + descP->currentReader.pid, + descP->currentReader.ref) ); + + if ((res = enif_select(env, + descP->sock, + (ERL_NIF_SELECT_READ), + descP, + &descP->currentReader.pid, + descP->currentReader.ref) < 0)) { - SSDBG( descP, ("SOCKET", "socket_down -> no active acceptor\r\n") ); + esock_warning_msg("Failed select (%d) for new reader " + "after current (%T) died\r\n", + res, *pid); - descP->currentAcceptorP = NULL; - descP->state = SOCKET_STATE_LISTENING; } } else { - /* Maybe unqueue one of the waiting acceptors */ - SSDBG( descP, ("SOCKET", - "socket_down -> " - "not current acceptor - maybe a waiting acceptor\r\n") ); + "socket_down_reader -> no active reader\r\n") ); - acceptor_unqueue(env, descP, pid); + descP->currentReaderP = NULL; } + + } else { + + /* Maybe unqueue one of the waiting reader(s) */ + + SSDBG( descP, ("SOCKET", + "socket_down_reader -> " + "not current reader - maybe a waiting reader\r\n") ); + + reader_unqueue(env, descP, pid); } - - SSDBG( descP, ("SOCKET", "socket_down -> done\r\n") ); - } -- cgit v1.2.3 From cd6afd8df61eb33aa2b51d4a0315fa8903135465 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 26 Sep 2018 12:11:50 +0200 Subject: [socket-nif] Improve socket stop Make sure the current whatever (writer, reader or writer) is not the closer pid before *attempting* to sending the abort message. And if that fails, issue a warning message (not aborting). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 66 +++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 16 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index a6940f788c..ccbcf63ece 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -2918,15 +2918,14 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, eSockAddr = argv[1]; SSDBG( descP, - ("SOCKET", "nif_bind -> args when sock = %d:" + ("SOCKET", "nif_bind -> args when sock = %d (0x%lX)" "\r\n Socket: %T" "\r\n SockAddr: %T" - "\r\n", descP->sock, argv[0], eSockAddr) ); + "\r\n", descP->sock, descP->state, argv[0], eSockAddr) ); /* Make sure we are ready * Not sure how this would even happen, but... */ - /* WHY NOT !IS_OPEN(...) */ if (descP->state != SOCKET_STATE_OPEN) return esock_make_error(env, atom_exbadstate); @@ -14646,11 +14645,22 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) /* We have a (current) writer and *may* therefor also have * writers waiting. */ - - ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, - descP->currentWriter.ref, - atom_closed, - &descP->currentWriter.pid)) ); + + if (!compare_pids(env, + &descP->closerPid, + &descP->currentWriter.pid) && + send_msg_nif_abort(env, + descP->currentWriter.ref, + atom_closed, + &descP->currentWriter.pid) != NULL) { + /* Shall we really do this? + * This happens if the controlling process has been killed! + */ + esock_warning_msg("Failed sending abort (%T) message to " + "current writer %T\r\n", + descP->currentWriter.ref, + descP->currentWriter.pid); + } /* And also deal with the waiting writers (in the same way) */ inform_waiting_procs(env, descP, &descP->writersQ, TRUE, atom_closed); @@ -14662,10 +14672,21 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * readers waiting. */ - ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, - descP->currentReader.ref, - atom_closed, - &descP->currentReader.pid)) ); + if (!compare_pids(env, + &descP->closerPid, + &descP->currentReader.pid) && + send_msg_nif_abort(env, + descP->currentReader.ref, + atom_closed, + &descP->currentReader.pid) != NULL) { + /* Shall we really do this? + * This happens if the controlling process has been killed! + */ + esock_warning_msg("Failed sending abort (%T) message to " + "current reader %T\r\n", + descP->currentReader.ref, + descP->currentReader.pid); + } /* And also deal with the waiting readers (in the same way) */ inform_waiting_procs(env, descP, &descP->readersQ, TRUE, atom_closed); @@ -14676,10 +14697,21 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * acceptors waiting. */ - ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, - descP->currentAcceptor.ref, - atom_closed, - &descP->currentAcceptor.pid)) ); + if (!compare_pids(env, + &descP->closerPid, + &descP->currentAcceptor.pid) && + send_msg_nif_abort(env, + descP->currentAcceptor.ref, + atom_closed, + &descP->currentAcceptor.pid) != NULL) { + /* Shall we really do this? + * This happens if the controlling process has been killed! + */ + esock_warning_msg("Failed sending abort (%T) message to " + "current acceptor %T\r\n", + descP->currentAcceptor.ref, + descP->currentAcceptor.pid); + } /* And also deal with the waiting acceptors (in the same way) */ inform_waiting_procs(env, descP, &descP->acceptorsQ, TRUE, atom_closed); @@ -14811,6 +14843,8 @@ void socket_down(ErlNifEnv* env, descP->state = SOCKET_STATE_CLOSING; descP->closeLocal = TRUE; + descP->closerPid = *pid; + descP->closerMon = *mon; descP->closeRef = MKREF(env); enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), descP, NULL, descP->closeRef); -- cgit v1.2.3 From 1c412c62ba3be17b7a818f264049a7ee7942351e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 28 Sep 2018 18:40:08 +0200 Subject: [socket-nif] Add support for socket (level otp) buffer options Add support for otp level socket options rcvbuf, rcvctrlbuf and sndctrlbuf. These options define default sizes for these buffers. The 'rcvbuf' is used when receiving messages when calling the recv, recvfrom and recvmsg functions. The 'rcvctrlbuf' is used for the control message header info when calling the recvmsg function. The 'sndctrlbuf' is used for the control message header info when calling the sendmsg function. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 146 ++++++++++++++++++++++++++++++++ erts/emulator/nifs/common/socket_util.c | 29 +++++++ erts/emulator/nifs/common/socket_util.h | 6 ++ 3 files changed, 181 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index ccbcf63ece..876bed3135 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -501,6 +501,9 @@ typedef union { #define SOCKET_OPT_OTP_DEBUG 1 #define SOCKET_OPT_OTP_IOW 2 #define SOCKET_OPT_OTP_CTRL_PROC 3 +#define SOCKET_OPT_OTP_RCVBUF 4 +#define SOCKET_OPT_OTP_RCVCTRLBUF 6 +#define SOCKET_OPT_OTP_SNDCTRLBUF 7 #define SOCKET_OPT_SOCK_ACCEPTCONN 1 #define SOCKET_OPT_SOCK_BINDTODEVICE 3 @@ -1004,6 +1007,15 @@ static ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, static ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal); static ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -1492,6 +1504,12 @@ static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -5001,6 +5019,18 @@ ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, result = nsetopt_otp_ctrl_proc(env, descP, eVal); break; + case SOCKET_OPT_OTP_RCVBUF: + result = nsetopt_otp_rcvbuf(env, descP, eVal); + break; + + case SOCKET_OPT_OTP_RCVCTRLBUF: + result = nsetopt_otp_rcvctrlbuf(env, descP, eVal); + break; + + case SOCKET_OPT_OTP_SNDCTRLBUF: + result = nsetopt_otp_sndctrlbuf(env, descP, eVal); + break; + default: result = esock_make_error(env, esock_atom_einval); break; @@ -5079,6 +5109,74 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, +/* nsetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option + */ +static +ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + size_t val; + char* xres; + + if ((xres = esock_decode_bufsz(env, + eVal, + SOCKET_RECV_BUFFER_SIZE_DEFAULT, &val)) != NULL) + return esock_make_error_str(env, xres); + + descP->rBufSz = val; + + return esock_atom_ok; +} + + + +/* nsetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option + */ +static +ERL_NIF_TERM nsetopt_otp_rcvctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + size_t val; + char* xres; + + if ((xres = esock_decode_bufsz(env, + eVal, + SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT, + &val)) != NULL) + return esock_make_error_str(env, xres); + + descP->rCtrlSz = val; + + return esock_atom_ok; +} + + + +/* nsetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option + */ +static +ERL_NIF_TERM nsetopt_otp_sndctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + size_t val; + char* xres; + + if ((xres = esock_decode_bufsz(env, + eVal, + SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT, + &val)) != NULL) + return esock_make_error_str(env, xres); + + descP->wCtrlSz = val; + + return esock_atom_ok; +} + + + /* The option has *not* been encoded. Instead it has been provided * in "native mode" (option is provided as is and value as a binary). */ @@ -8169,6 +8267,18 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, result = ngetopt_otp_ctrl_proc(env, descP); break; + case SOCKET_OPT_OTP_RCVBUF: + result = ngetopt_otp_rcvbuf(env, descP); + break; + + case SOCKET_OPT_OTP_RCVCTRLBUF: + result = ngetopt_otp_rcvctrlbuf(env, descP); + break; + + case SOCKET_OPT_OTP_SNDCTRLBUF: + result = ngetopt_otp_sndctrlbuf(env, descP); + break; + default: result = esock_make_error(env, esock_atom_einval); break; @@ -8220,6 +8330,42 @@ ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, +/* ngetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf options + */ +static +ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = MKI(env, descP->rBufSz); + + return esock_make_ok2(env, eVal); +} + + +/* ngetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf options + */ +static +ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = MKI(env, descP->rCtrlSz); + + return esock_make_ok2(env, eVal); +} + + +/* ngetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf options + */ +static +ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = MKI(env, descP->wCtrlSz); + + return esock_make_ok2(env, eVal); +} + + /* The option has *not* been encoded. Instead it has been provided * in "native mode" (option is provided as is). In this case it will have the * format: {NativeOpt :: integer(), ValueSize :: non_neg_integer()} diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 8bb725fb5b..ff50fd2384 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1261,6 +1261,35 @@ char* esock_decode_protocol(ErlNifEnv* env, +/* +++ esock_decode_bufsz +++ + * + * Decode an buffer size. The size of a buffer is: + * + * Sz > 0 => Use provided value + * Sz => Use provided default + * + */ +extern +char* esock_decode_bufsz(ErlNifEnv* env, + ERL_NIF_TERM eVal, + size_t defSz, + size_t* sz) +{ + int val; + + if (!GET_INT(env, eVal, &val)) + return ESOCK_STR_EINVAL; + + if (val > 0) + *sz = (size_t) val; + else + *sz = defSz; + + return NULL; +} + + + /* *** esock_decode_string *** * * Decode a string value. A successful decode results in an diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index a38453e238..1b5d003155 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -157,6 +157,12 @@ char* esock_encode_protocol(ErlNifEnv* env, int type, ERL_NIF_TERM* eProtocol); +extern +char* esock_decode_bufsz(ErlNifEnv* env, + ERL_NIF_TERM eVal, + size_t defSz, + size_t* sz); + extern BOOLEAN_T esock_decode_string(ErlNifEnv* env, const ERL_NIF_TERM eString, -- cgit v1.2.3 From 4b1e1e148a5cc9c19127b61aaa3e15eeeaea6cb2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 4 Oct 2018 10:06:16 +0200 Subject: [socket-nif] Socket option 'SO_DOMAIN' not avalable on all platforms Internally in the socket module we accessed domain, type and protocol for an open socket. But as it turns out, domain (family) is not actually available as a socket option on all platforms. FreeBSD in this case. So, since we store these values in the socket descriptor anyway, we switch to use these values for our internal use instead. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 146 ++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 3 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 876bed3135..b821fd6575 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -319,8 +319,8 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; /* Debug stuff... */ -#define SOCKET_NIF_DEBUG_DEFAULT FALSE -#define SOCKET_DEBUG_DEFAULT FALSE +#define SOCKET_GLOBAL_DEBUG_DEFAULT FALSE +#define SOCKET_DEBUG_DEFAULT FALSE /* Counters and stuff (Don't know where to sent this stuff anyway) */ #define SOCKET_NIF_IOW_DEFAULT FALSE @@ -504,6 +504,9 @@ typedef union { #define SOCKET_OPT_OTP_RCVBUF 4 #define SOCKET_OPT_OTP_RCVCTRLBUF 6 #define SOCKET_OPT_OTP_SNDCTRLBUF 7 +#define SOCKET_OPT_OTP_DOMAIN 0xFF01 // INTERNAL AND ONLY GET +#define SOCKET_OPT_OTP_TYPE 0xFF02 // INTERNAL AND ONLY GET +#define SOCKET_OPT_OTP_PROTOCOL 0xFF03 // INTERNAL AND ONLY GET #define SOCKET_OPT_SOCK_ACCEPTCONN 1 #define SOCKET_OPT_SOCK_BINDTODEVICE 3 @@ -1510,6 +1513,12 @@ static ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_domain(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_type(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_protocol(ErlNifEnv* env, + SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, SocketDescriptor* descP, int level, @@ -8279,6 +8288,19 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, result = ngetopt_otp_sndctrlbuf(env, descP); break; + /* *** INTERNAL *** */ + case SOCKET_OPT_OTP_DOMAIN: + result = ngetopt_otp_domain(env, descP); + break; + + case SOCKET_OPT_OTP_TYPE: + result = ngetopt_otp_type(env, descP); + break; + + case SOCKET_OPT_OTP_PROTOCOL: + result = ngetopt_otp_protocol(env, descP); + break; + default: result = esock_make_error(env, esock_atom_einval); break; @@ -8366,6 +8388,124 @@ ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, } +/* ngetopt_otp_domain - Handle the OTP (level) domain options. + */ +static +ERL_NIF_TERM ngetopt_otp_domain(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + int val = descP->domain; + + switch (val) { + case AF_INET: + result = esock_make_ok2(env, esock_atom_inet); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + result = esock_make_ok2(env, esock_atom_inet6); + break; +#endif + +#if defined(HAVE_SYS_UN_H) + case AF_UNIX: + result = esock_make_ok2(env, esock_atom_local); + break; +#endif + + default: + result = esock_make_error(env, + MKT2(env, + esock_atom_unknown, + MKI(env, val))); + break; + } + + return result; +} + + +/* ngetopt_otp_type - Handle the OTP (level) type options. + */ +static +ERL_NIF_TERM ngetopt_otp_type(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + int val = descP->type; + + 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; +} + + +/* ngetopt_otp_protocol - Handle the OTP (level) protocol options. + */ +static +ERL_NIF_TERM ngetopt_otp_protocol(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result; + int val = descP->protocol; + + switch (val) { + case IPPROTO_IP: + result = esock_make_ok2(env, esock_atom_ip); + break; + + case IPPROTO_TCP: + result = esock_make_ok2(env, esock_atom_tcp); + break; + + case IPPROTO_UDP: + result = esock_make_ok2(env, esock_atom_udp); + break; + +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + result = esock_make_ok2(env, esock_atom_sctp); + break; +#endif + + default: + result = esock_make_error(env, + MKT2(env, esock_atom_unknown, MKI(env, val))); + break; + } + + return result; +} + + + /* The option has *not* been encoded. Instead it has been provided * in "native mode" (option is provided as is). In this case it will have the * format: {NativeOpt :: integer(), ValueSize :: non_neg_integer()} @@ -15279,7 +15419,7 @@ BOOLEAN_T extract_debug(ErlNifEnv* env, */ ERL_NIF_TERM debug = MKA(env, "debug"); - return esock_extract_bool_from_map(env, map, debug, SOCKET_NIF_DEBUG_DEFAULT); + return esock_extract_bool_from_map(env, map, debug, SOCKET_GLOBAL_DEBUG_DEFAULT); } static -- cgit v1.2.3 From 6b43b24610329f9b1201c36cc4dfcb346ed0317c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 4 Oct 2018 12:01:35 +0200 Subject: [socket-nif] Fixed decode of send and recv flags Add explicitly handling the default case, when not flags are provided (value of zero). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 53 ++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 12 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index b821fd6575..4fd10e1ae6 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -3052,26 +3052,36 @@ static ERL_NIF_TERM nconnect(ErlNifEnv* env, SocketDescriptor* descP) { - int code; + int code, save_errno = 0; /* Verify that we are where in the proper state */ - if (!IS_OPEN(descP)) + if (!IS_OPEN(descP)) { + SSDBG( descP, ("SOCKET", "nif_sendto -> not open\r\n") ); return esock_make_error(env, atom_exbadstate); + } - if (IS_CONNECTED(descP)) + if (IS_CONNECTED(descP)) { + SSDBG( descP, ("SOCKET", "nif_sendto -> already connected\r\n") ); return esock_make_error(env, atom_eisconn); + } - if (IS_CONNECTING(descP)) + if (IS_CONNECTING(descP)) { + SSDBG( descP, ("SOCKET", "nif_sendto -> already connecting\r\n") ); return esock_make_error(env, esock_atom_einval); - + } + code = sock_connect(descP->sock, (struct sockaddr*) &descP->remote, descP->addrLen); + save_errno = sock_errno(); + + SSDBG( descP, ("SOCKET", "nif_sendto -> connect result: %d, %d\r\n", + code, save_errno) ); if (IS_SOCKET_ERROR(code) && - ((sock_errno() == ERRNO_BLOCK) || /* Winsock2 */ - (sock_errno() == EINPROGRESS))) { /* Unix & OSE!! */ + ((save_errno == ERRNO_BLOCK) || /* Winsock2 */ + (save_errno == EINPROGRESS))) { /* Unix & OSE!! */ ERL_NIF_TERM ref = MKREF(env); descP->state = SOCKET_STATE_CONNECTING; SELECT(env, @@ -3086,7 +3096,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, */ return esock_atom_ok; } else { - return esock_make_error_errno(env, sock_errno()); + return esock_make_error_errno(env, save_errno); } } @@ -3800,16 +3810,22 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, descP->sock, argv[0], sendRef, sndData.size, eSockAddr, eflags) ); /* THIS TEST IS NOT CORRECT!!! */ - if (!IS_OPEN(descP)) + if (!IS_OPEN(descP)) { + SSDBG( descP, ("SOCKET", "nif_sendto -> not open (%u)\r\n", descP->state) ); return esock_make_error(env, esock_atom_einval); + } - if (!esendflags2sendflags(eflags, &flags)) + if (!esendflags2sendflags(eflags, &flags)) { + SSDBG( descP, ("SOCKET", "nif_sendto -> sendflags decode failed\r\n") ); return esock_make_error(env, esock_atom_einval); + } if ((xres = esock_decode_sockaddr(env, eSockAddr, &remoteAddr, - &remoteAddrLen)) != NULL) + &remoteAddrLen)) != NULL) { + SSDBG( descP, ("SOCKET", "nif_sendto -> sockaddr decode: %s\r\n", xres) ); return esock_make_error_str(env, xres); + } MLOCK(descP->writeMtx); @@ -4345,8 +4361,10 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, /* if (IS_OPEN(descP)) */ /* return esock_make_error(env, atom_enotconn); */ - if (!erecvflags2recvflags(eflags, &flags)) + if (!erecvflags2recvflags(eflags, &flags)) { + SSDBG( descP, ("SOCKET", "nif_recvfrom -> recvflags decode failed\r\n") ); return enif_make_badarg(env); + } MLOCK(descP->readMtx); @@ -14198,6 +14216,12 @@ BOOLEAN_T esendflags2sendflags(unsigned int eflags, int* flags) unsigned int ef; int tmp = 0; + /* First, check if we have any flags at all */ + if (eflags == 0) { + *flags = 0; + return TRUE; + } + for (ef = SOCKET_SEND_FLAG_LOW; ef <= SOCKET_SEND_FLAG_HIGH; ef++) { switch (ef) { @@ -14269,6 +14293,11 @@ BOOLEAN_T erecvflags2recvflags(unsigned int eflags, int* flags) "\r\n eflags: %d" "\r\n", eflags) ); + if (eflags == 0) { + *flags = 0; + return TRUE; + } + for (ef = SOCKET_RECV_FLAG_LOW; ef <= SOCKET_RECV_FLAG_HIGH; ef++) { SGDBG( ("SOCKET", "erecvflags2recvflags -> iteration" -- cgit v1.2.3 From 352b28117de7e523ea09fd2b6a5066d8ffd1156b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 4 Oct 2018 15:24:13 +0200 Subject: [socket-nif] Add owner validation when setopt controlling_process Before allowing setopt(Sock, opt, controlling_process, NewPid), verify that the caller is actually the current controlling_process. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 11 +++++++++++ erts/emulator/nifs/common/socket_nif.c | 14 +++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index c3595e495d..f9246856fa 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -103,8 +103,10 @@ typedef unsigned int BOOLEAN_T; /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * "Global" atoms */ +extern ERL_NIF_TERM esock_atom_accept; extern ERL_NIF_TERM esock_atom_addr; extern ERL_NIF_TERM esock_atom_any; +extern ERL_NIF_TERM esock_atom_connect; extern ERL_NIF_TERM esock_atom_credentials; extern ERL_NIF_TERM esock_atom_ctrl; extern ERL_NIF_TERM esock_atom_ctrunc; @@ -129,6 +131,8 @@ extern ERL_NIF_TERM esock_atom_local; extern ERL_NIF_TERM esock_atom_loopback; extern ERL_NIF_TERM esock_atom_lowdelay; extern ERL_NIF_TERM esock_atom_mincost; +extern ERL_NIF_TERM esock_atom_not_found; +extern ERL_NIF_TERM esock_atom_not_owner; extern ERL_NIF_TERM esock_atom_ok; extern ERL_NIF_TERM esock_atom_oob; extern ERL_NIF_TERM esock_atom_origdstaddr; @@ -138,11 +142,18 @@ extern ERL_NIF_TERM esock_atom_port; extern ERL_NIF_TERM esock_atom_protocol; extern ERL_NIF_TERM esock_atom_raw; extern ERL_NIF_TERM esock_atom_rdm; +extern ERL_NIF_TERM esock_atom_recv; +extern ERL_NIF_TERM esock_atom_recvfrom; +extern ERL_NIF_TERM esock_atom_recvmsg; extern ERL_NIF_TERM esock_atom_reliability; extern ERL_NIF_TERM esock_atom_rights; extern ERL_NIF_TERM esock_atom_scope_id; extern ERL_NIF_TERM esock_atom_sctp; extern ERL_NIF_TERM esock_atom_sec; +extern ERL_NIF_TERM esock_atom_select_sent; +extern ERL_NIF_TERM esock_atom_send; +extern ERL_NIF_TERM esock_atom_sendmsg; +extern ERL_NIF_TERM esock_atom_sendto; extern ERL_NIF_TERM esock_atom_seqpacket; extern ERL_NIF_TERM esock_atom_socket; extern ERL_NIF_TERM esock_atom_spec_dst; diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 876bed3135..a762742cd7 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -2391,6 +2391,7 @@ ERL_NIF_TERM esock_atom_loopback; ERL_NIF_TERM esock_atom_lowdelay; ERL_NIF_TERM esock_atom_mincost; ERL_NIF_TERM esock_atom_not_found; +ERL_NIF_TERM esock_atom_not_owner; ERL_NIF_TERM esock_atom_ok; ERL_NIF_TERM esock_atom_oob; ERL_NIF_TERM esock_atom_origdstaddr; @@ -5074,7 +5075,7 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - ErlNifPid newCtrlPid; + ErlNifPid caller, newCtrlPid; ErlNifMonitor newCtrlMon; int xres; @@ -5083,6 +5084,16 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, "\r\n eVal: %T" "\r\n", eVal) ); + /* Before we begin, ensure that caller is (current) controlling-process */ + if (enif_self(env, &caller) == NULL) + return esock_make_error(env, atom_exself); + + if (!compare_pids(env, &descP->ctrlPid, &caller)) { + SSDBG( descP, ("SOCKET", "nsetopt_otp_ctrl_proc -> not owner (%T)\r\n", + descP->ctrlPid) ); + return esock_make_error(env, esock_atom_not_owner); + } + if (!GET_LPID(env, eVal, &newCtrlPid)) { esock_warning_msg("Failed get pid of new controlling process\r\n"); return esock_make_error(env, esock_atom_einval); @@ -15413,6 +15424,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_lowdelay = MKA(env, "lowdelay"); esock_atom_mincost = MKA(env, "mincost"); esock_atom_not_found = MKA(env, "not_found"); + esock_atom_not_owner = MKA(env, "not_owner"); esock_atom_ok = MKA(env, "ok"); esock_atom_oob = MKA(env, "oob"); esock_atom_origdstaddr = MKA(env, "origdstaddr"); -- cgit v1.2.3 From 6e931258872c15404aa1dfd5198f2b490452b30b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 5 Oct 2018 14:52:47 +0200 Subject: [socket-nif] Add *preliminary* new function supports/0,1 --- erts/emulator/nifs/common/socket_nif.c | 37 +++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 0ef88162bc..4def45f869 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -860,6 +860,9 @@ typedef struct { static ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_supports(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); /* This is a *global* debug function (enable or disable for all operations and all sockets. @@ -2536,6 +2539,7 @@ static SocketData data; * Utility and admin functions: * ---------------------------- * nif_info/0 + * nif_supports/1 * (nif_debug/1) * * The "proper" socket functions: @@ -2619,6 +2623,31 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, } + +/* ---------------------------------------------------------------------- + * nif_supports + * + * Description: + * This function is intended to answer the question: "Is X supported?" + * Currently only one key is "supported": options + * That results in a list of all *known options* (known by us) and if + * the platform supports (OS) it or not. + */ + +static +ERL_NIF_TERM nif_support(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 1) { + return enif_make_badarg(env); + } else { + return MKEL(env); // PLACEHOLDER + } +} + + + /* ---------------------------------------------------------------------- * nif_open * @@ -15412,9 +15441,11 @@ void socket_down_reader(ErlNifEnv* env, static ErlNifFunc socket_funcs[] = { - // Some utility functions - {"nif_info", 0, nif_info, 0}, - // {"nif_debug", 1, nif_debug_, 0}, + // Some utility and support functions + {"nif_info", 0, nif_info, 0}, + {"nif_supports", 1, nif_supports, 0}, + // {"nif_debug", 1, nif_debug, 0}, + // {"nif_command", 1, nif_command, 0}, // The proper "socket" interface // nif_open/1 is used when we already have a file descriptor -- cgit v1.2.3 From 09ada8ef363de23dff9af58b2284ea8aaf399071 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 5 Oct 2018 18:46:13 +0200 Subject: [socket-nif] Add first three socket (socket) options to supports Added the first three socket (level socket) options to the supports function(s). OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 3 + erts/emulator/nifs/common/socket_nif.c | 335 ++++++++++++++++++++++++++------- 2 files changed, 265 insertions(+), 73 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index f9246856fa..69bd6aba96 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -104,8 +104,11 @@ typedef unsigned int BOOLEAN_T; * "Global" atoms */ extern ERL_NIF_TERM esock_atom_accept; +extern ERL_NIF_TERM esock_atom_acceptconn; extern ERL_NIF_TERM esock_atom_addr; extern ERL_NIF_TERM esock_atom_any; +extern ERL_NIF_TERM esock_atom_bindtodevice; +extern ERL_NIF_TERM esock_atom_broadcast; extern ERL_NIF_TERM esock_atom_connect; extern ERL_NIF_TERM esock_atom_credentials; extern ERL_NIF_TERM esock_atom_ctrl; diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 4def45f869..964b9ccd73 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -603,6 +603,9 @@ typedef union { #define ESOCK_RECVMSG_IOVEC_SZ 1 +#define SOCKET_SUPPORTS_OPTIONS 0x0001 + + /* =================================================================== * * * @@ -931,6 +934,14 @@ static ERL_NIF_TERM nif_cancel(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nsupports(ErlNifEnv* env, int key); +static ERL_NIF_TERM nsupports_options(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env); static ERL_NIF_TERM nopen(ErlNifEnv* env, int domain, @@ -2375,8 +2386,11 @@ static char str_exsend[] = "exsend"; // failed send /* *** "Global" Atoms *** */ ERL_NIF_TERM esock_atom_accept; +ERL_NIF_TERM esock_atom_acceptconn; ERL_NIF_TERM esock_atom_addr; ERL_NIF_TERM esock_atom_any; +ERL_NIF_TERM esock_atom_bindtodevice; +ERL_NIF_TERM esock_atom_broadcast; ERL_NIF_TERM esock_atom_connect; ERL_NIF_TERM esock_atom_credentials; ERL_NIF_TERM esock_atom_ctrl; @@ -2632,18 +2646,190 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, * Currently only one key is "supported": options * That results in a list of all *known options* (known by us) and if * the platform supports (OS) it or not. + * + * Key + * --- + * options [{socket, [{Opt, boolean()}]}, + * {ip, [{Opt, boolean()}]}, + * {ipv6, [{Opt, boolean()}]}, + * {tcp, [{Opt, boolean()}]}, + * {udp, [{Opt, boolean()}]}, + * {sctp, [{Opt, boolean()}]}] */ static -ERL_NIF_TERM nif_support(ErlNifEnv* env, - int argc, - const ERL_NIF_TERM argv[]) +ERL_NIF_TERM nif_supports(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) { - if (argc != 1) { + int key; + + SGDBG( ("SOCKET", "nif_supports -> entry with %d args\r\n", argc) ); + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 1) || + !GET_INT(env, argv[0], &key)) { return enif_make_badarg(env); - } else { - return MKEL(env); // PLACEHOLDER } + + return nsupports(env, key); +} + + + +/* nopen - create an endpoint for communication + * + * Assumes the input has been validated. + * + * Normally we want debugging on (individual) sockets to be controlled + * by the sockets own debug flag. But since we don't even have a socket + * yet, we must use the global debug flag. + */ +static +ERL_NIF_TERM nsupports(ErlNifEnv* env, int key) +{ + ERL_NIF_TERM result; + + SGDBG( ("SOCKET", "nsupports -> entry with 0x%lX\r\n", key) ); + + switch (key) { + case SOCKET_SUPPORTS_OPTIONS: + result = nsupports_options(env); + break; + + default: + result = esock_atom_false; + break; + } + + return result; +} + + +static +ERL_NIF_TERM nsupports_options(ErlNifEnv* env) +{ + ERL_NIF_TERM sockOpts = nsupports_options_socket(env); + ERL_NIF_TERM sockOptsT = MKT2(env, esock_atom_socket, sockOpts); + ERL_NIF_TERM ipOpts = nsupports_options_ip(env); + ERL_NIF_TERM ipOptsT = MKT2(env, esock_atom_ip, ipOpts); + ERL_NIF_TERM ipv6Opts = nsupports_options_ipv6(env); + ERL_NIF_TERM ipv6OptsT = MKT2(env, esock_atom_ipv6, ipv6Opts); + ERL_NIF_TERM tcpOpts = nsupports_options_tcp(env); + ERL_NIF_TERM tcpOptsT = MKT2(env, esock_atom_tcp, tcpOpts); + ERL_NIF_TERM udpOpts = nsupports_options_udp(env); + ERL_NIF_TERM udpOptsT = MKT2(env, esock_atom_udp, udpOpts); + ERL_NIF_TERM sctpOpts = nsupports_options_sctp(env); + ERL_NIF_TERM sctpOptsT = MKT2(env, esock_atom_sctp, sctpOpts); + ERL_NIF_TERM optsA[] = {sockOptsT, + ipOptsT, ipv6OptsT, + tcpOptsT, udpOptsT, sctpOptsT}; + unsigned int lenOptsA = sizeof(optsA) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM optsL = MKLA(env, optsA, lenOptsA); + + return optsL; +} + + + +static +ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env) +{ + SocketTArray opts = TARRAY_CREATE(128); + ERL_NIF_TERM tmp, optsL; + + + /* *** SOCKET_OPT_SOCK_ACCEPTCONN => SO_ACCEPTCONN *** */ +#if defined(SO_ACCEPTCONN) + tmp = MKT2(env, esock_atom_acceptconn, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_acceptconn, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_BINDTODEVICE => SO_BINDTODEVICE *** */ +#if defined(SO_BINDTODEVICE) + tmp = MKT2(env, esock_atom_bindtodevice, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_bindtodevice, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_BROADCAST => SO_BROADCAST *** */ +#if defined(SO_BROADCAST) + tmp = MKT2(env, esock_atom_broadcast, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_broadcast, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + TARRAY_TOLIST(opts, env, &optsL); + + return optsL; +} + + + +static +ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + + result = MKEL(env); + + return result; +} + + + +static +ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + + result = MKEL(env); + + return result; +} + + + +static +ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + + result = MKEL(env); + + return result; +} + + + +static +ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + + result = MKEL(env); + + return result; +} + + + +static +ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + + result = MKEL(env); + + return result; } @@ -15595,73 +15781,76 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_want = MKA(env, str_want); /* Global atom(s) */ - esock_atom_accept = MKA(env, "accept"); - esock_atom_addr = MKA(env, "addr"); - esock_atom_any = MKA(env, "any"); - esock_atom_connect = MKA(env, "connect"); - esock_atom_credentials = MKA(env, "credentials"); - esock_atom_ctrl = MKA(env, "ctrl"); - esock_atom_ctrunc = MKA(env, "ctrunc"); - esock_atom_data = MKA(env, "data"); - esock_atom_debug = MKA(env, "debug"); - esock_atom_dgram = MKA(env, "dgram"); - esock_atom_eor = MKA(env, "eor"); - esock_atom_error = MKA(env, "error"); - esock_atom_errqueue = MKA(env, "errqueue"); - esock_atom_false = MKA(env, "false"); - esock_atom_family = MKA(env, "family"); - esock_atom_flags = MKA(env, "flags"); - esock_atom_flowinfo = MKA(env, "flowinfo"); - esock_atom_ifindex = MKA(env, "ifindex"); - esock_atom_inet = MKA(env, "inet"); - esock_atom_inet6 = MKA(env, "inet6"); - esock_atom_iov = MKA(env, "iov"); - esock_atom_ip = MKA(env, "ip"); - esock_atom_ipv6 = MKA(env, "ipv6"); - esock_atom_level = MKA(env, "level"); - esock_atom_local = MKA(env, "local"); - esock_atom_loopback = MKA(env, "loopback"); - esock_atom_lowdelay = MKA(env, "lowdelay"); - esock_atom_mincost = MKA(env, "mincost"); - esock_atom_not_found = MKA(env, "not_found"); - esock_atom_not_owner = MKA(env, "not_owner"); - esock_atom_ok = MKA(env, "ok"); - esock_atom_oob = MKA(env, "oob"); - esock_atom_origdstaddr = MKA(env, "origdstaddr"); - esock_atom_path = MKA(env, "path"); - esock_atom_pktinfo = MKA(env, "pktinfo"); - esock_atom_port = MKA(env, "port"); - esock_atom_protocol = MKA(env, "protocol"); - esock_atom_raw = MKA(env, "raw"); - esock_atom_rdm = MKA(env, "rdm"); - esock_atom_recv = MKA(env, "recv"); - esock_atom_recvfrom = MKA(env, "recvfrom"); - esock_atom_recvmsg = MKA(env, "recvmsg"); - esock_atom_reliability = MKA(env, "reliability"); - esock_atom_rights = MKA(env, "rights"); - esock_atom_scope_id = MKA(env, "scope_id"); - esock_atom_sctp = MKA(env, "sctp"); - esock_atom_sec = MKA(env, "sec"); - esock_atom_select_sent = MKA(env, "select_sent"); - esock_atom_send = MKA(env, "send"); - esock_atom_sendmsg = MKA(env, "sendmsg"); - esock_atom_sendto = MKA(env, "sendto"); - esock_atom_seqpacket = MKA(env, "seqpacket"); - esock_atom_socket = MKA(env, "socket"); - esock_atom_spec_dst = MKA(env, "spec_dst"); - esock_atom_stream = MKA(env, "stream"); - esock_atom_tcp = MKA(env, "tcp"); - esock_atom_throughput = MKA(env, "throughput"); - esock_atom_timestamp = MKA(env, "timestamp"); - esock_atom_tos = MKA(env, "tos"); - esock_atom_true = MKA(env, "true"); - esock_atom_trunc = MKA(env, "trunc"); - esock_atom_ttl = MKA(env, "ttl"); - esock_atom_type = MKA(env, "type"); - esock_atom_udp = MKA(env, "udp"); - esock_atom_undefined = MKA(env, "undefined"); - esock_atom_unknown = MKA(env, "unknown"); - esock_atom_usec = MKA(env, "usec"); + esock_atom_accept = MKA(env, "accept"); + esock_atom_acceptconn = MKA(env, "acceptconn"); + esock_atom_addr = MKA(env, "addr"); + esock_atom_any = MKA(env, "any"); + esock_atom_bindtodevice = MKA(env, "bindtodevice"); + esock_atom_broadcast = MKA(env, "broadcast"); + esock_atom_connect = MKA(env, "connect"); + esock_atom_credentials = MKA(env, "credentials"); + esock_atom_ctrl = MKA(env, "ctrl"); + esock_atom_ctrunc = MKA(env, "ctrunc"); + esock_atom_data = MKA(env, "data"); + esock_atom_debug = MKA(env, "debug"); + esock_atom_dgram = MKA(env, "dgram"); + esock_atom_eor = MKA(env, "eor"); + esock_atom_error = MKA(env, "error"); + esock_atom_errqueue = MKA(env, "errqueue"); + esock_atom_false = MKA(env, "false"); + esock_atom_family = MKA(env, "family"); + esock_atom_flags = MKA(env, "flags"); + esock_atom_flowinfo = MKA(env, "flowinfo"); + esock_atom_ifindex = MKA(env, "ifindex"); + esock_atom_inet = MKA(env, "inet"); + esock_atom_inet6 = MKA(env, "inet6"); + esock_atom_iov = MKA(env, "iov"); + esock_atom_ip = MKA(env, "ip"); + esock_atom_ipv6 = MKA(env, "ipv6"); + esock_atom_level = MKA(env, "level"); + esock_atom_local = MKA(env, "local"); + esock_atom_loopback = MKA(env, "loopback"); + esock_atom_lowdelay = MKA(env, "lowdelay"); + esock_atom_mincost = MKA(env, "mincost"); + esock_atom_not_found = MKA(env, "not_found"); + esock_atom_not_owner = MKA(env, "not_owner"); + esock_atom_ok = MKA(env, "ok"); + esock_atom_oob = MKA(env, "oob"); + esock_atom_origdstaddr = MKA(env, "origdstaddr"); + esock_atom_path = MKA(env, "path"); + esock_atom_pktinfo = MKA(env, "pktinfo"); + esock_atom_port = MKA(env, "port"); + esock_atom_protocol = MKA(env, "protocol"); + esock_atom_raw = MKA(env, "raw"); + esock_atom_rdm = MKA(env, "rdm"); + esock_atom_recv = MKA(env, "recv"); + esock_atom_recvfrom = MKA(env, "recvfrom"); + esock_atom_recvmsg = MKA(env, "recvmsg"); + esock_atom_reliability = MKA(env, "reliability"); + esock_atom_rights = MKA(env, "rights"); + esock_atom_scope_id = MKA(env, "scope_id"); + esock_atom_sctp = MKA(env, "sctp"); + esock_atom_sec = MKA(env, "sec"); + esock_atom_select_sent = MKA(env, "select_sent"); + esock_atom_send = MKA(env, "send"); + esock_atom_sendmsg = MKA(env, "sendmsg"); + esock_atom_sendto = MKA(env, "sendto"); + esock_atom_seqpacket = MKA(env, "seqpacket"); + esock_atom_socket = MKA(env, "socket"); + esock_atom_spec_dst = MKA(env, "spec_dst"); + esock_atom_stream = MKA(env, "stream"); + esock_atom_tcp = MKA(env, "tcp"); + esock_atom_throughput = MKA(env, "throughput"); + esock_atom_timestamp = MKA(env, "timestamp"); + esock_atom_tos = MKA(env, "tos"); + esock_atom_true = MKA(env, "true"); + esock_atom_trunc = MKA(env, "trunc"); + esock_atom_ttl = MKA(env, "ttl"); + esock_atom_type = MKA(env, "type"); + esock_atom_udp = MKA(env, "udp"); + esock_atom_undefined = MKA(env, "undefined"); + esock_atom_unknown = MKA(env, "unknown"); + esock_atom_usec = MKA(env, "usec"); /* Global error codes */ esock_atom_eafnosupport = MKA(env, ESOCK_STR_EAFNOSUPPORT); -- cgit v1.2.3 From f1c6c983a7f21b55c597279e806b5df8f38d4947 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 8 Oct 2018 15:32:37 +0200 Subject: [socket-nif] Add the options for socket, ip and ipv6 for supports Add the options for level socket (full), ip and ipv6 to the supports function. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 77 +++ erts/emulator/nifs/common/socket_nif.c | 914 ++++++++++++++++++++++++++++++++- 2 files changed, 984 insertions(+), 7 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 69bd6aba96..93722d2e98 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -105,10 +105,19 @@ typedef unsigned int BOOLEAN_T; */ extern ERL_NIF_TERM esock_atom_accept; extern ERL_NIF_TERM esock_atom_acceptconn; +extern ERL_NIF_TERM esock_atom_acceptfilter; extern ERL_NIF_TERM esock_atom_addr; +extern ERL_NIF_TERM esock_atom_addrform; +extern ERL_NIF_TERM esock_atom_add_membership; +extern ERL_NIF_TERM esock_atom_add_source_membership; extern ERL_NIF_TERM esock_atom_any; +extern ERL_NIF_TERM esock_atom_authhdr; +extern ERL_NIF_TERM esock_atom_auth_level; extern ERL_NIF_TERM esock_atom_bindtodevice; +extern ERL_NIF_TERM esock_atom_block_source; extern ERL_NIF_TERM esock_atom_broadcast; +extern ERL_NIF_TERM esock_atom_busy_poll; +extern ERL_NIF_TERM esock_atom_checksum; extern ERL_NIF_TERM esock_atom_connect; extern ERL_NIF_TERM esock_atom_credentials; extern ERL_NIF_TERM esock_atom_ctrl; @@ -116,63 +125,131 @@ extern ERL_NIF_TERM esock_atom_ctrunc; extern ERL_NIF_TERM esock_atom_data; extern ERL_NIF_TERM esock_atom_debug; extern ERL_NIF_TERM esock_atom_dgram; +extern ERL_NIF_TERM esock_atom_domain; +extern ERL_NIF_TERM esock_atom_dontfrag; +extern ERL_NIF_TERM esock_atom_dontroute; +extern ERL_NIF_TERM esock_atom_drop_membership; +extern ERL_NIF_TERM esock_atom_drop_source_membership; +extern ERL_NIF_TERM esock_atom_dstopts; extern ERL_NIF_TERM esock_atom_eor; extern ERL_NIF_TERM esock_atom_error; extern ERL_NIF_TERM esock_atom_errqueue; +extern ERL_NIF_TERM esock_atom_esp_network_level; +extern ERL_NIF_TERM esock_atom_esp_trans_level; +extern ERL_NIF_TERM esock_atom_faith; extern ERL_NIF_TERM esock_atom_false; extern ERL_NIF_TERM esock_atom_family; extern ERL_NIF_TERM esock_atom_flags; extern ERL_NIF_TERM esock_atom_flowinfo; +extern ERL_NIF_TERM esock_atom_freebind; +extern ERL_NIF_TERM esock_atom_hdrincl; +extern ERL_NIF_TERM esock_atom_hoplimit; +extern ERL_NIF_TERM esock_atom_hopopts; extern ERL_NIF_TERM esock_atom_ifindex; extern ERL_NIF_TERM esock_atom_inet; extern ERL_NIF_TERM esock_atom_inet6; extern ERL_NIF_TERM esock_atom_iov; extern ERL_NIF_TERM esock_atom_ip; +extern ERL_NIF_TERM esock_atom_ipcomp_level; extern ERL_NIF_TERM esock_atom_ipv6; +extern ERL_NIF_TERM esock_atom_join_group; +extern ERL_NIF_TERM esock_atom_keepalive; +extern ERL_NIF_TERM esock_atom_leave_group; extern ERL_NIF_TERM esock_atom_level; +extern ERL_NIF_TERM esock_atom_linger; extern ERL_NIF_TERM esock_atom_local; extern ERL_NIF_TERM esock_atom_loopback; extern ERL_NIF_TERM esock_atom_lowdelay; +extern ERL_NIF_TERM esock_atom_mark; extern ERL_NIF_TERM esock_atom_mincost; +extern ERL_NIF_TERM esock_atom_minttl; +extern ERL_NIF_TERM esock_atom_msfilter; +extern ERL_NIF_TERM esock_atom_mtu; +extern ERL_NIF_TERM esock_atom_mtu_discover; +extern ERL_NIF_TERM esock_atom_multicast_all; +extern ERL_NIF_TERM esock_atom_multicast_hops; +extern ERL_NIF_TERM esock_atom_multicast_if; +extern ERL_NIF_TERM esock_atom_multicast_loop; +extern ERL_NIF_TERM esock_atom_multicast_ttl; +extern ERL_NIF_TERM esock_atom_nodefrag; extern ERL_NIF_TERM esock_atom_not_found; extern ERL_NIF_TERM esock_atom_not_owner; extern ERL_NIF_TERM esock_atom_ok; extern ERL_NIF_TERM esock_atom_oob; +extern ERL_NIF_TERM esock_atom_oobinline; +extern ERL_NIF_TERM esock_atom_options; extern ERL_NIF_TERM esock_atom_origdstaddr; +extern ERL_NIF_TERM esock_atom_passcred; extern ERL_NIF_TERM esock_atom_path; +extern ERL_NIF_TERM esock_atom_peekcred; +extern ERL_NIF_TERM esock_atom_peek_off; extern ERL_NIF_TERM esock_atom_pktinfo; +extern ERL_NIF_TERM esock_atom_pktoptions; extern ERL_NIF_TERM esock_atom_port; +extern ERL_NIF_TERM esock_atom_portrange; +extern ERL_NIF_TERM esock_atom_priority; extern ERL_NIF_TERM esock_atom_protocol; extern ERL_NIF_TERM esock_atom_raw; +extern ERL_NIF_TERM esock_atom_rcvbuf; +extern ERL_NIF_TERM esock_atom_rcvbufforce; +extern ERL_NIF_TERM esock_atom_rcvlowat; +extern ERL_NIF_TERM esock_atom_rcvtimeo; extern ERL_NIF_TERM esock_atom_rdm; extern ERL_NIF_TERM esock_atom_recv; +extern ERL_NIF_TERM esock_atom_recvdstaddr; +extern ERL_NIF_TERM esock_atom_recverr; extern ERL_NIF_TERM esock_atom_recvfrom; +extern ERL_NIF_TERM esock_atom_recvif; extern ERL_NIF_TERM esock_atom_recvmsg; +extern ERL_NIF_TERM esock_atom_recvopts; +extern ERL_NIF_TERM esock_atom_recvorigdstaddr; +extern ERL_NIF_TERM esock_atom_recvpktinfo; +extern ERL_NIF_TERM esock_atom_recvtclass; +extern ERL_NIF_TERM esock_atom_recvtos; +extern ERL_NIF_TERM esock_atom_recvttl; extern ERL_NIF_TERM esock_atom_reliability; +extern ERL_NIF_TERM esock_atom_retopts; +extern ERL_NIF_TERM esock_atom_reuseaddr; +extern ERL_NIF_TERM esock_atom_reuseport; extern ERL_NIF_TERM esock_atom_rights; +extern ERL_NIF_TERM esock_atom_router_alert; +extern ERL_NIF_TERM esock_atom_rthdr; +extern ERL_NIF_TERM esock_atom_rxq_ovfl; extern ERL_NIF_TERM esock_atom_scope_id; extern ERL_NIF_TERM esock_atom_sctp; extern ERL_NIF_TERM esock_atom_sec; extern ERL_NIF_TERM esock_atom_select_sent; extern ERL_NIF_TERM esock_atom_send; extern ERL_NIF_TERM esock_atom_sendmsg; +extern ERL_NIF_TERM esock_atom_sendsrcaddr; extern ERL_NIF_TERM esock_atom_sendto; extern ERL_NIF_TERM esock_atom_seqpacket; +extern ERL_NIF_TERM esock_atom_setfib; +extern ERL_NIF_TERM esock_atom_sndbuf; +extern ERL_NIF_TERM esock_atom_sndbufforce; +extern ERL_NIF_TERM esock_atom_sndlowat; +extern ERL_NIF_TERM esock_atom_sndtimeo; extern ERL_NIF_TERM esock_atom_socket; extern ERL_NIF_TERM esock_atom_spec_dst; extern ERL_NIF_TERM esock_atom_stream; +extern ERL_NIF_TERM esock_atom_tclass; extern ERL_NIF_TERM esock_atom_tcp; extern ERL_NIF_TERM esock_atom_throughput; extern ERL_NIF_TERM esock_atom_timestamp; extern ERL_NIF_TERM esock_atom_tos; +extern ERL_NIF_TERM esock_atom_transparent; extern ERL_NIF_TERM esock_atom_true; extern ERL_NIF_TERM esock_atom_trunc; extern ERL_NIF_TERM esock_atom_ttl; extern ERL_NIF_TERM esock_atom_type; extern ERL_NIF_TERM esock_atom_udp; +extern ERL_NIF_TERM esock_atom_unblock_source; extern ERL_NIF_TERM esock_atom_undefined; +extern ERL_NIF_TERM esock_atom_unicast_hops; extern ERL_NIF_TERM esock_atom_unknown; extern ERL_NIF_TERM esock_atom_usec; +extern ERL_NIF_TERM esock_atom_use_min_mtu; +extern ERL_NIF_TERM esock_atom_v6only; /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 964b9ccd73..359cc7db62 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -2387,74 +2387,151 @@ static char str_exsend[] = "exsend"; // failed send /* *** "Global" Atoms *** */ ERL_NIF_TERM esock_atom_accept; ERL_NIF_TERM esock_atom_acceptconn; +ERL_NIF_TERM esock_atom_acceptfilter; ERL_NIF_TERM esock_atom_addr; +ERL_NIF_TERM esock_atom_addrform; +ERL_NIF_TERM esock_atom_add_membership; +ERL_NIF_TERM esock_atom_add_source_membership; ERL_NIF_TERM esock_atom_any; +ERL_NIF_TERM esock_atom_authhdr; +ERL_NIF_TERM esock_atom_auth_level; ERL_NIF_TERM esock_atom_bindtodevice; +ERL_NIF_TERM esock_atom_block_source; ERL_NIF_TERM esock_atom_broadcast; +ERL_NIF_TERM esock_atom_busy_poll; +ERL_NIF_TERM esock_atom_checksum; ERL_NIF_TERM esock_atom_connect; ERL_NIF_TERM esock_atom_credentials; ERL_NIF_TERM esock_atom_ctrl; ERL_NIF_TERM esock_atom_ctrunc; ERL_NIF_TERM esock_atom_data; -ERL_NIF_TERM esock_atom_dgram; ERL_NIF_TERM esock_atom_debug; +ERL_NIF_TERM esock_atom_dgram; +ERL_NIF_TERM esock_atom_domain; +ERL_NIF_TERM esock_atom_dontfrag; +ERL_NIF_TERM esock_atom_dontroute; +ERL_NIF_TERM esock_atom_drop_membership; +ERL_NIF_TERM esock_atom_drop_source_membership; +ERL_NIF_TERM esock_atom_dstopts; ERL_NIF_TERM esock_atom_eor; ERL_NIF_TERM esock_atom_error; ERL_NIF_TERM esock_atom_errqueue; +ERL_NIF_TERM esock_atom_esp_network_level; +ERL_NIF_TERM esock_atom_esp_trans_level; +ERL_NIF_TERM esock_atom_faith; ERL_NIF_TERM esock_atom_false; ERL_NIF_TERM esock_atom_family; ERL_NIF_TERM esock_atom_flags; ERL_NIF_TERM esock_atom_flowinfo; +ERL_NIF_TERM esock_atom_freebind; +ERL_NIF_TERM esock_atom_hdrincl; +ERL_NIF_TERM esock_atom_hoplimit; +ERL_NIF_TERM esock_atom_hopopts; ERL_NIF_TERM esock_atom_ifindex; ERL_NIF_TERM esock_atom_inet; ERL_NIF_TERM esock_atom_inet6; ERL_NIF_TERM esock_atom_iov; ERL_NIF_TERM esock_atom_ip; +ERL_NIF_TERM esock_atom_ipcomp_level; ERL_NIF_TERM esock_atom_ipv6; +ERL_NIF_TERM esock_atom_join_group; +ERL_NIF_TERM esock_atom_keepalive; +ERL_NIF_TERM esock_atom_leave_group; ERL_NIF_TERM esock_atom_level; +ERL_NIF_TERM esock_atom_linger; ERL_NIF_TERM esock_atom_local; ERL_NIF_TERM esock_atom_loopback; ERL_NIF_TERM esock_atom_lowdelay; +ERL_NIF_TERM esock_atom_mark; ERL_NIF_TERM esock_atom_mincost; +ERL_NIF_TERM esock_atom_minttl; +ERL_NIF_TERM esock_atom_msfilter; +ERL_NIF_TERM esock_atom_mtu; +ERL_NIF_TERM esock_atom_mtu_discover; +ERL_NIF_TERM esock_atom_multicast_all; +ERL_NIF_TERM esock_atom_multicast_hops; +ERL_NIF_TERM esock_atom_multicast_if; +ERL_NIF_TERM esock_atom_multicast_loop; +ERL_NIF_TERM esock_atom_multicast_ttl; +ERL_NIF_TERM esock_atom_nodefrag; ERL_NIF_TERM esock_atom_not_found; ERL_NIF_TERM esock_atom_not_owner; ERL_NIF_TERM esock_atom_ok; ERL_NIF_TERM esock_atom_oob; +ERL_NIF_TERM esock_atom_oobinline; +ERL_NIF_TERM esock_atom_options; ERL_NIF_TERM esock_atom_origdstaddr; +ERL_NIF_TERM esock_atom_passcred; ERL_NIF_TERM esock_atom_path; +ERL_NIF_TERM esock_atom_peekcred; +ERL_NIF_TERM esock_atom_peek_off; ERL_NIF_TERM esock_atom_pktinfo; +ERL_NIF_TERM esock_atom_pktoptions; ERL_NIF_TERM esock_atom_port; +ERL_NIF_TERM esock_atom_portrange; +ERL_NIF_TERM esock_atom_priority; ERL_NIF_TERM esock_atom_protocol; ERL_NIF_TERM esock_atom_raw; +ERL_NIF_TERM esock_atom_rcvbuf; +ERL_NIF_TERM esock_atom_rcvbufforce; +ERL_NIF_TERM esock_atom_rcvlowat; +ERL_NIF_TERM esock_atom_rcvtimeo; ERL_NIF_TERM esock_atom_rdm; ERL_NIF_TERM esock_atom_recv; +ERL_NIF_TERM esock_atom_recvdstaddr; +ERL_NIF_TERM esock_atom_recverr; ERL_NIF_TERM esock_atom_recvfrom; +ERL_NIF_TERM esock_atom_recvif; ERL_NIF_TERM esock_atom_recvmsg; +ERL_NIF_TERM esock_atom_recvopts; +ERL_NIF_TERM esock_atom_recvorigdstaddr; +ERL_NIF_TERM esock_atom_recvpktinfo; +ERL_NIF_TERM esock_atom_recvtclass; +ERL_NIF_TERM esock_atom_recvtos; +ERL_NIF_TERM esock_atom_recvttl; ERL_NIF_TERM esock_atom_reliability; +ERL_NIF_TERM esock_atom_retopts; +ERL_NIF_TERM esock_atom_reuseaddr; +ERL_NIF_TERM esock_atom_reuseport; ERL_NIF_TERM esock_atom_rights; +ERL_NIF_TERM esock_atom_router_alert; +ERL_NIF_TERM esock_atom_rthdr; +ERL_NIF_TERM esock_atom_rxq_ovfl; ERL_NIF_TERM esock_atom_scope_id; ERL_NIF_TERM esock_atom_sctp; ERL_NIF_TERM esock_atom_sec; ERL_NIF_TERM esock_atom_select_sent; ERL_NIF_TERM esock_atom_send; ERL_NIF_TERM esock_atom_sendmsg; +ERL_NIF_TERM esock_atom_sendsrcaddr; ERL_NIF_TERM esock_atom_sendto; ERL_NIF_TERM esock_atom_seqpacket; +ERL_NIF_TERM esock_atom_setfib; ERL_NIF_TERM esock_atom_socket; +ERL_NIF_TERM esock_atom_sndbuf; +ERL_NIF_TERM esock_atom_sndbufforce; +ERL_NIF_TERM esock_atom_sndlowat; +ERL_NIF_TERM esock_atom_sndtimeo; ERL_NIF_TERM esock_atom_spec_dst; ERL_NIF_TERM esock_atom_stream; +ERL_NIF_TERM esock_atom_tclass; ERL_NIF_TERM esock_atom_tcp; ERL_NIF_TERM esock_atom_throughput; ERL_NIF_TERM esock_atom_timestamp; ERL_NIF_TERM esock_atom_tos; +ERL_NIF_TERM esock_atom_transparent; ERL_NIF_TERM esock_atom_true; ERL_NIF_TERM esock_atom_trunc; ERL_NIF_TERM esock_atom_ttl; ERL_NIF_TERM esock_atom_type; ERL_NIF_TERM esock_atom_udp; +ERL_NIF_TERM esock_atom_unblock_source; ERL_NIF_TERM esock_atom_undefined; +ERL_NIF_TERM esock_atom_unicast_hops; ERL_NIF_TERM esock_atom_unknown; ERL_NIF_TERM esock_atom_usec; +ERL_NIF_TERM esock_atom_use_min_mtu; +ERL_NIF_TERM esock_atom_v6only; /* *** "Global" error (=reason) atoms *** */ ERL_NIF_TERM esock_atom_eagain; @@ -2749,6 +2826,11 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env) TARRAY_ADD(opts, tmp); + /* *** SOCKET_OPT_SOCK_ACCEPTFILTER => SO_ACCEPTFILTER *** */ + tmp = MKT2(env, esock_atom_acceptfilter, esock_atom_false); + TARRAY_ADD(opts, tmp); + + /* *** SOCKET_OPT_SOCK_BINDTODEVICE => SO_BINDTODEVICE *** */ #if defined(SO_BINDTODEVICE) tmp = MKT2(env, esock_atom_bindtodevice, esock_atom_true); @@ -2767,6 +2849,222 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env) TARRAY_ADD(opts, tmp); + /* *** SOCKET_OPT_SOCK_BUSY_POLL => SO_BUSY_POLL *** */ + tmp = MKT2(env, esock_atom_busy_poll, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_DEBUG => SO_DEBUG *** */ +#if defined(SO_DEBUG) + tmp = MKT2(env, esock_atom_debug, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_debug, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_DOMAIN => SO_DOMAIN *** */ +#if defined(SO_DOMAIN) + tmp = MKT2(env, esock_atom_domain, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_domain, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_DONTROUTE => SO_DONTROUTE *** */ +#if defined(SO_DONTROUTE) + tmp = MKT2(env, esock_atom_dontroute, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_dontroute, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_ERROR => SO_ERROR *** */ + tmp = MKT2(env, esock_atom_error, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_KEEPALIVE => SO_KEEPALIVE *** */ +#if defined(SO_KEEPALIVE) + tmp = MKT2(env, esock_atom_keepalive, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_keepalive, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_LINGER => SO_LINGER *** */ +#if defined(SO_LINGER) + tmp = MKT2(env, esock_atom_linger, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_linger, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_MARK => SO_MARK *** */ + tmp = MKT2(env, esock_atom_mark, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_OOBINLINE => SO_OOBINLINE *** */ +#if defined(SO_OOBINLINE) + tmp = MKT2(env, esock_atom_oobinline, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_oobinline, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_PASSCRED => SO_PASSCRED *** */ + tmp = MKT2(env, esock_atom_passcred, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_PEEK_OFF => SO_PEEK_OFF *** */ +#if defined(SO_PEEK_OFF) + tmp = MKT2(env, esock_atom_peek_off, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_peek_off, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_PEEKCRED => SO_PEEKCRED *** */ + tmp = MKT2(env, esock_atom_peekcred, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_PRIORITY => SO_PRIORITY *** */ +#if defined(SO_PRIORITY) + tmp = MKT2(env, esock_atom_priority, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_priority, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_PROTOCOL => SO_PROTOCOL *** */ +#if defined(SO_PROTOCOL) + tmp = MKT2(env, esock_atom_protocol, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_protocol, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_RCVBUF => SO_RCVBUF *** */ +#if defined(SO_RCVBUF) + tmp = MKT2(env, esock_atom_rcvbuf, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_rcvbuf, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_RCVBUFFORCE => SO_RCVBUFFORCE *** */ + tmp = MKT2(env, esock_atom_rcvbufforce, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_RCVLOWAT => SO_RCVLOWAT *** */ +#if defined(SO_RCVLOWAT) + tmp = MKT2(env, esock_atom_rcvlowat, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_rcvlowat, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_RCVTIMEO => SO_RCVTIMEO *** */ +#if defined(SO_RCVTIMEO) + tmp = MKT2(env, esock_atom_rcvtimeo, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_rcvtimeo, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_REUSEADDR => SO_REUSEADDR *** */ +#if defined(SO_REUSEADDR) + tmp = MKT2(env, esock_atom_reuseaddr, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_reuseaddr, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_REUSEPORT => SO_REUSEPORT *** */ +#if defined(SO_REUSEPORT) + tmp = MKT2(env, esock_atom_reuseport, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_reuseport, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_RXQ_OVFL => SO_RXQ_OVFL *** */ + tmp = MKT2(env, esock_atom_rxq_ovfl, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_SETFIB => SO_SETFIB *** */ + tmp = MKT2(env, esock_atom_setfib, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_SNDBUF => SO_SNDBUF *** */ +#if defined(SO_SNDBUF) + tmp = MKT2(env, esock_atom_sndbuf, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_sndbuf, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_SNDBUFFORCE => SO_SNDBUFFORCE *** */ + tmp = MKT2(env, esock_atom_sndbufforce, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_SNDLOWAT => SO_SNDLOWAT *** */ +#if defined(SO_SNDLOWAT) + tmp = MKT2(env, esock_atom_sndlowat, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_sndlowat, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_SNDTIMEO => SO_SNDTIMEO *** */ +#if defined(SO_SNDTIMEO) + tmp = MKT2(env, esock_atom_sndtimeo, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_sndtimeo, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_TIMESTAMP => SO_TIMESTAMP *** */ +#if defined(SO_TIMESTAMP) + tmp = MKT2(env, esock_atom_timestamp, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_timestamp, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SOCK_TYPE => SO_TYPE *** */ +#if defined(SO_TYPE) + tmp = MKT2(env, esock_atom_type, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_type, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + TARRAY_TOLIST(opts, env, &optsL); return optsL; @@ -2777,11 +3075,298 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env) static ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env) { - ERL_NIF_TERM result; + SocketTArray opts = TARRAY_CREATE(128); + ERL_NIF_TERM tmp, optsL; - result = MKEL(env); - return result; + /* *** SOCKET_OPT_IP_ADD_MEMBERSHIP => IP_ADD_MEMBERSHIP *** */ +#if defined(IP_ADD_MEMBERSHIP) + tmp = MKT2(env, esock_atom_add_membership, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_add_membership, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP => IP_ADD_SOURCE_MEMBERSHIP *** */ +#if defined(IP_ADD_SOURCE_MEMBERSHIP) + tmp = MKT2(env, esock_atom_add_source_membership, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_add_source_membership, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_BLOCK_SOURCE => IP_BLOCK_SOURCE *** */ +#if defined(IP_BLOCK_SOURCE) + tmp = MKT2(env, esock_atom_block_source, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_block_source, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_DONTFRAG => IP_DONTFRAG *** */ + tmp = MKT2(env, esock_atom_dontfrag, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_DROP_MEMBERSHIP => IP_DROP_MEMBERSHIP *** */ +#if defined(IP_DROP_MEMBERSHIP) + tmp = MKT2(env, esock_atom_drop_membership, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_drop_membership, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP => IP_DROP_SOURCE_MEMBERSHIP *** */ +#if defined(IP_DROP_SOURCE_MEMBERSHIP) + tmp = MKT2(env, esock_atom_drop_source_membership, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_drop_source_membership, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_FREEBIND => IP_FREEBIND *** */ +#if defined(IP_FREEBIND) + tmp = MKT2(env, esock_atom_freebind, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_freebind, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_HDRINCL => IP_HDRINCL *** */ +#if defined(IP_HDRINCL) + tmp = MKT2(env, esock_atom_hdrincl, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_hdrincl, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_MINTTL => IP_MINTTL *** */ +#if defined(IP_MINTTL) + tmp = MKT2(env, esock_atom_minttl, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_minttl, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_MSFILTER => IP_MSFILTER / IP_MSFILTER_SIZE *** */ +#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) + tmp = MKT2(env, esock_atom_msfilter, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_msfilter, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_MTU => IP_MTU *** */ + tmp = MKT2(env, esock_atom_mtu, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_MTU_DISCOVER => IP_MTU_DISCOVER *** */ +#if defined(IP_MTU_DISCOVER) + tmp = MKT2(env, esock_atom_mtu_discover, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_mtu_discover, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_MULTICAST_ALL => IP_MULTICAST_ALL *** */ +#if defined(IP_MULTICAST_ALL) + tmp = MKT2(env, esock_atom_multicast_all, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_multicast_all, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_MULTICAST_IF => IP_MULTICAST_IF *** */ +#if defined(IP_MULTICAST_IF) + tmp = MKT2(env, esock_atom_multicast_if, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_multicast_if, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_MULTICAST_LOOP => IP_MULTICAST_LOOP *** */ +#if defined(IP_MULTICAST_LOOP) + tmp = MKT2(env, esock_atom_multicast_loop, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_multicast_loop, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_MULTICAST_TTL => IP_MULTICAST_TTL *** */ +#if defined(IP_MULTICAST_TTL) + tmp = MKT2(env, esock_atom_multicast_ttl, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_multicast_ttl, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_NODEFRAG => IP_NODEFRAG *** */ +#if defined(IP_NODEFRAG) + tmp = MKT2(env, esock_atom_nodefrag, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_nodefrag, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_OPTIONS => IP_OPTIONS *** */ + tmp = MKT2(env, esock_atom_options, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_PKTINFO => IP_PKTINFO *** */ +#if defined(IP_PKTINFO) + tmp = MKT2(env, esock_atom_pktinfo, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_pktinfo, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_RECVDSTADDR => IP_RECVDSTADDR *** */ +#if defined(IP_RECVDSTADDR) + tmp = MKT2(env, esock_atom_recvdstaddr, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_recvdstaddr, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_RECVERR => IP_RECVERR *** */ +#if defined(IP_RECVERR) + tmp = MKT2(env, esock_atom_recverr, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_recverr, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_RECVIF => IP_RECVIF *** */ +#if defined(IP_RECVIF) + tmp = MKT2(env, esock_atom_recvif, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_recvif, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_RECVOPTS => IP_RECVOPTS *** */ +#if defined(IP_RECVOPTS) + tmp = MKT2(env, esock_atom_recvopts, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_recvopts, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_RECVORIGDSTADDR => IP_RECVORIGDSTADDR *** */ +#if defined(IP_RECVORIGDSTADDR) + tmp = MKT2(env, esock_atom_recvorigdstaddr, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_recvorigdstaddr, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_RECVTOS => IP_RECVTOS *** */ +#if defined(IP_RECVTOS) + tmp = MKT2(env, esock_atom_recvtos, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_recvtos, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_RECVTTL => IP_RECVTTL *** */ +#if defined(IP_RECVTTL) + tmp = MKT2(env, esock_atom_recvttl, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_recvttl, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_RETOPTS => IP_RETOPTS *** */ +#if defined(IP_RETOPTS) + tmp = MKT2(env, esock_atom_retopts, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_retopts, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_ROUTER_ALERT => IP_ROUTER_ALERT *** */ +#if defined(IP_ROUTER_ALERT) + tmp = MKT2(env, esock_atom_router_alert, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_router_alert, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_SENDSRCADDR => IP_SENDSRCADDR *** */ +#if defined(IP_SENDSRCADDR) + tmp = MKT2(env, esock_atom_sendsrcaddr, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_sendsrcaddr, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_TOS => IP_TOS *** */ +#if defined(IP_TOS) + tmp = MKT2(env, esock_atom_tos, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_tos, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_TRANSPARENT => IP_TRANSPARENT *** */ +#if defined(IP_TRANSPARENT) + tmp = MKT2(env, esock_atom_transparent, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_transparent, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_TTL => IP_TTL *** */ +#if defined(IP_TTL) + tmp = MKT2(env, esock_atom_ttl, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_ttl, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IP_UNBLOCK_SOURCE => IP_UNBLOCK_SOURCE *** */ +#if defined(IP_UNBLOCK_SOURCE) + tmp = MKT2(env, esock_atom_unblock_source, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_unblock_source, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + TARRAY_TOLIST(opts, env, &optsL); + + return optsL; } @@ -2789,11 +3374,249 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env) static ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env) { - ERL_NIF_TERM result; + SocketTArray opts = TARRAY_CREATE(128); + ERL_NIF_TERM tmp, optsL; - result = MKEL(env); - return result; + /* *** SOCKET_OPT_IPV6_ADDRFORM => IPV6_ADDRFORM *** */ +#if defined(IPV6_ADDRFORM) + tmp = MKT2(env, esock_atom_addrform, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_addrform, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_ADD_MEMBERSHIP => IPV6_ADD_MEMBERSHIP *** */ +#if defined(IPV6_ADD_MEMBERSHIP) + tmp = MKT2(env, esock_atom_add_membership, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_add_membership, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_AUTHHDR => IPV6_AUTHHDR *** */ +#if defined(IPV6_AUTHHDR) + tmp = MKT2(env, esock_atom_authhdr, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_authhdr, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_AUTH_LEVEL => IPV6_AUTH_LEVEL *** */ + tmp = MKT2(env, esock_atom_auth_level, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_CHECKSUM => IPV6_CHECKSUM *** */ + tmp = MKT2(env, esock_atom_checksum, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_DROP_MEMBERSHIP => IPV6_DROP_MEMBERSHIP *** */ +#if defined(IPV6_DROP_MEMBERSHIP) + tmp = MKT2(env, esock_atom_drop_membership, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_drop_membership, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_DSTOPTS => IPV6_DSTOPTS *** */ +#if defined(IPV6_DSTOPTS) + tmp = MKT2(env, esock_atom_dstopts, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_dstopts, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL => IPV6_ESP_NETWORK_LEVEL *** */ + tmp = MKT2(env, esock_atom_esp_network_level, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_ESP_TRANS_LEVEL => IPV6_ESP_TRANS_LEVEL *** */ + tmp = MKT2(env, esock_atom_esp_trans_level, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_FAITH => IPV6_FAITH *** */ + tmp = MKT2(env, esock_atom_faith, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_FLOWINFO => IPV6_FLOWINFO *** */ +#if defined(IPV6_FLOWINFO) + tmp = MKT2(env, esock_atom_flowinfo, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_flowinfo, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_HOPLIMIT => IPV6_HOPLIMIT *** */ +#if defined(IPV6_HOPLIMIT) + tmp = MKT2(env, esock_atom_hoplimit, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_hoplimit, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_HOPOPTS => IPV6_HOPOPTS *** */ +#if defined(IPV6_HOPOPTS) + tmp = MKT2(env, esock_atom_hopopts, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_hopopts, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_IPCOMP_LEVEL => IPV6_IPCOMP_LEVEL *** */ + tmp = MKT2(env, esock_atom_ipcomp_level, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_JOIN_GROUP => IPV6_JOIN_GROUP *** */ + tmp = MKT2(env, esock_atom_join_group, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_LEAVE_GROUP => IPV6_LEAVE_GROUP *** */ + tmp = MKT2(env, esock_atom_leave_group, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_MTU => IPV6_MTU *** */ +#if defined(IPV6_MTU) + tmp = MKT2(env, esock_atom_mtu, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_mtu, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_MTU_DISCOVER => IPV6_MTU_DISCOVER *** */ +#if defined(IPV6_MTU_DISCOVER) + tmp = MKT2(env, esock_atom_mtu_discover, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_mtu_discover, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_MULTICAST_HOPS => IPV6_MULTICAST_HOPS *** */ +#if defined(IPV6_MULTICAST_HOPS) + tmp = MKT2(env, esock_atom_multicast_hops, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_multicast_hops, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_MULTICAST_IF => IPV6_MULTICAST_IF *** */ +#if defined(IPV6_MULTICAST_IF) + tmp = MKT2(env, esock_atom_multicast_if, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_multicast_if, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_MULTICAST_LOOP => IPV6_MULTICAST_LOOP *** */ +#if defined(IPV6_MULTICAST_LOOP) + tmp = MKT2(env, esock_atom_multicast_loop, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_multicast_loop, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_PORTRANGE => IPV6_PORTRANGE *** */ + tmp = MKT2(env, esock_atom_portrange, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_PKTOPTIONS => IPV6_PKTOPTIONS *** */ + tmp = MKT2(env, esock_atom_pktoptions, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_RECVERR => IPV6_RECVERR *** */ +#if defined(IPV6_RECVERR) + tmp = MKT2(env, esock_atom_recverr, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_recverr, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_RECVPKTINFO => IPV6_RECVPKTINFO *** */ +#if defined(IPV6_RECVPKTINFO) + tmp = MKT2(env, esock_atom_recvpktinfo, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_recvpktinfo, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_RECVTCLASS => IPV6_RECVTCLASS *** */ + tmp = MKT2(env, esock_atom_recvtclass, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_ROUTER_ALERT => IPV6_ROUTER_ALERT *** */ +#if defined(IPV6_ROUTER_ALERT) + tmp = MKT2(env, esock_atom_router_alert, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_router_alert, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_RTHDR => IPV6_RTHDR *** */ +#if defined(IPV6_RTHDR) + tmp = MKT2(env, esock_atom_rthdr, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_rthdr, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_TCLASS => IPV6_TCLASS *** */ + tmp = MKT2(env, esock_atom_tclass, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_UNICAST_HOPS => IPV6_UNICAST_HOPS *** */ +#if defined(IPV6_UNICAST_HOPS) + tmp = MKT2(env, esock_atom_unicast_hops, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_unicast_hops, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_USE_MIN_MTU => IPV6_USE_MIN_MTU *** */ + tmp = MKT2(env, esock_atom_use_min_mtu, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_IPV6_V6ONLY => IPV6_V6ONLY *** */ +#if defined(IPV6_V6ONLY) + tmp = MKT2(env, esock_atom_v6only, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_v6only, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + TARRAY_TOLIST(opts, env, &optsL); + + return optsL; } @@ -15783,10 +16606,19 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) /* Global atom(s) */ esock_atom_accept = MKA(env, "accept"); esock_atom_acceptconn = MKA(env, "acceptconn"); + esock_atom_acceptfilter = MKA(env, "acceptfilter"); esock_atom_addr = MKA(env, "addr"); + esock_atom_addrform = MKA(env, "addrform"); + esock_atom_add_membership = MKA(env, "add_membership"); + esock_atom_add_source_membership = MKA(env, "add_source_membership"); esock_atom_any = MKA(env, "any"); + esock_atom_authhdr = MKA(env, "authhdr"); + esock_atom_auth_level = MKA(env, "auth_level"); esock_atom_bindtodevice = MKA(env, "bindtodevice"); + esock_atom_block_source = MKA(env, "block_source"); esock_atom_broadcast = MKA(env, "broadcast"); + esock_atom_busy_poll = MKA(env, "busy_poll"); + esock_atom_checksum = MKA(env, "checksum"); esock_atom_connect = MKA(env, "connect"); esock_atom_credentials = MKA(env, "credentials"); esock_atom_ctrl = MKA(env, "ctrl"); @@ -15794,63 +16626,131 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_data = MKA(env, "data"); esock_atom_debug = MKA(env, "debug"); esock_atom_dgram = MKA(env, "dgram"); + esock_atom_domain = MKA(env, "domain"); + esock_atom_dontfrag = MKA(env, "dontfrag"); + esock_atom_dontroute = MKA(env, "dontroute"); + esock_atom_drop_membership = MKA(env, "drop_membership"); + esock_atom_drop_source_membership = MKA(env, "drop_source_membership"); + esock_atom_dstopts = MKA(env, "dstpopts"); esock_atom_eor = MKA(env, "eor"); esock_atom_error = MKA(env, "error"); esock_atom_errqueue = MKA(env, "errqueue"); + esock_atom_esp_network_level = MKA(env, "esp_network_level"); + esock_atom_esp_trans_level = MKA(env, "esp_trans_level"); + esock_atom_faith = MKA(env, "faith"); esock_atom_false = MKA(env, "false"); esock_atom_family = MKA(env, "family"); esock_atom_flags = MKA(env, "flags"); esock_atom_flowinfo = MKA(env, "flowinfo"); + esock_atom_freebind = MKA(env, "freebind"); + esock_atom_hdrincl = MKA(env, "hdrincl"); + esock_atom_hoplimit = MKA(env, "hoplimit"); + esock_atom_hopopts = MKA(env, "hopopts"); esock_atom_ifindex = MKA(env, "ifindex"); esock_atom_inet = MKA(env, "inet"); esock_atom_inet6 = MKA(env, "inet6"); esock_atom_iov = MKA(env, "iov"); esock_atom_ip = MKA(env, "ip"); + esock_atom_ipcomp_level = MKA(env, "ipcomp_level"); esock_atom_ipv6 = MKA(env, "ipv6"); + esock_atom_join_group = MKA(env, "join_group"); + esock_atom_keepalive = MKA(env, "keepalive"); + esock_atom_leave_group = MKA(env, "leave_group"); esock_atom_level = MKA(env, "level"); + esock_atom_linger = MKA(env, "linger"); esock_atom_local = MKA(env, "local"); esock_atom_loopback = MKA(env, "loopback"); esock_atom_lowdelay = MKA(env, "lowdelay"); + esock_atom_mark = MKA(env, "mark"); esock_atom_mincost = MKA(env, "mincost"); + esock_atom_minttl = MKA(env, "minttl"); + esock_atom_msfilter = MKA(env, "msfilter"); + esock_atom_mtu = MKA(env, "mtu"); + esock_atom_mtu_discover = MKA(env, "mtu_discover"); + esock_atom_multicast_all = MKA(env, "multicast_all"); + esock_atom_multicast_hops = MKA(env, "multicast_hops"); + esock_atom_multicast_if = MKA(env, "multicast_if"); + esock_atom_multicast_loop = MKA(env, "multicast_loop"); + esock_atom_multicast_ttl = MKA(env, "multicast_ttl"); + esock_atom_nodefrag = MKA(env, "nodefrag"); esock_atom_not_found = MKA(env, "not_found"); esock_atom_not_owner = MKA(env, "not_owner"); esock_atom_ok = MKA(env, "ok"); esock_atom_oob = MKA(env, "oob"); + esock_atom_oobinline = MKA(env, "oobinline"); + esock_atom_options = MKA(env, "options"); esock_atom_origdstaddr = MKA(env, "origdstaddr"); + esock_atom_passcred = MKA(env, "passcred"); esock_atom_path = MKA(env, "path"); + esock_atom_peekcred = MKA(env, "peekcred"); + esock_atom_peek_off = MKA(env, "peek_off"); esock_atom_pktinfo = MKA(env, "pktinfo"); + esock_atom_pktoptions = MKA(env, "pktoptions"); esock_atom_port = MKA(env, "port"); + esock_atom_portrange = MKA(env, "portrange"); + esock_atom_priority = MKA(env, "priority"); esock_atom_protocol = MKA(env, "protocol"); esock_atom_raw = MKA(env, "raw"); + esock_atom_rcvbuf = MKA(env, "rcvbuf"); + esock_atom_rcvbufforce = MKA(env, "rcvbufforce"); + esock_atom_rcvlowat = MKA(env, "rcvlowat"); + esock_atom_rcvtimeo = MKA(env, "rcvtimeo"); esock_atom_rdm = MKA(env, "rdm"); esock_atom_recv = MKA(env, "recv"); + esock_atom_recvdstaddr = MKA(env, "recvdstaddr"); + esock_atom_recverr = MKA(env, "recverr"); esock_atom_recvfrom = MKA(env, "recvfrom"); + esock_atom_recvif = MKA(env, "recvif"); esock_atom_recvmsg = MKA(env, "recvmsg"); + esock_atom_recvopts = MKA(env, "recvopts"); + esock_atom_recvorigdstaddr = MKA(env, "recvorigdstaddr"); + esock_atom_recvpktinfo = MKA(env, "recvpktinfo"); + esock_atom_recvtclass = MKA(env, "recvtclass"); + esock_atom_recvtos = MKA(env, "recvtos"); + esock_atom_recvttl = MKA(env, "recvttl"); esock_atom_reliability = MKA(env, "reliability"); + esock_atom_retopts = MKA(env, "retopts"); + esock_atom_reuseaddr = MKA(env, "reuseaddr"); + esock_atom_reuseport = MKA(env, "reuseport"); esock_atom_rights = MKA(env, "rights"); + esock_atom_router_alert = MKA(env, "router_alert"); + esock_atom_rthdr = MKA(env, "rthdr"); + esock_atom_rxq_ovfl = MKA(env, "rxq_ovfl"); esock_atom_scope_id = MKA(env, "scope_id"); esock_atom_sctp = MKA(env, "sctp"); esock_atom_sec = MKA(env, "sec"); esock_atom_select_sent = MKA(env, "select_sent"); esock_atom_send = MKA(env, "send"); esock_atom_sendmsg = MKA(env, "sendmsg"); + esock_atom_sendsrcaddr = MKA(env, "sendsrcaddr"); esock_atom_sendto = MKA(env, "sendto"); esock_atom_seqpacket = MKA(env, "seqpacket"); + esock_atom_setfib = MKA(env, "setfib"); + esock_atom_sndbuf = MKA(env, "sndbuf"); + esock_atom_sndbufforce = MKA(env, "sndbufforce"); + esock_atom_sndlowat = MKA(env, "sndlowat"); + esock_atom_sndtimeo = MKA(env, "sndtimeo"); esock_atom_socket = MKA(env, "socket"); esock_atom_spec_dst = MKA(env, "spec_dst"); esock_atom_stream = MKA(env, "stream"); + esock_atom_tclass = MKA(env, "tclass"); esock_atom_tcp = MKA(env, "tcp"); esock_atom_throughput = MKA(env, "throughput"); esock_atom_timestamp = MKA(env, "timestamp"); esock_atom_tos = MKA(env, "tos"); + esock_atom_transparent = MKA(env, "transparent"); esock_atom_true = MKA(env, "true"); esock_atom_trunc = MKA(env, "trunc"); esock_atom_ttl = MKA(env, "ttl"); esock_atom_type = MKA(env, "type"); esock_atom_udp = MKA(env, "udp"); + esock_atom_unblock_source = MKA(env, "unblock_source"); esock_atom_undefined = MKA(env, "undefined"); + esock_atom_unicast_hops = MKA(env, "unicast_hops"); esock_atom_unknown = MKA(env, "unknown"); esock_atom_usec = MKA(env, "usec"); + esock_atom_use_min_mtu = MKA(env, "use_min_mtu"); + esock_atom_v6only = MKA(env, "v6only"); /* Global error codes */ esock_atom_eafnosupport = MKA(env, ESOCK_STR_EAFNOSUPPORT); -- cgit v1.2.3 From 46c3090432a391f27b9af8f6b19ee27310257c76 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 9 Oct 2018 11:35:38 +0200 Subject: [socket-nif] Completed (ahum) the socket option(s) part of supports "Completed" the socket options part of the supports function. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 43 ++++ erts/emulator/nifs/common/socket_nif.c | 394 ++++++++++++++++++++++++++++++++- 2 files changed, 427 insertions(+), 10 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 93722d2e98..06f677482c 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -106,25 +106,39 @@ typedef unsigned int BOOLEAN_T; extern ERL_NIF_TERM esock_atom_accept; extern ERL_NIF_TERM esock_atom_acceptconn; extern ERL_NIF_TERM esock_atom_acceptfilter; +extern ERL_NIF_TERM esock_atom_adaption_layer; extern ERL_NIF_TERM esock_atom_addr; extern ERL_NIF_TERM esock_atom_addrform; extern ERL_NIF_TERM esock_atom_add_membership; extern ERL_NIF_TERM esock_atom_add_source_membership; extern ERL_NIF_TERM esock_atom_any; +extern ERL_NIF_TERM esock_atom_associnfo; extern ERL_NIF_TERM esock_atom_authhdr; +extern ERL_NIF_TERM esock_atom_auth_active_key; +extern ERL_NIF_TERM esock_atom_auth_asconf; +extern ERL_NIF_TERM esock_atom_auth_chunk; +extern ERL_NIF_TERM esock_atom_auth_delete_key; +extern ERL_NIF_TERM esock_atom_auth_key; extern ERL_NIF_TERM esock_atom_auth_level; +extern ERL_NIF_TERM esock_atom_autoclose; extern ERL_NIF_TERM esock_atom_bindtodevice; extern ERL_NIF_TERM esock_atom_block_source; extern ERL_NIF_TERM esock_atom_broadcast; extern ERL_NIF_TERM esock_atom_busy_poll; extern ERL_NIF_TERM esock_atom_checksum; extern ERL_NIF_TERM esock_atom_connect; +extern ERL_NIF_TERM esock_atom_congestion; +extern ERL_NIF_TERM esock_atom_context; +extern ERL_NIF_TERM esock_atom_cork; extern ERL_NIF_TERM esock_atom_credentials; extern ERL_NIF_TERM esock_atom_ctrl; extern ERL_NIF_TERM esock_atom_ctrunc; extern ERL_NIF_TERM esock_atom_data; extern ERL_NIF_TERM esock_atom_debug; +extern ERL_NIF_TERM esock_atom_default_send_params; +extern ERL_NIF_TERM esock_atom_delayed_ack_time; extern ERL_NIF_TERM esock_atom_dgram; +extern ERL_NIF_TERM esock_atom_disable_fragments; extern ERL_NIF_TERM esock_atom_domain; extern ERL_NIF_TERM esock_atom_dontfrag; extern ERL_NIF_TERM esock_atom_dontroute; @@ -136,31 +150,46 @@ extern ERL_NIF_TERM esock_atom_error; extern ERL_NIF_TERM esock_atom_errqueue; extern ERL_NIF_TERM esock_atom_esp_network_level; extern ERL_NIF_TERM esock_atom_esp_trans_level; +extern ERL_NIF_TERM esock_atom_events; +extern ERL_NIF_TERM esock_atom_explicit_eor; extern ERL_NIF_TERM esock_atom_faith; extern ERL_NIF_TERM esock_atom_false; extern ERL_NIF_TERM esock_atom_family; extern ERL_NIF_TERM esock_atom_flags; extern ERL_NIF_TERM esock_atom_flowinfo; +extern ERL_NIF_TERM esock_atom_fragment_interleave; extern ERL_NIF_TERM esock_atom_freebind; +extern ERL_NIF_TERM esock_atom_get_peer_addr_info; extern ERL_NIF_TERM esock_atom_hdrincl; +extern ERL_NIF_TERM esock_atom_hmac_ident; extern ERL_NIF_TERM esock_atom_hoplimit; extern ERL_NIF_TERM esock_atom_hopopts; extern ERL_NIF_TERM esock_atom_ifindex; extern ERL_NIF_TERM esock_atom_inet; extern ERL_NIF_TERM esock_atom_inet6; +extern ERL_NIF_TERM esock_atom_info; +extern ERL_NIF_TERM esock_atom_initmsg; extern ERL_NIF_TERM esock_atom_iov; extern ERL_NIF_TERM esock_atom_ip; extern ERL_NIF_TERM esock_atom_ipcomp_level; extern ERL_NIF_TERM esock_atom_ipv6; +extern ERL_NIF_TERM esock_atom_i_want_mapped_v4_addr; extern ERL_NIF_TERM esock_atom_join_group; extern ERL_NIF_TERM esock_atom_keepalive; +extern ERL_NIF_TERM esock_atom_keepcnt; +extern ERL_NIF_TERM esock_atom_keepidle; +extern ERL_NIF_TERM esock_atom_keepintvl; extern ERL_NIF_TERM esock_atom_leave_group; extern ERL_NIF_TERM esock_atom_level; extern ERL_NIF_TERM esock_atom_linger; extern ERL_NIF_TERM esock_atom_local; +extern ERL_NIF_TERM esock_atom_local_auth_chunks; extern ERL_NIF_TERM esock_atom_loopback; extern ERL_NIF_TERM esock_atom_lowdelay; extern ERL_NIF_TERM esock_atom_mark; +extern ERL_NIF_TERM esock_atom_maxburst; +extern ERL_NIF_TERM esock_atom_maxseg; +extern ERL_NIF_TERM esock_atom_md5sig; extern ERL_NIF_TERM esock_atom_mincost; extern ERL_NIF_TERM esock_atom_minttl; extern ERL_NIF_TERM esock_atom_msfilter; @@ -171,7 +200,10 @@ extern ERL_NIF_TERM esock_atom_multicast_hops; extern ERL_NIF_TERM esock_atom_multicast_if; extern ERL_NIF_TERM esock_atom_multicast_loop; extern ERL_NIF_TERM esock_atom_multicast_ttl; +extern ERL_NIF_TERM esock_atom_nodelay; extern ERL_NIF_TERM esock_atom_nodefrag; +extern ERL_NIF_TERM esock_atom_noopt; +extern ERL_NIF_TERM esock_atom_nopush; extern ERL_NIF_TERM esock_atom_not_found; extern ERL_NIF_TERM esock_atom_not_owner; extern ERL_NIF_TERM esock_atom_ok; @@ -179,14 +211,18 @@ extern ERL_NIF_TERM esock_atom_oob; extern ERL_NIF_TERM esock_atom_oobinline; extern ERL_NIF_TERM esock_atom_options; extern ERL_NIF_TERM esock_atom_origdstaddr; +extern ERL_NIF_TERM esock_atom_partial_delivery_point; extern ERL_NIF_TERM esock_atom_passcred; extern ERL_NIF_TERM esock_atom_path; extern ERL_NIF_TERM esock_atom_peekcred; extern ERL_NIF_TERM esock_atom_peek_off; +extern ERL_NIF_TERM esock_atom_peer_addr_params; +extern ERL_NIF_TERM esock_atom_peer_auth_chunks; extern ERL_NIF_TERM esock_atom_pktinfo; extern ERL_NIF_TERM esock_atom_pktoptions; extern ERL_NIF_TERM esock_atom_port; extern ERL_NIF_TERM esock_atom_portrange; +extern ERL_NIF_TERM esock_atom_primary_addr; extern ERL_NIF_TERM esock_atom_priority; extern ERL_NIF_TERM esock_atom_protocol; extern ERL_NIF_TERM esock_atom_raw; @@ -208,12 +244,14 @@ extern ERL_NIF_TERM esock_atom_recvtclass; extern ERL_NIF_TERM esock_atom_recvtos; extern ERL_NIF_TERM esock_atom_recvttl; extern ERL_NIF_TERM esock_atom_reliability; +extern ERL_NIF_TERM esock_atom_reset_streams; extern ERL_NIF_TERM esock_atom_retopts; extern ERL_NIF_TERM esock_atom_reuseaddr; extern ERL_NIF_TERM esock_atom_reuseport; extern ERL_NIF_TERM esock_atom_rights; extern ERL_NIF_TERM esock_atom_router_alert; extern ERL_NIF_TERM esock_atom_rthdr; +extern ERL_NIF_TERM esock_atom_rtoinfo; extern ERL_NIF_TERM esock_atom_rxq_ovfl; extern ERL_NIF_TERM esock_atom_scope_id; extern ERL_NIF_TERM esock_atom_sctp; @@ -225,13 +263,16 @@ extern ERL_NIF_TERM esock_atom_sendsrcaddr; extern ERL_NIF_TERM esock_atom_sendto; extern ERL_NIF_TERM esock_atom_seqpacket; extern ERL_NIF_TERM esock_atom_setfib; +extern ERL_NIF_TERM esock_atom_set_peer_primary_addr; extern ERL_NIF_TERM esock_atom_sndbuf; extern ERL_NIF_TERM esock_atom_sndbufforce; extern ERL_NIF_TERM esock_atom_sndlowat; extern ERL_NIF_TERM esock_atom_sndtimeo; extern ERL_NIF_TERM esock_atom_socket; extern ERL_NIF_TERM esock_atom_spec_dst; +extern ERL_NIF_TERM esock_atom_status; extern ERL_NIF_TERM esock_atom_stream; +extern ERL_NIF_TERM esock_atom_syncnt; extern ERL_NIF_TERM esock_atom_tclass; extern ERL_NIF_TERM esock_atom_tcp; extern ERL_NIF_TERM esock_atom_throughput; @@ -248,6 +289,8 @@ extern ERL_NIF_TERM esock_atom_undefined; extern ERL_NIF_TERM esock_atom_unicast_hops; extern ERL_NIF_TERM esock_atom_unknown; extern ERL_NIF_TERM esock_atom_usec; +extern ERL_NIF_TERM esock_atom_user_timeout; +extern ERL_NIF_TERM esock_atom_use_ext_recvinfo; extern ERL_NIF_TERM esock_atom_use_min_mtu; extern ERL_NIF_TERM esock_atom_v6only; diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 359cc7db62..a59b9ec772 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -2388,25 +2388,39 @@ static char str_exsend[] = "exsend"; // failed send ERL_NIF_TERM esock_atom_accept; ERL_NIF_TERM esock_atom_acceptconn; ERL_NIF_TERM esock_atom_acceptfilter; +ERL_NIF_TERM esock_atom_adaption_layer; ERL_NIF_TERM esock_atom_addr; ERL_NIF_TERM esock_atom_addrform; ERL_NIF_TERM esock_atom_add_membership; ERL_NIF_TERM esock_atom_add_source_membership; ERL_NIF_TERM esock_atom_any; +ERL_NIF_TERM esock_atom_associnfo; ERL_NIF_TERM esock_atom_authhdr; +ERL_NIF_TERM esock_atom_auth_active_key; +ERL_NIF_TERM esock_atom_auth_asconf; +ERL_NIF_TERM esock_atom_auth_chunk; +ERL_NIF_TERM esock_atom_auth_delete_key; +ERL_NIF_TERM esock_atom_auth_key; ERL_NIF_TERM esock_atom_auth_level; +ERL_NIF_TERM esock_atom_autoclose; ERL_NIF_TERM esock_atom_bindtodevice; ERL_NIF_TERM esock_atom_block_source; ERL_NIF_TERM esock_atom_broadcast; ERL_NIF_TERM esock_atom_busy_poll; ERL_NIF_TERM esock_atom_checksum; ERL_NIF_TERM esock_atom_connect; +ERL_NIF_TERM esock_atom_congestion; +ERL_NIF_TERM esock_atom_context; +ERL_NIF_TERM esock_atom_cork; ERL_NIF_TERM esock_atom_credentials; ERL_NIF_TERM esock_atom_ctrl; ERL_NIF_TERM esock_atom_ctrunc; ERL_NIF_TERM esock_atom_data; ERL_NIF_TERM esock_atom_debug; +ERL_NIF_TERM esock_atom_default_send_params; +ERL_NIF_TERM esock_atom_delayed_ack_time; ERL_NIF_TERM esock_atom_dgram; +ERL_NIF_TERM esock_atom_disable_fragments; ERL_NIF_TERM esock_atom_domain; ERL_NIF_TERM esock_atom_dontfrag; ERL_NIF_TERM esock_atom_dontroute; @@ -2418,31 +2432,46 @@ ERL_NIF_TERM esock_atom_error; ERL_NIF_TERM esock_atom_errqueue; ERL_NIF_TERM esock_atom_esp_network_level; ERL_NIF_TERM esock_atom_esp_trans_level; +ERL_NIF_TERM esock_atom_events; +ERL_NIF_TERM esock_atom_explicit_eor; ERL_NIF_TERM esock_atom_faith; ERL_NIF_TERM esock_atom_false; ERL_NIF_TERM esock_atom_family; ERL_NIF_TERM esock_atom_flags; ERL_NIF_TERM esock_atom_flowinfo; +ERL_NIF_TERM esock_atom_fragment_interleave; ERL_NIF_TERM esock_atom_freebind; +ERL_NIF_TERM esock_atom_get_peer_addr_info; ERL_NIF_TERM esock_atom_hdrincl; +ERL_NIF_TERM esock_atom_hmac_ident; ERL_NIF_TERM esock_atom_hoplimit; ERL_NIF_TERM esock_atom_hopopts; ERL_NIF_TERM esock_atom_ifindex; ERL_NIF_TERM esock_atom_inet; ERL_NIF_TERM esock_atom_inet6; +ERL_NIF_TERM esock_atom_info; +ERL_NIF_TERM esock_atom_initmsg; ERL_NIF_TERM esock_atom_iov; ERL_NIF_TERM esock_atom_ip; ERL_NIF_TERM esock_atom_ipcomp_level; ERL_NIF_TERM esock_atom_ipv6; +ERL_NIF_TERM esock_atom_i_want_mapped_v4_addr; ERL_NIF_TERM esock_atom_join_group; ERL_NIF_TERM esock_atom_keepalive; +ERL_NIF_TERM esock_atom_keepcnt; +ERL_NIF_TERM esock_atom_keepidle; +ERL_NIF_TERM esock_atom_keepintvl; ERL_NIF_TERM esock_atom_leave_group; ERL_NIF_TERM esock_atom_level; ERL_NIF_TERM esock_atom_linger; ERL_NIF_TERM esock_atom_local; +ERL_NIF_TERM esock_atom_local_auth_chunks; ERL_NIF_TERM esock_atom_loopback; ERL_NIF_TERM esock_atom_lowdelay; ERL_NIF_TERM esock_atom_mark; +ERL_NIF_TERM esock_atom_maxburst; +ERL_NIF_TERM esock_atom_maxseg; +ERL_NIF_TERM esock_atom_md5sig; ERL_NIF_TERM esock_atom_mincost; ERL_NIF_TERM esock_atom_minttl; ERL_NIF_TERM esock_atom_msfilter; @@ -2453,7 +2482,10 @@ ERL_NIF_TERM esock_atom_multicast_hops; ERL_NIF_TERM esock_atom_multicast_if; ERL_NIF_TERM esock_atom_multicast_loop; ERL_NIF_TERM esock_atom_multicast_ttl; +ERL_NIF_TERM esock_atom_nodelay; ERL_NIF_TERM esock_atom_nodefrag; +ERL_NIF_TERM esock_atom_noopt; +ERL_NIF_TERM esock_atom_nopush; ERL_NIF_TERM esock_atom_not_found; ERL_NIF_TERM esock_atom_not_owner; ERL_NIF_TERM esock_atom_ok; @@ -2461,14 +2493,18 @@ ERL_NIF_TERM esock_atom_oob; ERL_NIF_TERM esock_atom_oobinline; ERL_NIF_TERM esock_atom_options; ERL_NIF_TERM esock_atom_origdstaddr; +ERL_NIF_TERM esock_atom_partial_delivery_point; ERL_NIF_TERM esock_atom_passcred; ERL_NIF_TERM esock_atom_path; ERL_NIF_TERM esock_atom_peekcred; ERL_NIF_TERM esock_atom_peek_off; +ERL_NIF_TERM esock_atom_peer_addr_params; +ERL_NIF_TERM esock_atom_peer_auth_chunks; ERL_NIF_TERM esock_atom_pktinfo; ERL_NIF_TERM esock_atom_pktoptions; ERL_NIF_TERM esock_atom_port; ERL_NIF_TERM esock_atom_portrange; +ERL_NIF_TERM esock_atom_primary_addr; ERL_NIF_TERM esock_atom_priority; ERL_NIF_TERM esock_atom_protocol; ERL_NIF_TERM esock_atom_raw; @@ -2490,12 +2526,14 @@ ERL_NIF_TERM esock_atom_recvtclass; ERL_NIF_TERM esock_atom_recvtos; ERL_NIF_TERM esock_atom_recvttl; ERL_NIF_TERM esock_atom_reliability; +ERL_NIF_TERM esock_atom_reset_streams; ERL_NIF_TERM esock_atom_retopts; ERL_NIF_TERM esock_atom_reuseaddr; ERL_NIF_TERM esock_atom_reuseport; ERL_NIF_TERM esock_atom_rights; ERL_NIF_TERM esock_atom_router_alert; ERL_NIF_TERM esock_atom_rthdr; +ERL_NIF_TERM esock_atom_rtoinfo; ERL_NIF_TERM esock_atom_rxq_ovfl; ERL_NIF_TERM esock_atom_scope_id; ERL_NIF_TERM esock_atom_sctp; @@ -2507,13 +2545,16 @@ ERL_NIF_TERM esock_atom_sendsrcaddr; ERL_NIF_TERM esock_atom_sendto; ERL_NIF_TERM esock_atom_seqpacket; ERL_NIF_TERM esock_atom_setfib; +ERL_NIF_TERM esock_atom_set_peer_primary_addr; ERL_NIF_TERM esock_atom_socket; ERL_NIF_TERM esock_atom_sndbuf; ERL_NIF_TERM esock_atom_sndbufforce; ERL_NIF_TERM esock_atom_sndlowat; ERL_NIF_TERM esock_atom_sndtimeo; ERL_NIF_TERM esock_atom_spec_dst; +ERL_NIF_TERM esock_atom_status; ERL_NIF_TERM esock_atom_stream; +ERL_NIF_TERM esock_atom_syncnt; ERL_NIF_TERM esock_atom_tclass; ERL_NIF_TERM esock_atom_tcp; ERL_NIF_TERM esock_atom_throughput; @@ -2530,6 +2571,8 @@ ERL_NIF_TERM esock_atom_undefined; ERL_NIF_TERM esock_atom_unicast_hops; ERL_NIF_TERM esock_atom_unknown; ERL_NIF_TERM esock_atom_usec; +ERL_NIF_TERM esock_atom_user_timeout; +ERL_NIF_TERM esock_atom_use_ext_recvinfo; ERL_NIF_TERM esock_atom_use_min_mtu; ERL_NIF_TERM esock_atom_v6only; @@ -3624,11 +3667,94 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env) static ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env) { - ERL_NIF_TERM result; + SocketTArray opts = TARRAY_CREATE(32); + ERL_NIF_TERM tmp, optsL; - result = MKEL(env); - return result; + /* *** SOCKET_OPT_TCP_CONGESTION => TCP_CONGESTION *** */ +#if defined(TCP_CONGESTION) + tmp = MKT2(env, esock_atom_congestion, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_congestion, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_CORK => TCP_CORK *** */ +#if defined(TCP_CORK) + tmp = MKT2(env, esock_atom_cork, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_cork, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_INFO => TCP_INFO *** */ + tmp = MKT2(env, esock_atom_info, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_KEEPCNT => TCP_KEEPCNT *** */ + tmp = MKT2(env, esock_atom_keepcnt, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_KEEPIDLE => TCP_KEEPIDLE *** */ + tmp = MKT2(env, esock_atom_keepidle, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_KEEPINTVL => TCP_KEEPINTVL *** */ + tmp = MKT2(env, esock_atom_keepintvl, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_MAXSEG => TCP_MAXSEG *** */ +#if defined(TCP_) + tmp = MKT2(env, esock_atom_maxseg, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_maxseg, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_MD5SIG => TCP_MD5SIG *** */ + tmp = MKT2(env, esock_atom_md5sig, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_NODELAY => TCP_NODELAY *** */ +#if defined(TCP_) + tmp = MKT2(env, esock_atom_nodelay, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_nodelay, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_NOOPT => TCP_NOOPT *** */ + tmp = MKT2(env, esock_atom_noopt, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_NOPUSH => TCP_NOPUSH *** */ + tmp = MKT2(env, esock_atom_nopush, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_SYNCNT => TCP_SYNCNT *** */ + tmp = MKT2(env, esock_atom_syncnt, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_TCP_USER_TIMEOUT => TCP_USER_TIMEOUT *** */ + tmp = MKT2(env, esock_atom_user_timeout, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + TARRAY_TOLIST(opts, env, &optsL); + + return optsL; } @@ -3636,11 +3762,22 @@ ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env) static ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env) { - ERL_NIF_TERM result; + SocketTArray opts = TARRAY_CREATE(8); + ERL_NIF_TERM tmp, optsL; - result = MKEL(env); - return result; + /* *** SOCKET_OPT_UDP_CORK => UDP_CORK *** */ +#if defined(UDP_CORK) + tmp = MKT2(env, esock_atom_cork, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_cork, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + TARRAY_TOLIST(opts, env, &optsL); + + return optsL; } @@ -3648,11 +3785,205 @@ ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env) static ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env) { - ERL_NIF_TERM result; + SocketTArray opts = TARRAY_CREATE(64); + ERL_NIF_TERM tmp, optsL; - result = MKEL(env); - return result; + /* *** SOCKET_OPT_SCTP_ADAPTION_LAYER => SCTP_ADAPTION_LAYER *** */ + tmp = MKT2(env, esock_atom_adaption_layer, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_ASSOCINFO => SCTP_ASSOCINFO *** */ +#if defined(SCTP_ASSOCINFO) + tmp = MKT2(env, esock_atom_associnfo, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_associnfo, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_AUTH_ACTIVE_KEY => SCTP_AUTH_ACTIVE_KEY *** */ + tmp = MKT2(env, esock_atom_auth_active_key, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_AUTH_ASCONF => SCTP_AUTH_ASCONF *** */ + tmp = MKT2(env, esock_atom_auth_asconf, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_AUTH_CHUNK => SCTP_AUTH_CHUNK *** */ + tmp = MKT2(env, esock_atom_auth_chunk, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_AUTH_DELETE_KEY => SCTP_AUTH_DELETE_KEY *** */ + tmp = MKT2(env, esock_atom_auth_delete_key, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_AUTH_KEY => SCTP_AUTH_KEY *** */ + tmp = MKT2(env, esock_atom_auth_key, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_AUTOCLOSE => SCTP_AUTOCLOSE *** */ +#if defined(SCTP_AUTOCLOSE) + tmp = MKT2(env, esock_atom_autoclose, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_autoclose, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_CONTEXT => SCTP_CONTEXT *** */ + tmp = MKT2(env, esock_atom_context, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_DEFAULT_SEND_PARAMS => SCTP_DEFAULT_SEND_PARAMS *** */ + tmp = MKT2(env, esock_atom_default_send_params, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_DELAYED_ACK_TIME => SCTP_DELAYED_ACK_TIME *** */ + tmp = MKT2(env, esock_atom_delayed_ack_time, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_DISABLE_FRAGMENTS => SCTP_DISABLE_FRAGMENTS *** */ +#if defined(SCTP_DISABLE_FRAGMENTS) + tmp = MKT2(env, esock_atom_disable_fragments, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_disable_fragments, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_HMAC_IDENT => SCTP_HMAC_IDENT *** */ + tmp = MKT2(env, esock_atom_hmac_ident, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_EVENTS => SCTP_EVENTS *** */ +#if defined(SCTP_EVENTS) + tmp = MKT2(env, esock_atom_events, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_events, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_EXPLICIT_EOR => SCTP_EXPLICIT_EOR *** */ + tmp = MKT2(env, esock_atom_explicit_eor, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_FRAGMENT_INTERLEAVE => SCTP_FRAGMENT_INTERLEAVE *** */ + tmp = MKT2(env, esock_atom_fragment_interleave, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_GET_PEER_ADDR_INFO => SCTP_GET_PEER_ADDR_INFO *** */ + tmp = MKT2(env, esock_atom_get_peer_addr_info, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_INITMSG => SCTP_INITMSG *** */ +#if defined(SCTP_INITMSG) + tmp = MKT2(env, esock_atom_initmsg, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_initmsg, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR => SCTP_I_WANT_MAPPED_V4_ADDR *** */ + tmp = MKT2(env, esock_atom_i_want_mapped_v4_addr, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS => SCTP_LOCAL_AUTH_CHUNKS *** */ + tmp = MKT2(env, esock_atom_local_auth_chunks, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_MAXSEG => SCTP_MAXSEG *** */ +#if defined(SCTP_MAXSEG) + tmp = MKT2(env, esock_atom_maxseg, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_maxseg, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_MAXBURST => SCTP_MAXBURST *** */ + tmp = MKT2(env, esock_atom_maxburst, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_NODELAY => SCTP_NODELAY *** */ +#if defined(SCTP_NODELAY) + tmp = MKT2(env, esock_atom_nodelay, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_nodelay, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_PARTIAL_DELIVERY_POINT => SCTP_PARTIAL_DELIVERY_POINT *** */ + tmp = MKT2(env, esock_atom_partial_delivery_point, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_PEER_ADDR_PARAMS => SCTP_PEER_ADDR_PARAMS *** */ + tmp = MKT2(env, esock_atom_peer_addr_params, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_PEER_AUTH_CHUNKS => SCTP_PEER_AUTH_CHUNKS *** */ + tmp = MKT2(env, esock_atom_peer_auth_chunks, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_PRIMARY_ADDR => SCTP_PRIMARY_ADDR *** */ + tmp = MKT2(env, esock_atom_primary_addr, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_RESET_STREAMS => SCTP_RESET_STREAMS *** */ + tmp = MKT2(env, esock_atom_reset_streams, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_RTOINFO => SCTP_RTOINFO *** */ +#if defined(SCTP_RTOINFO) + tmp = MKT2(env, esock_atom_rtoinfo, esock_atom_true); +#else + tmp = MKT2(env, esock_atom_rtoinfo, esock_atom_false); +#endif + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_SET_PEER_PRIMARY_ADDR => SCTP_SET_PEER_PRIMARY_ADDR *** */ + tmp = MKT2(env, esock_atom_set_peer_primary_addr, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_STATUS => SCTP_STATUS *** */ + tmp = MKT2(env, esock_atom_status, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + /* *** SOCKET_OPT_SCTP_USE_EXT_RECVINFO => SCTP_USE_EXT_RECVINFO *** */ + tmp = MKT2(env, esock_atom_use_ext_recvinfo, esock_atom_false); + TARRAY_ADD(opts, tmp); + + + TARRAY_TOLIST(opts, env, &optsL); + + return optsL; } @@ -16607,25 +16938,39 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_accept = MKA(env, "accept"); esock_atom_acceptconn = MKA(env, "acceptconn"); esock_atom_acceptfilter = MKA(env, "acceptfilter"); + esock_atom_adaption_layer = MKA(env, "adaption_layer"); esock_atom_addr = MKA(env, "addr"); esock_atom_addrform = MKA(env, "addrform"); esock_atom_add_membership = MKA(env, "add_membership"); esock_atom_add_source_membership = MKA(env, "add_source_membership"); esock_atom_any = MKA(env, "any"); + esock_atom_associnfo = MKA(env, "associnfo"); esock_atom_authhdr = MKA(env, "authhdr"); + esock_atom_auth_active_key = MKA(env, "auth_active_key"); + esock_atom_auth_asconf = MKA(env, "auth_asconf"); + esock_atom_auth_chunk = MKA(env, "auth_chunk"); + esock_atom_auth_delete_key = MKA(env, "auth_delete_key"); + esock_atom_auth_key = MKA(env, "auth_key"); esock_atom_auth_level = MKA(env, "auth_level"); + esock_atom_autoclose = MKA(env, "autoclose"); esock_atom_bindtodevice = MKA(env, "bindtodevice"); esock_atom_block_source = MKA(env, "block_source"); esock_atom_broadcast = MKA(env, "broadcast"); esock_atom_busy_poll = MKA(env, "busy_poll"); esock_atom_checksum = MKA(env, "checksum"); esock_atom_connect = MKA(env, "connect"); + esock_atom_congestion = MKA(env, "congestion"); + esock_atom_context = MKA(env, "context"); + esock_atom_cork = MKA(env, "cork"); esock_atom_credentials = MKA(env, "credentials"); esock_atom_ctrl = MKA(env, "ctrl"); esock_atom_ctrunc = MKA(env, "ctrunc"); esock_atom_data = MKA(env, "data"); esock_atom_debug = MKA(env, "debug"); + esock_atom_default_send_params = MKA(env, "default_send_params"); + esock_atom_delayed_ack_time = MKA(env, "delayed_ack_time"); esock_atom_dgram = MKA(env, "dgram"); + esock_atom_disable_fragments = MKA(env, "disable_fragments"); esock_atom_domain = MKA(env, "domain"); esock_atom_dontfrag = MKA(env, "dontfrag"); esock_atom_dontroute = MKA(env, "dontroute"); @@ -16637,31 +16982,46 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_errqueue = MKA(env, "errqueue"); esock_atom_esp_network_level = MKA(env, "esp_network_level"); esock_atom_esp_trans_level = MKA(env, "esp_trans_level"); + esock_atom_events = MKA(env, "events"); + esock_atom_explicit_eor = MKA(env, "explicit_eor"); esock_atom_faith = MKA(env, "faith"); esock_atom_false = MKA(env, "false"); esock_atom_family = MKA(env, "family"); esock_atom_flags = MKA(env, "flags"); esock_atom_flowinfo = MKA(env, "flowinfo"); + esock_atom_fragment_interleave = MKA(env, "fragment_interleave"); esock_atom_freebind = MKA(env, "freebind"); + esock_atom_get_peer_addr_info = MKA(env, "get_peer_addr_info"); esock_atom_hdrincl = MKA(env, "hdrincl"); + esock_atom_hmac_ident = MKA(env, "hmac_ident"); esock_atom_hoplimit = MKA(env, "hoplimit"); esock_atom_hopopts = MKA(env, "hopopts"); esock_atom_ifindex = MKA(env, "ifindex"); esock_atom_inet = MKA(env, "inet"); esock_atom_inet6 = MKA(env, "inet6"); + esock_atom_info = MKA(env, "info"); + esock_atom_initmsg = MKA(env, "initmsg"); esock_atom_iov = MKA(env, "iov"); esock_atom_ip = MKA(env, "ip"); esock_atom_ipcomp_level = MKA(env, "ipcomp_level"); esock_atom_ipv6 = MKA(env, "ipv6"); + esock_atom_i_want_mapped_v4_addr = MKA(env, "i_want_mapped_v4_addr"); esock_atom_join_group = MKA(env, "join_group"); esock_atom_keepalive = MKA(env, "keepalive"); + esock_atom_keepcnt = MKA(env, "keepcnt"); + esock_atom_keepidle = MKA(env, "keepidle"); + esock_atom_keepintvl = MKA(env, "keepintvl"); esock_atom_leave_group = MKA(env, "leave_group"); esock_atom_level = MKA(env, "level"); esock_atom_linger = MKA(env, "linger"); esock_atom_local = MKA(env, "local"); + esock_atom_local_auth_chunks = MKA(env, "local_auth_chunks"); esock_atom_loopback = MKA(env, "loopback"); esock_atom_lowdelay = MKA(env, "lowdelay"); esock_atom_mark = MKA(env, "mark"); + esock_atom_maxburst = MKA(env, "maxburst"); + esock_atom_maxseg = MKA(env, "maxseg"); + esock_atom_md5sig = MKA(env, "md5sig"); esock_atom_mincost = MKA(env, "mincost"); esock_atom_minttl = MKA(env, "minttl"); esock_atom_msfilter = MKA(env, "msfilter"); @@ -16673,6 +17033,9 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_multicast_loop = MKA(env, "multicast_loop"); esock_atom_multicast_ttl = MKA(env, "multicast_ttl"); esock_atom_nodefrag = MKA(env, "nodefrag"); + esock_atom_nodelay = MKA(env, "nodelay"); + esock_atom_noopt = MKA(env, "noopt"); + esock_atom_nopush = MKA(env, "nopush"); esock_atom_not_found = MKA(env, "not_found"); esock_atom_not_owner = MKA(env, "not_owner"); esock_atom_ok = MKA(env, "ok"); @@ -16680,14 +17043,18 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_oobinline = MKA(env, "oobinline"); esock_atom_options = MKA(env, "options"); esock_atom_origdstaddr = MKA(env, "origdstaddr"); + esock_atom_partial_delivery_point = MKA(env, "partial_delivery_point"); esock_atom_passcred = MKA(env, "passcred"); esock_atom_path = MKA(env, "path"); esock_atom_peekcred = MKA(env, "peekcred"); esock_atom_peek_off = MKA(env, "peek_off"); + esock_atom_peer_addr_params = MKA(env, "peer_addr_params"); + esock_atom_peer_auth_chunks = MKA(env, "peer_auth_chunks"); esock_atom_pktinfo = MKA(env, "pktinfo"); esock_atom_pktoptions = MKA(env, "pktoptions"); esock_atom_port = MKA(env, "port"); esock_atom_portrange = MKA(env, "portrange"); + esock_atom_primary_addr = MKA(env, "primary_addr"); esock_atom_priority = MKA(env, "priority"); esock_atom_protocol = MKA(env, "protocol"); esock_atom_raw = MKA(env, "raw"); @@ -16709,12 +17076,14 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_recvtos = MKA(env, "recvtos"); esock_atom_recvttl = MKA(env, "recvttl"); esock_atom_reliability = MKA(env, "reliability"); - esock_atom_retopts = MKA(env, "retopts"); + esock_atom_reset_streams = MKA(env, "reset_streams"); + esock_atom_retopts = MKA(env, "retopts"); esock_atom_reuseaddr = MKA(env, "reuseaddr"); esock_atom_reuseport = MKA(env, "reuseport"); esock_atom_rights = MKA(env, "rights"); esock_atom_router_alert = MKA(env, "router_alert"); esock_atom_rthdr = MKA(env, "rthdr"); + esock_atom_rtoinfo = MKA(env, "rtoinfo"); esock_atom_rxq_ovfl = MKA(env, "rxq_ovfl"); esock_atom_scope_id = MKA(env, "scope_id"); esock_atom_sctp = MKA(env, "sctp"); @@ -16726,13 +17095,16 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_sendto = MKA(env, "sendto"); esock_atom_seqpacket = MKA(env, "seqpacket"); esock_atom_setfib = MKA(env, "setfib"); + esock_atom_set_peer_primary_addr = MKA(env, "set_peer_primary_addr"); esock_atom_sndbuf = MKA(env, "sndbuf"); esock_atom_sndbufforce = MKA(env, "sndbufforce"); esock_atom_sndlowat = MKA(env, "sndlowat"); esock_atom_sndtimeo = MKA(env, "sndtimeo"); esock_atom_socket = MKA(env, "socket"); esock_atom_spec_dst = MKA(env, "spec_dst"); + esock_atom_status = MKA(env, "status"); esock_atom_stream = MKA(env, "stream"); + esock_atom_syncnt = MKA(env, "syncnt"); esock_atom_tclass = MKA(env, "tclass"); esock_atom_tcp = MKA(env, "tcp"); esock_atom_throughput = MKA(env, "throughput"); @@ -16749,6 +17121,8 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_unicast_hops = MKA(env, "unicast_hops"); esock_atom_unknown = MKA(env, "unknown"); esock_atom_usec = MKA(env, "usec"); + esock_atom_user_timeout = MKA(env, "user_timeout"); + esock_atom_use_ext_recvinfo = MKA(env, "use_ext_recvinfo"); esock_atom_use_min_mtu = MKA(env, "use_min_mtu"); esock_atom_v6only = MKA(env, "v6only"); -- cgit v1.2.3 From 789d5cefd0f710fd83dec27ad2239ea299737a49 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 9 Oct 2018 12:23:59 +0200 Subject: [socket-nif] The supports function now also handles sctp and ipv6 The supports function now also handles testing for SCTP and IPv6 support. Basic test at the moment, but better then nothing. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 475 +++++++++++++++++++-------------- 1 file changed, 278 insertions(+), 197 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index a59b9ec772..4544604f28 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -314,6 +314,10 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #include "socket_int.h" #include "socket_util.h" +#if defined(SOL_IPV6) || defined(IPPROTO_IPV6) +#define HAVE_IPV6 +#endif + /* All platforms fail on malloc errors. */ #define FATAL_MALLOC @@ -604,6 +608,8 @@ typedef union { #define SOCKET_SUPPORTS_OPTIONS 0x0001 +#define SOCKET_SUPPORTS_SCTP 0x0002 +#define SOCKET_SUPPORTS_IPV6 0x0003 @@ -942,6 +948,8 @@ static ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env); static ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env); static ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env); static ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_sctp(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env); static ERL_NIF_TERM nopen(ErlNifEnv* env, int domain, @@ -1322,7 +1330,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, /* *** Handling set of socket options for level = ipv6 *** */ -#if defined(SOL_IPV6) +#if defined(HAVE_IPV6) static ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, @@ -1430,7 +1438,7 @@ static ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, int opt); #endif -#endif // defined(SOL_IPV6) +#endif // defined(HAVE_IPV6) static ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, SocketDescriptor* descP, int eOpt, @@ -1736,7 +1744,7 @@ static ERL_NIF_TERM ngetopt_lvl_ip_transparent(ErlNifEnv* env, static ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, SocketDescriptor* descP); #endif -#if defined(SOL_IPV6) +#if defined(HAVE_IPV6) static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, SocketDescriptor* descP, int eOpt); @@ -1805,7 +1813,7 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP); #endif -#endif // defined(SOL_IPV6) +#endif // defined(HAVE_IPV6) static ERL_NIF_TERM ngetopt_lvl_tcp(ErlNifEnv* env, SocketDescriptor* descP, @@ -2060,7 +2068,7 @@ static char* encode_cmsghdr_data_ip(ErlNifEnv* env, size_t dataPos, size_t dataLen, ERL_NIF_TERM* eCMsgHdrData); -#if defined(SOL_IPV6) +#if defined(HAVE_IPV6) static char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, ERL_NIF_TERM ctrlBuf, int type, @@ -2818,6 +2826,14 @@ ERL_NIF_TERM nsupports(ErlNifEnv* env, int key) result = nsupports_options(env); break; + case SOCKET_SUPPORTS_SCTP: + result = nsupports_sctp(env); + break; + + case SOCKET_SUPPORTS_IPV6: + result = nsupports_ipv6(env); + break; + default: result = esock_atom_false; break; @@ -3988,6 +4004,39 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env) +static +ERL_NIF_TERM nsupports_sctp(ErlNifEnv* env) +{ + ERL_NIF_TERM supports; + +#if defined(HAVE_SCTP) + supports = esock_atom_true; +#else + supports = esock_atom_false; +#endif + + return supports; +} + + + +static +ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env) +{ + ERL_NIF_TERM supports; + + /* Is this (test) really sufficient for testing if we support IPv6? */ +#if defined(HAVE_IPV6) + supports = esock_atom_true; +#else + supports = esock_atom_false; +#endif + + return supports; +} + + + /* ---------------------------------------------------------------------- * nif_open * @@ -6654,8 +6703,12 @@ ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, result = nsetopt_lvl_ip(env, descP, eOpt, eVal); break; +#if defined(HAVE_IPV6) #if defined(SOL_IPV6) case SOL_IPV6: +#else + case IPPROTO_IPV6: +#endif result = nsetopt_lvl_ipv6(env, descP, eOpt, eVal); break; #endif @@ -8141,7 +8194,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_update_source(ErlNifEnv* env, /* nsetopt_lvl_ipv6 - Level *IPv6* option(s) */ -#if defined(SOL_IPV6) +#if defined(HAVE_IPV6) static ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, SocketDescriptor* descP, @@ -8612,7 +8665,7 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, -#endif // defined(SOL_IPV6) +#endif // defined(HAVE_IPV6) @@ -9414,10 +9467,14 @@ BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, result = TRUE; break; -#if defined(SOL_IPV6) +#if defined(HAVE_IPV6) case SOCKET_OPT_LEVEL_IPV6: *isOTP = FALSE; +#if defined(SOL_IPV6) *level = SOL_IPV6; +#else + *level = IPPROTO_IPV6; +#endif result = TRUE; break; #endif @@ -10063,8 +10120,12 @@ ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, result = ngetopt_lvl_ip(env, descP, eOpt); break; +#if defined(HAVE_IPV6) #if defined(SOL_IPV6) case SOL_IPV6: +#else + case IPPROTO_IPV6: +#endif result = ngetopt_lvl_ipv6(env, descP, eOpt); break; #endif @@ -11267,7 +11328,7 @@ ERL_NIF_TERM ngetopt_lvl_ip_ttl(ErlNifEnv* env, /* ngetopt_lvl_ipv6 - Level *IPv6* option(s) */ -#if defined(SOL_IPV6) +#if defined(HAVE_IPV6) static ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, SocketDescriptor* descP, @@ -11576,7 +11637,7 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, #endif -#endif // defined(SOL_IPV6) +#endif // defined(HAVE_IPV6) @@ -14180,8 +14241,12 @@ char* encode_cmsghdr_level(ErlNifEnv* env, xres = NULL; break; +#if defined(HAVE_IPV6) #if defined(SOL_IPV6) case SOL_IPV6: +#else + case IPPROTO_IPV6: +#endif *eLevel = esock_atom_ip; xres = NULL; break; @@ -14228,9 +14293,13 @@ char* decode_cmsghdr_level(ErlNifEnv* env, *level = IPPROTO_IP; #endif xres = NULL; -#if defined(SOL_IPV6) +#if defined(HAVE_IPV6) } else if (COMPARE(eLevel, esock_atom_ipv6) == 0) { +#if defined(SOL_IPV6) *level = SOL_IPV6; +#else + *level = IPPROTO_IPV6; +#endif xres = NULL; #endif } else if (COMPARE(eLevel, esock_atom_udp) == 0) { @@ -14330,8 +14399,12 @@ char* encode_cmsghdr_type(ErlNifEnv* env, } break; +#if defined(HAVE_IPV6) #if defined(SOL_IPV6) - case SOL_IPV6: + case SOL_IPV6: +#else + case IPPROTO_IPV6: +#endif switch (type) { #if defined(IPV6_PKTINFO) case IPV6_PKTINFO: @@ -14442,8 +14515,12 @@ char* decode_cmsghdr_type(ErlNifEnv* env, } break; +#if defined(HAVE_IPV6) #if defined(SOL_IPV6) case SOL_IPV6: +#else + case IPPROTO_IPV6: +#endif if (IS_NUM(env, eType)) { if (!GET_INT(env, eType, type)) { *type = -1; @@ -14516,8 +14593,12 @@ char* encode_cmsghdr_data(ErlNifEnv* env, eCMsgHdrData); break; +#if defined(HAVE_IPV6) #if defined(SOL_IPV6) case SOL_IPV6: +#else + case IPPROTO_IPV6: +#endif xres = encode_cmsghdr_data_ipv6(env, ctrlBuf, type, dataP, dataPos, dataLen, eCMsgHdrData); @@ -14714,7 +14795,7 @@ char* encode_cmsghdr_data_ip(ErlNifEnv* env, * Encode the data part when protocol = IPv6 of the cmsghdr(). * */ -#if defined(SOL_IPV6) +#if defined(HAVE_IPV6) static char* encode_cmsghdr_data_ipv6(ErlNifEnv* env, ERL_NIF_TERM ctrlBuf, @@ -16935,196 +17016,196 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_want = MKA(env, str_want); /* Global atom(s) */ - esock_atom_accept = MKA(env, "accept"); - esock_atom_acceptconn = MKA(env, "acceptconn"); - esock_atom_acceptfilter = MKA(env, "acceptfilter"); - esock_atom_adaption_layer = MKA(env, "adaption_layer"); - esock_atom_addr = MKA(env, "addr"); - esock_atom_addrform = MKA(env, "addrform"); - esock_atom_add_membership = MKA(env, "add_membership"); - esock_atom_add_source_membership = MKA(env, "add_source_membership"); - esock_atom_any = MKA(env, "any"); - esock_atom_associnfo = MKA(env, "associnfo"); - esock_atom_authhdr = MKA(env, "authhdr"); - esock_atom_auth_active_key = MKA(env, "auth_active_key"); - esock_atom_auth_asconf = MKA(env, "auth_asconf"); - esock_atom_auth_chunk = MKA(env, "auth_chunk"); - esock_atom_auth_delete_key = MKA(env, "auth_delete_key"); - esock_atom_auth_key = MKA(env, "auth_key"); - esock_atom_auth_level = MKA(env, "auth_level"); - esock_atom_autoclose = MKA(env, "autoclose"); - esock_atom_bindtodevice = MKA(env, "bindtodevice"); - esock_atom_block_source = MKA(env, "block_source"); - esock_atom_broadcast = MKA(env, "broadcast"); - esock_atom_busy_poll = MKA(env, "busy_poll"); - esock_atom_checksum = MKA(env, "checksum"); - esock_atom_connect = MKA(env, "connect"); - esock_atom_congestion = MKA(env, "congestion"); - esock_atom_context = MKA(env, "context"); - esock_atom_cork = MKA(env, "cork"); - esock_atom_credentials = MKA(env, "credentials"); - esock_atom_ctrl = MKA(env, "ctrl"); - esock_atom_ctrunc = MKA(env, "ctrunc"); - esock_atom_data = MKA(env, "data"); - esock_atom_debug = MKA(env, "debug"); + esock_atom_accept = MKA(env, "accept"); + esock_atom_acceptconn = MKA(env, "acceptconn"); + esock_atom_acceptfilter = MKA(env, "acceptfilter"); + esock_atom_adaption_layer = MKA(env, "adaption_layer"); + esock_atom_addr = MKA(env, "addr"); + esock_atom_addrform = MKA(env, "addrform"); + esock_atom_add_membership = MKA(env, "add_membership"); + esock_atom_add_source_membership = MKA(env, "add_source_membership"); + esock_atom_any = MKA(env, "any"); + esock_atom_associnfo = MKA(env, "associnfo"); + esock_atom_authhdr = MKA(env, "authhdr"); + esock_atom_auth_active_key = MKA(env, "auth_active_key"); + esock_atom_auth_asconf = MKA(env, "auth_asconf"); + esock_atom_auth_chunk = MKA(env, "auth_chunk"); + esock_atom_auth_delete_key = MKA(env, "auth_delete_key"); + esock_atom_auth_key = MKA(env, "auth_key"); + esock_atom_auth_level = MKA(env, "auth_level"); + esock_atom_autoclose = MKA(env, "autoclose"); + esock_atom_bindtodevice = MKA(env, "bindtodevice"); + esock_atom_block_source = MKA(env, "block_source"); + esock_atom_broadcast = MKA(env, "broadcast"); + esock_atom_busy_poll = MKA(env, "busy_poll"); + esock_atom_checksum = MKA(env, "checksum"); + esock_atom_connect = MKA(env, "connect"); + esock_atom_congestion = MKA(env, "congestion"); + esock_atom_context = MKA(env, "context"); + esock_atom_cork = MKA(env, "cork"); + esock_atom_credentials = MKA(env, "credentials"); + esock_atom_ctrl = MKA(env, "ctrl"); + esock_atom_ctrunc = MKA(env, "ctrunc"); + esock_atom_data = MKA(env, "data"); + esock_atom_debug = MKA(env, "debug"); esock_atom_default_send_params = MKA(env, "default_send_params"); esock_atom_delayed_ack_time = MKA(env, "delayed_ack_time"); - esock_atom_dgram = MKA(env, "dgram"); + esock_atom_dgram = MKA(env, "dgram"); esock_atom_disable_fragments = MKA(env, "disable_fragments"); - esock_atom_domain = MKA(env, "domain"); - esock_atom_dontfrag = MKA(env, "dontfrag"); - esock_atom_dontroute = MKA(env, "dontroute"); + esock_atom_domain = MKA(env, "domain"); + esock_atom_dontfrag = MKA(env, "dontfrag"); + esock_atom_dontroute = MKA(env, "dontroute"); esock_atom_drop_membership = MKA(env, "drop_membership"); esock_atom_drop_source_membership = MKA(env, "drop_source_membership"); - esock_atom_dstopts = MKA(env, "dstpopts"); - esock_atom_eor = MKA(env, "eor"); - esock_atom_error = MKA(env, "error"); - esock_atom_errqueue = MKA(env, "errqueue"); - esock_atom_esp_network_level = MKA(env, "esp_network_level"); - esock_atom_esp_trans_level = MKA(env, "esp_trans_level"); - esock_atom_events = MKA(env, "events"); - esock_atom_explicit_eor = MKA(env, "explicit_eor"); - esock_atom_faith = MKA(env, "faith"); - esock_atom_false = MKA(env, "false"); - esock_atom_family = MKA(env, "family"); - esock_atom_flags = MKA(env, "flags"); - esock_atom_flowinfo = MKA(env, "flowinfo"); - esock_atom_fragment_interleave = MKA(env, "fragment_interleave"); - esock_atom_freebind = MKA(env, "freebind"); - esock_atom_get_peer_addr_info = MKA(env, "get_peer_addr_info"); - esock_atom_hdrincl = MKA(env, "hdrincl"); - esock_atom_hmac_ident = MKA(env, "hmac_ident"); - esock_atom_hoplimit = MKA(env, "hoplimit"); - esock_atom_hopopts = MKA(env, "hopopts"); - esock_atom_ifindex = MKA(env, "ifindex"); - esock_atom_inet = MKA(env, "inet"); - esock_atom_inet6 = MKA(env, "inet6"); - esock_atom_info = MKA(env, "info"); - esock_atom_initmsg = MKA(env, "initmsg"); - esock_atom_iov = MKA(env, "iov"); - esock_atom_ip = MKA(env, "ip"); - esock_atom_ipcomp_level = MKA(env, "ipcomp_level"); - esock_atom_ipv6 = MKA(env, "ipv6"); - esock_atom_i_want_mapped_v4_addr = MKA(env, "i_want_mapped_v4_addr"); - esock_atom_join_group = MKA(env, "join_group"); - esock_atom_keepalive = MKA(env, "keepalive"); - esock_atom_keepcnt = MKA(env, "keepcnt"); - esock_atom_keepidle = MKA(env, "keepidle"); - esock_atom_keepintvl = MKA(env, "keepintvl"); - esock_atom_leave_group = MKA(env, "leave_group"); - esock_atom_level = MKA(env, "level"); - esock_atom_linger = MKA(env, "linger"); - esock_atom_local = MKA(env, "local"); - esock_atom_local_auth_chunks = MKA(env, "local_auth_chunks"); - esock_atom_loopback = MKA(env, "loopback"); - esock_atom_lowdelay = MKA(env, "lowdelay"); - esock_atom_mark = MKA(env, "mark"); - esock_atom_maxburst = MKA(env, "maxburst"); - esock_atom_maxseg = MKA(env, "maxseg"); - esock_atom_md5sig = MKA(env, "md5sig"); - esock_atom_mincost = MKA(env, "mincost"); - esock_atom_minttl = MKA(env, "minttl"); - esock_atom_msfilter = MKA(env, "msfilter"); - esock_atom_mtu = MKA(env, "mtu"); - esock_atom_mtu_discover = MKA(env, "mtu_discover"); - esock_atom_multicast_all = MKA(env, "multicast_all"); - esock_atom_multicast_hops = MKA(env, "multicast_hops"); - esock_atom_multicast_if = MKA(env, "multicast_if"); - esock_atom_multicast_loop = MKA(env, "multicast_loop"); - esock_atom_multicast_ttl = MKA(env, "multicast_ttl"); - esock_atom_nodefrag = MKA(env, "nodefrag"); - esock_atom_nodelay = MKA(env, "nodelay"); - esock_atom_noopt = MKA(env, "noopt"); - esock_atom_nopush = MKA(env, "nopush"); - esock_atom_not_found = MKA(env, "not_found"); - esock_atom_not_owner = MKA(env, "not_owner"); - esock_atom_ok = MKA(env, "ok"); - esock_atom_oob = MKA(env, "oob"); - esock_atom_oobinline = MKA(env, "oobinline"); - esock_atom_options = MKA(env, "options"); - esock_atom_origdstaddr = MKA(env, "origdstaddr"); + esock_atom_dstopts = MKA(env, "dstpopts"); + esock_atom_eor = MKA(env, "eor"); + esock_atom_error = MKA(env, "error"); + esock_atom_errqueue = MKA(env, "errqueue"); + esock_atom_esp_network_level = MKA(env, "esp_network_level"); + esock_atom_esp_trans_level = MKA(env, "esp_trans_level"); + esock_atom_events = MKA(env, "events"); + esock_atom_explicit_eor = MKA(env, "explicit_eor"); + esock_atom_faith = MKA(env, "faith"); + esock_atom_false = MKA(env, "false"); + esock_atom_family = MKA(env, "family"); + esock_atom_flags = MKA(env, "flags"); + esock_atom_flowinfo = MKA(env, "flowinfo"); + esock_atom_fragment_interleave = MKA(env, "fragment_interleave"); + esock_atom_freebind = MKA(env, "freebind"); + esock_atom_get_peer_addr_info = MKA(env, "get_peer_addr_info"); + esock_atom_hdrincl = MKA(env, "hdrincl"); + esock_atom_hmac_ident = MKA(env, "hmac_ident"); + esock_atom_hoplimit = MKA(env, "hoplimit"); + esock_atom_hopopts = MKA(env, "hopopts"); + esock_atom_ifindex = MKA(env, "ifindex"); + esock_atom_inet = MKA(env, "inet"); + esock_atom_inet6 = MKA(env, "inet6"); + esock_atom_info = MKA(env, "info"); + esock_atom_initmsg = MKA(env, "initmsg"); + esock_atom_iov = MKA(env, "iov"); + esock_atom_ip = MKA(env, "ip"); + esock_atom_ipcomp_level = MKA(env, "ipcomp_level"); + esock_atom_ipv6 = MKA(env, "ipv6"); + esock_atom_i_want_mapped_v4_addr = MKA(env, "i_want_mapped_v4_addr"); + esock_atom_join_group = MKA(env, "join_group"); + esock_atom_keepalive = MKA(env, "keepalive"); + esock_atom_keepcnt = MKA(env, "keepcnt"); + esock_atom_keepidle = MKA(env, "keepidle"); + esock_atom_keepintvl = MKA(env, "keepintvl"); + esock_atom_leave_group = MKA(env, "leave_group"); + esock_atom_level = MKA(env, "level"); + esock_atom_linger = MKA(env, "linger"); + esock_atom_local = MKA(env, "local"); + esock_atom_local_auth_chunks = MKA(env, "local_auth_chunks"); + esock_atom_loopback = MKA(env, "loopback"); + esock_atom_lowdelay = MKA(env, "lowdelay"); + esock_atom_mark = MKA(env, "mark"); + esock_atom_maxburst = MKA(env, "maxburst"); + esock_atom_maxseg = MKA(env, "maxseg"); + esock_atom_md5sig = MKA(env, "md5sig"); + esock_atom_mincost = MKA(env, "mincost"); + esock_atom_minttl = MKA(env, "minttl"); + esock_atom_msfilter = MKA(env, "msfilter"); + esock_atom_mtu = MKA(env, "mtu"); + esock_atom_mtu_discover = MKA(env, "mtu_discover"); + esock_atom_multicast_all = MKA(env, "multicast_all"); + esock_atom_multicast_hops = MKA(env, "multicast_hops"); + esock_atom_multicast_if = MKA(env, "multicast_if"); + esock_atom_multicast_loop = MKA(env, "multicast_loop"); + esock_atom_multicast_ttl = MKA(env, "multicast_ttl"); + esock_atom_nodefrag = MKA(env, "nodefrag"); + esock_atom_nodelay = MKA(env, "nodelay"); + esock_atom_noopt = MKA(env, "noopt"); + esock_atom_nopush = MKA(env, "nopush"); + esock_atom_not_found = MKA(env, "not_found"); + esock_atom_not_owner = MKA(env, "not_owner"); + esock_atom_ok = MKA(env, "ok"); + esock_atom_oob = MKA(env, "oob"); + esock_atom_oobinline = MKA(env, "oobinline"); + esock_atom_options = MKA(env, "options"); + esock_atom_origdstaddr = MKA(env, "origdstaddr"); esock_atom_partial_delivery_point = MKA(env, "partial_delivery_point"); - esock_atom_passcred = MKA(env, "passcred"); - esock_atom_path = MKA(env, "path"); - esock_atom_peekcred = MKA(env, "peekcred"); - esock_atom_peek_off = MKA(env, "peek_off"); - esock_atom_peer_addr_params = MKA(env, "peer_addr_params"); - esock_atom_peer_auth_chunks = MKA(env, "peer_auth_chunks"); - esock_atom_pktinfo = MKA(env, "pktinfo"); - esock_atom_pktoptions = MKA(env, "pktoptions"); - esock_atom_port = MKA(env, "port"); - esock_atom_portrange = MKA(env, "portrange"); - esock_atom_primary_addr = MKA(env, "primary_addr"); - esock_atom_priority = MKA(env, "priority"); - esock_atom_protocol = MKA(env, "protocol"); - esock_atom_raw = MKA(env, "raw"); - esock_atom_rcvbuf = MKA(env, "rcvbuf"); - esock_atom_rcvbufforce = MKA(env, "rcvbufforce"); - esock_atom_rcvlowat = MKA(env, "rcvlowat"); - esock_atom_rcvtimeo = MKA(env, "rcvtimeo"); - esock_atom_rdm = MKA(env, "rdm"); - esock_atom_recv = MKA(env, "recv"); - esock_atom_recvdstaddr = MKA(env, "recvdstaddr"); - esock_atom_recverr = MKA(env, "recverr"); - esock_atom_recvfrom = MKA(env, "recvfrom"); - esock_atom_recvif = MKA(env, "recvif"); - esock_atom_recvmsg = MKA(env, "recvmsg"); - esock_atom_recvopts = MKA(env, "recvopts"); - esock_atom_recvorigdstaddr = MKA(env, "recvorigdstaddr"); - esock_atom_recvpktinfo = MKA(env, "recvpktinfo"); - esock_atom_recvtclass = MKA(env, "recvtclass"); - esock_atom_recvtos = MKA(env, "recvtos"); - esock_atom_recvttl = MKA(env, "recvttl"); - esock_atom_reliability = MKA(env, "reliability"); - esock_atom_reset_streams = MKA(env, "reset_streams"); - esock_atom_retopts = MKA(env, "retopts"); - esock_atom_reuseaddr = MKA(env, "reuseaddr"); - esock_atom_reuseport = MKA(env, "reuseport"); - esock_atom_rights = MKA(env, "rights"); - esock_atom_router_alert = MKA(env, "router_alert"); - esock_atom_rthdr = MKA(env, "rthdr"); - esock_atom_rtoinfo = MKA(env, "rtoinfo"); - esock_atom_rxq_ovfl = MKA(env, "rxq_ovfl"); - esock_atom_scope_id = MKA(env, "scope_id"); - esock_atom_sctp = MKA(env, "sctp"); - esock_atom_sec = MKA(env, "sec"); - esock_atom_select_sent = MKA(env, "select_sent"); - esock_atom_send = MKA(env, "send"); - esock_atom_sendmsg = MKA(env, "sendmsg"); - esock_atom_sendsrcaddr = MKA(env, "sendsrcaddr"); - esock_atom_sendto = MKA(env, "sendto"); - esock_atom_seqpacket = MKA(env, "seqpacket"); - esock_atom_setfib = MKA(env, "setfib"); - esock_atom_set_peer_primary_addr = MKA(env, "set_peer_primary_addr"); - esock_atom_sndbuf = MKA(env, "sndbuf"); - esock_atom_sndbufforce = MKA(env, "sndbufforce"); - esock_atom_sndlowat = MKA(env, "sndlowat"); - esock_atom_sndtimeo = MKA(env, "sndtimeo"); - esock_atom_socket = MKA(env, "socket"); - esock_atom_spec_dst = MKA(env, "spec_dst"); - esock_atom_status = MKA(env, "status"); - esock_atom_stream = MKA(env, "stream"); - esock_atom_syncnt = MKA(env, "syncnt"); - esock_atom_tclass = MKA(env, "tclass"); - esock_atom_tcp = MKA(env, "tcp"); - esock_atom_throughput = MKA(env, "throughput"); - esock_atom_timestamp = MKA(env, "timestamp"); - esock_atom_tos = MKA(env, "tos"); - esock_atom_transparent = MKA(env, "transparent"); - esock_atom_true = MKA(env, "true"); - esock_atom_trunc = MKA(env, "trunc"); - esock_atom_ttl = MKA(env, "ttl"); - esock_atom_type = MKA(env, "type"); - esock_atom_udp = MKA(env, "udp"); - esock_atom_unblock_source = MKA(env, "unblock_source"); - esock_atom_undefined = MKA(env, "undefined"); - esock_atom_unicast_hops = MKA(env, "unicast_hops"); - esock_atom_unknown = MKA(env, "unknown"); - esock_atom_usec = MKA(env, "usec"); - esock_atom_user_timeout = MKA(env, "user_timeout"); - esock_atom_use_ext_recvinfo = MKA(env, "use_ext_recvinfo"); - esock_atom_use_min_mtu = MKA(env, "use_min_mtu"); - esock_atom_v6only = MKA(env, "v6only"); + esock_atom_passcred = MKA(env, "passcred"); + esock_atom_path = MKA(env, "path"); + esock_atom_peekcred = MKA(env, "peekcred"); + esock_atom_peek_off = MKA(env, "peek_off"); + esock_atom_peer_addr_params = MKA(env, "peer_addr_params"); + esock_atom_peer_auth_chunks = MKA(env, "peer_auth_chunks"); + esock_atom_pktinfo = MKA(env, "pktinfo"); + esock_atom_pktoptions = MKA(env, "pktoptions"); + esock_atom_port = MKA(env, "port"); + esock_atom_portrange = MKA(env, "portrange"); + esock_atom_primary_addr = MKA(env, "primary_addr"); + esock_atom_priority = MKA(env, "priority"); + esock_atom_protocol = MKA(env, "protocol"); + esock_atom_raw = MKA(env, "raw"); + esock_atom_rcvbuf = MKA(env, "rcvbuf"); + esock_atom_rcvbufforce = MKA(env, "rcvbufforce"); + esock_atom_rcvlowat = MKA(env, "rcvlowat"); + esock_atom_rcvtimeo = MKA(env, "rcvtimeo"); + esock_atom_rdm = MKA(env, "rdm"); + esock_atom_recv = MKA(env, "recv"); + esock_atom_recvdstaddr = MKA(env, "recvdstaddr"); + esock_atom_recverr = MKA(env, "recverr"); + esock_atom_recvfrom = MKA(env, "recvfrom"); + esock_atom_recvif = MKA(env, "recvif"); + esock_atom_recvmsg = MKA(env, "recvmsg"); + esock_atom_recvopts = MKA(env, "recvopts"); + esock_atom_recvorigdstaddr = MKA(env, "recvorigdstaddr"); + esock_atom_recvpktinfo = MKA(env, "recvpktinfo"); + esock_atom_recvtclass = MKA(env, "recvtclass"); + esock_atom_recvtos = MKA(env, "recvtos"); + esock_atom_recvttl = MKA(env, "recvttl"); + esock_atom_reliability = MKA(env, "reliability"); + esock_atom_reset_streams = MKA(env, "reset_streams"); + esock_atom_retopts = MKA(env, "retopts"); + esock_atom_reuseaddr = MKA(env, "reuseaddr"); + esock_atom_reuseport = MKA(env, "reuseport"); + esock_atom_rights = MKA(env, "rights"); + esock_atom_router_alert = MKA(env, "router_alert"); + esock_atom_rthdr = MKA(env, "rthdr"); + esock_atom_rtoinfo = MKA(env, "rtoinfo"); + esock_atom_rxq_ovfl = MKA(env, "rxq_ovfl"); + esock_atom_scope_id = MKA(env, "scope_id"); + esock_atom_sctp = MKA(env, "sctp"); + esock_atom_sec = MKA(env, "sec"); + esock_atom_select_sent = MKA(env, "select_sent"); + esock_atom_send = MKA(env, "send"); + esock_atom_sendmsg = MKA(env, "sendmsg"); + esock_atom_sendsrcaddr = MKA(env, "sendsrcaddr"); + esock_atom_sendto = MKA(env, "sendto"); + esock_atom_seqpacket = MKA(env, "seqpacket"); + esock_atom_setfib = MKA(env, "setfib"); + esock_atom_set_peer_primary_addr = MKA(env, "set_peer_primary_addr"); + esock_atom_sndbuf = MKA(env, "sndbuf"); + esock_atom_sndbufforce = MKA(env, "sndbufforce"); + esock_atom_sndlowat = MKA(env, "sndlowat"); + esock_atom_sndtimeo = MKA(env, "sndtimeo"); + esock_atom_socket = MKA(env, "socket"); + esock_atom_spec_dst = MKA(env, "spec_dst"); + esock_atom_status = MKA(env, "status"); + esock_atom_stream = MKA(env, "stream"); + esock_atom_syncnt = MKA(env, "syncnt"); + esock_atom_tclass = MKA(env, "tclass"); + esock_atom_tcp = MKA(env, "tcp"); + esock_atom_throughput = MKA(env, "throughput"); + esock_atom_timestamp = MKA(env, "timestamp"); + esock_atom_tos = MKA(env, "tos"); + esock_atom_transparent = MKA(env, "transparent"); + esock_atom_true = MKA(env, "true"); + esock_atom_trunc = MKA(env, "trunc"); + esock_atom_ttl = MKA(env, "ttl"); + esock_atom_type = MKA(env, "type"); + esock_atom_udp = MKA(env, "udp"); + esock_atom_unblock_source = MKA(env, "unblock_source"); + esock_atom_undefined = MKA(env, "undefined"); + esock_atom_unicast_hops = MKA(env, "unicast_hops"); + esock_atom_unknown = MKA(env, "unknown"); + esock_atom_usec = MKA(env, "usec"); + esock_atom_user_timeout = MKA(env, "user_timeout"); + esock_atom_use_ext_recvinfo = MKA(env, "use_ext_recvinfo"); + esock_atom_use_min_mtu = MKA(env, "use_min_mtu"); + esock_atom_v6only = MKA(env, "v6only"); /* Global error codes */ esock_atom_eafnosupport = MKA(env, ESOCK_STR_EAFNOSUPPORT); -- cgit v1.2.3 From 468c633a6e96a689532957e299ce7fb7c67a5d58 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 15 Oct 2018 18:29:03 +0200 Subject: [socket-nif] Fix socket close when owner process exits The handling of terminating controlling-process was incomplete. Also added state check (CLOSED and CLOSING) in all nif-functions to prevent access for a closed (or closing) socket. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 180 +++++++++++++++++++++++++++++++-- 1 file changed, 173 insertions(+), 7 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 0ef88162bc..f09f618653 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -390,7 +390,18 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define SOCKET_STATE_ACCEPTING (SOCKET_STATE_LISTENING | SOCKET_FLAG_ACC) #define SOCKET_STATE_CLOSING (SOCKET_FLAG_CLOSE) -#define IS_OPEN(d) \ +#define IS_CLOSED(d) \ + ((d)->state == SOCKET_STATE_CLOSED) + +/* +#define IS_STATE(d, f) \ + (((d)->state & (f)) == (f)) +*/ + +#define IS_CLOSING(d) \ + (((d)->state & SOCKET_STATE_CLOSING) == SOCKET_STATE_CLOSING) + +#define IS_OPEN(d) \ (((d)->state & SOCKET_FLAG_OPEN) == SOCKET_FLAG_OPEN) #define IS_CONNECTED(d) \ @@ -2945,6 +2956,9 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, } eSockAddr = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_bind -> args when sock = %d (0x%lX)" "\r\n Socket: %T" @@ -3034,6 +3048,9 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, } eSockAddr = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_connect -> args when sock = %d:" "\r\n Socket: %T" @@ -3235,6 +3252,9 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_listen -> args when sock = %d:" "\r\n Socket: %T" @@ -3296,6 +3316,9 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, } ref = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_accept -> args when sock = %d:" "\r\n Socket: %T" @@ -3676,6 +3699,9 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, } sendRef = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_send -> args when sock = %d:" "\r\n Socket: %T" @@ -3800,6 +3826,9 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, sendRef = argv[1]; eSockAddr = argv[3]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_sendto -> args when sock = %d:" "\r\n Socket: %T" @@ -3923,6 +3952,9 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, sendRef = argv[1]; eMsgHdr = argv[2]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_sendmsg -> args when sock = %d:" "\r\n Socket: %T" @@ -4212,6 +4244,9 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, } recvRef = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + if (!IS_CONNECTED(descP)) return esock_make_error(env, atom_enotconn); @@ -4351,6 +4386,9 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, } recvRef = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_recvfrom -> args when sock = %d:" "\r\n Socket: %T" @@ -4511,6 +4549,9 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, } recvRef = argv[1]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_recvmsg -> args when sock = %d:" "\r\n Socket: %T" @@ -4669,6 +4710,9 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + return nclose(env, descP); } @@ -4807,10 +4851,10 @@ ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, { ERL_NIF_TERM reply; - if (descP->state == SOCKET_STATE_CLOSED) + if (IS_CLOSED(descP)) return esock_atom_ok; - if (descP->state != SOCKET_STATE_CLOSING) + if (!IS_CLOSING(descP)) return esock_make_error(env, atom_enotclosing); /* This nif is executed in a dirty scheduler just so that @@ -4873,6 +4917,9 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + if (!ehow2how(ehow, &how)) return enif_make_badarg(env); @@ -4957,6 +5004,9 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, eIsEncoded = argv[1]; eVal = argv[4]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + isEncoded = esock_decode_bool(eIsEncoded); /* SGDBG( ("SOCKET", "nif_setopt -> eIsDecoded (%T) decoded: %d\r\n", */ @@ -8214,6 +8264,9 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, eIsEncoded = argv[1]; eOpt = argv[3]; // Is "normally" an int, but if raw mode: {Int, ValueSz} + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_getopt -> args when sock = %d:" "\r\n Socket: %T" @@ -10807,6 +10860,9 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_sockname -> args when sock = %d:" "\r\n Socket: %T" @@ -10872,6 +10928,9 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, return enif_make_badarg(env); } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_peername -> args when sock = %d:" "\r\n Socket: %T" @@ -10940,6 +10999,9 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, op = argv[1]; opRef = argv[2]; + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + SSDBG( descP, ("SOCKET", "nif_cancel -> args when sock = %d:" "\r\n op: %T" @@ -14955,6 +15017,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) MLOCK(descP->accMtx); MLOCK(descP->closeMtx); + SSDBG( descP, ("SOCKET", "socket_stop -> all mutex(s) locked\r\n") ); descP->state = SOCKET_STATE_CLOSING; // Just in case...??? descP->isReadable = FALSE; @@ -14964,7 +15027,13 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) /* We should check that we actually have a monitor. * This *should* be done with a "NULL" monitor value, * which there currently is none... + * If we got here because the controlling process died, + * its no point in demonitor. Also, we not actually have + * a monitor in that case... */ + SSDBG( descP, + ("SOCKET", + "socket_stop -> demonitor (maybe) controlling process\r\n") ); DEMONP(env, descP, &descP->ctrlMon); if (descP->currentWriterP != NULL) { @@ -14972,6 +15041,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * writers waiting. */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle writer(s)\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentWriter.pid) && @@ -14998,6 +15068,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * readers waiting. */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle reader(s)\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentReader.pid) && @@ -15023,6 +15094,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * acceptors waiting. */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle acceptor(s)\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentAcceptor.pid) && @@ -15085,7 +15157,15 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) } } - + + if (!is_direct_call) { + descP->sock = INVALID_SOCKET; + descP->event = INVALID_EVENT; + descP->state = SOCKET_STATE_CLOSED; + } + + SSDBG( descP, ("SOCKET", "socket_stop -> unlock all mutex(s)\r\n") ); + MUNLOCK(descP->closeMtx); MUNLOCK(descP->accMtx); MUNLOCK(descP->readMtx); @@ -15163,22 +15243,108 @@ void socket_down(ErlNifEnv* env, if (compare_pids(env, &descP->ctrlPid, pid)) { + int selectRes; + /* We don't bother with the queue cleanup here - * we leave it to the stop callback function. */ + SSDBG( descP, ("SOCKET", "socket_down -> controlling process term\r\n") ); + descP->state = SOCKET_STATE_CLOSING; descP->closeLocal = TRUE; descP->closerPid = *pid; descP->closerMon = *mon; - descP->closeRef = MKREF(env); - enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), - descP, NULL, descP->closeRef); + descP->closeRef = MKREF(env); // Do we really need this in this case? + + selectRes = enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), + descP, NULL, descP->closeRef); + + if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { + /* We are done - wwe can finalize (socket close) directly */ + SSDBG( descP, + ("SOCKET", "socket_down -> [%d] stop called\r\n", descP->sock) ); + dec_socket(descP->domain, descP->type, descP->protocol); + descP->state = SOCKET_STATE_CLOSED; + + /* And finally close the socket. + * Since we close the socket because of an exiting owner, + * we do not need to wait for buffers to sync (linger). + * If the owner wish to ensure the buffer are written, + * it should have closed teh socket explicitly... + */ + if (sock_close(descP->sock) != 0) { + int save_errno = sock_errno(); + + esock_warning_msg("Failed closing socket for terminating " + "controlling process: " + "\r\n Controlling Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d" + "\r\n", pid, descP->sock, save_errno); + } + sock_close_event(descP->event); + + descP->sock = INVALID_SOCKET; + descP->event = INVALID_EVENT; + + descP->state = SOCKET_STATE_CLOSED; + + } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { + /* The stop callback function has been *scheduled* which means that + * "should" wait for it to complete. But since we are in a callback + * (down) function, we cannot... + * So, we must close the socket + */ + SSDBG( descP, + ("SOCKET", + "socket_down -> [%d] stop scheduled\r\n", descP->sock) ); + dec_socket(descP->domain, descP->type, descP->protocol); + + /* And now what? We can't wait for the stop function here... + * So, we simply close it here and leave the rest of the "close" + * for later (when the stop function actually gets called... + */ + + if (sock_close(descP->sock) != 0) { + int save_errno = sock_errno(); + + esock_warning_msg("Failed closing socket for terminating " + "controlling process: " + "\r\n Controlling Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d" + "\r\n", pid, descP->sock, save_errno); + } + sock_close_event(descP->event); + + } else { + + /* + * + * + * WE SHOULD REALLY HAVE A WAY TO CLOBBER THE SOCKET, + * SO WE DON'T LET STUFF LEAK. + * NOW, BECAUSE WE FAILED TO SELECT, WE CANNOT FINISH + * THE CLOSE, WHAT TO DO? ABORT? + * + * + */ + esock_warning_msg("Failed selecting stop when handling down " + "of controlling process: " + "\r\n Select Res: %d" + "\r\n Controlling Process: %T" + "\r\n Descriptor: %d" + "\r\n", selectRes, pid, descP->sock); + } + } else { /* check all operation queue(s): acceptor, writer and reader. */ + SSDBG( descP, ("SOCKET", "socket_down -> other process term\r\n") ); + MLOCK(descP->accMtx); if (descP->currentAcceptorP != NULL) socket_down_acceptor(env, descP, pid); -- cgit v1.2.3 From 043624804888dc021a75b01c0a3d8c1c0a1fde23 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 16 Oct 2018 15:53:37 +0200 Subject: [socket-nif] Recv handling of closing sockets Fixed more issues regarding closing sockets. Specifically with respect to recv calls. Also, added demonitor for all *current* processes. Also fixed a buffer overflow problem when writing an warning message (the timestamp buffer was not large enough). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 61 +++++++++++++++++++++++++++------ erts/emulator/nifs/common/socket_util.c | 2 +- 2 files changed, 51 insertions(+), 12 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f09f618653..3ec19c5a60 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -4776,17 +4776,23 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { /* Prep done - inform the caller it can finalize (close) directly */ SSDBG( descP, - ("SOCKET", "nclose -> [%d] stop called\r\n", descP->sock) ); + ("SOCKET", "nclose -> [%d] stop was called\r\n", descP->sock) ); dec_socket(domain, type, protocol); reply = esock_atom_ok; } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { /* The stop callback function has been *scheduled* which means that we * have to wait for it to complete. */ SSDBG( descP, - ("SOCKET", "nclose -> [%d] stop scheduled\r\n", descP->sock) ); + ("SOCKET", "nclose -> [%d] stop was scheduled\r\n", + descP->sock) ); dec_socket(domain, type, protocol); // SHALL WE DO THIS AT finalize? reply = esock_make_ok2(env, descP->closeRef); } else { + + SSDBG( descP, + ("SOCKET", "nclose -> [%d] stop failed: %d\r\n", + descP->sock, selectRes) ); + /* * * WE SHOULD REALLY HAVE A WAY TO CLOBBER THE SOCKET, @@ -4796,6 +4802,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, * * */ + reason = MKT2(env, atom_select, MKI(env, selectRes)); reply = esock_make_error(env, reason); } @@ -12000,6 +12007,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] eagain\r\n", toRead) ); + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) + return esock_make_error_str(env, xres); + SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); @@ -15005,6 +15015,7 @@ static void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) { SocketDescriptor* descP = (SocketDescriptor*) obj; + int dres; SSDBG( descP, ("SOCKET", "socket_stop -> entry when" @@ -15033,15 +15044,24 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) */ SSDBG( descP, ("SOCKET", - "socket_stop -> demonitor (maybe) controlling process\r\n") ); - DEMONP(env, descP, &descP->ctrlMon); + "socket_stop -> demonitor (maybe) controlling process (%T)\r\n", + descP->ctrlPid) ); + dres = DEMONP(env, descP, &descP->ctrlMon); + SSDBG( descP, ("SOCKET", "socket_stop -> demonitor result: %d\r\n", dres) ); + if (descP->currentWriterP != NULL) { /* We have a (current) writer and *may* therefor also have * writers waiting. */ - SSDBG( descP, ("SOCKET", "socket_stop -> handle writer(s)\r\n") ); + SSDBG( descP, + ("SOCKET", + "socket_stop -> demonitor (maybe) current writer: %T, %T\r\n", + descP->currentWriter.pid, descP->currentWriter.ref) ); + DEMONP(env, descP, &descP->currentWriter.mon); + + SSDBG( descP, ("SOCKET", "socket_stop -> handle current writer\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentWriter.pid) && @@ -15059,6 +15079,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) } /* And also deal with the waiting writers (in the same way) */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting writer(s)\r\n") ); inform_waiting_procs(env, descP, &descP->writersQ, TRUE, atom_closed); } @@ -15068,7 +15089,13 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * readers waiting. */ - SSDBG( descP, ("SOCKET", "socket_stop -> handle reader(s)\r\n") ); + SSDBG( descP, + ("SOCKET", + "socket_stop -> demonitor (maybe) current reader: %T, %T\r\n", + descP->currentReader.pid, descP->currentReader.ref) ); + DEMONP(env, descP, &descP->currentReader.mon); + + SSDBG( descP, ("SOCKET", "socket_stop -> handle current reader\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentReader.pid) && @@ -15086,6 +15113,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) } /* And also deal with the waiting readers (in the same way) */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting reader(s)\r\n") ); inform_waiting_procs(env, descP, &descP->readersQ, TRUE, atom_closed); } @@ -15094,7 +15122,13 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * acceptors waiting. */ - SSDBG( descP, ("SOCKET", "socket_stop -> handle acceptor(s)\r\n") ); + SSDBG( descP, + ("SOCKET", + "socket_stop -> demonitor (maybe) current acceptor: %T, %T\r\n", + descP->currentAcceptor.pid, descP->currentAcceptor.ref) ); + DEMONP(env, descP, &descP->currentAcceptor.mon); + + SSDBG( descP, ("SOCKET", "socket_stop -> handle current acceptor\r\n") ); if (!compare_pids(env, &descP->closerPid, &descP->currentAcceptor.pid) && @@ -15112,6 +15146,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) } /* And also deal with the waiting acceptors (in the same way) */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting acceptor(s)\r\n") ); inform_waiting_procs(env, descP, &descP->acceptorsQ, TRUE, atom_closed); } @@ -15237,9 +15272,13 @@ void socket_down(ErlNifEnv* env, SocketDescriptor* descP = (SocketDescriptor*) obj; SSDBG( descP, ("SOCKET", "socket_down -> entry with" - "\r\n sock: %d" - "\r\n pid: %T" - "\r\n", descP->sock, *pid) ); + "\r\n sock: %d" + "\r\n pid: %T" + "\r\n Closed: %s" + "\r\n Closing: %s" + "\r\n", + descP->sock, *pid, + B2S(IS_CLOSED(descP)), B2S(IS_CLOSING(descP))) ); if (compare_pids(env, &descP->ctrlPid, pid)) { @@ -15249,7 +15288,7 @@ void socket_down(ErlNifEnv* env, * we leave it to the stop callback function. */ - SSDBG( descP, ("SOCKET", "socket_down -> controlling process term\r\n") ); + SSDBG( descP, ("SOCKET", "socket_down -> controlling process exit\r\n") ); descP->state = SOCKET_STATE_CLOSING; descP->closeLocal = TRUE; diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index ff50fd2384..766d3724c1 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1506,7 +1506,7 @@ void esock_warning_msg( const char* format, ... ) { va_list args; char f[512 + sizeof(format)]; // This has to suffice... - char stamp[32]; + char stamp[64]; // Just in case... struct timespec ts; int res; -- cgit v1.2.3 From 6a5013d89a02401b132483711325ca07b8357020 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 18 Oct 2018 16:17:42 +0200 Subject: [socket-nif] Socket close Fixed a number of issues regarding monitor handling. Monitors are now wrapped in its own type (ESockMonitor). This is hopefully temporary! The reason for this is that there is no way to "initialize" a monitor after a demonitor. This could mean that after a successful demonitor, you could still get a down-callback, and thenh compare with, for instance, ctrlMOn field and get a match, even though it was just (successfully) demonitor'ed. To make debugging easier, the monitor and demonitor calls are now wrapped in their own functions, with debug printouts before and in case of failure, also after the actual calls Also, fixed a number of problems with cancel, where monitors where left still active after cleaning up current and active reader, writer and acceptor. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 8 +- erts/emulator/nifs/common/socket_nif.c | 412 ++++++++++++++++++++++++++------- 2 files changed, 329 insertions(+), 91 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index f9246856fa..d89970ecd6 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -216,8 +216,12 @@ extern ERL_NIF_TERM esock_atom_einval; #define MLOCK(M) enif_mutex_lock((M)) #define MUNLOCK(M) enif_mutex_unlock((M)) -#define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) -#define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) +// #define MONP(S,E,D,P,M) enif_monitor_process((E), (D), (P), (M)) +// #define DEMONP(S,E,D,M) enif_demonitor_process((E), (D), (M)) +#define MONP(S,E,D,P,M) esock_monitor((S), (E), (D), (P), (M)) +#define DEMONP(S,E,D,M) esock_demonitor((S), (E), (D), (M)) +#define MON_INIT(M) esock_monitor_init((M)) +// #define MON_COMP(M1, M2) esock_monitor_compare((M1), (M2)) #define SELECT(E,FD,M,O,P,R) \ if (enif_select((E), (FD), (M), (O), (P), (R)) < 0) \ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 3ec19c5a60..5d2cfbc10b 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -725,9 +725,16 @@ static unsigned long one_value = 1; ((sap)->in4.sin_port) : -1) +typedef union { + ErlNifMonitor mon; + uint32_t raw[4]; +} ESockMonitor; + + typedef struct { ErlNifPid pid; // PID of the requesting process - ErlNifMonitor mon; // Monitor to the requesting process + // ErlNifMonitor mon; Monitor to the requesting process + ESockMonitor mon; // Monitor to the requesting process ERL_NIF_TERM ref; // The (unique) reference (ID) of the request } SocketRequestor; @@ -760,7 +767,8 @@ typedef struct { /* +++ Controller (owner) process +++ */ ErlNifPid ctrlPid; - ErlNifMonitor ctrlMon; + // ErlNifMonitor ctrlMon; + ESockMonitor ctrlMon; /* +++ Write stuff +++ */ ErlNifMutex* writeMtx; @@ -803,7 +811,8 @@ typedef struct { /* +++ Close stuff +++ */ ErlNifMutex* closeMtx; ErlNifPid closerPid; - ErlNifMonitor closerMon; + // ErlNifMonitor closerMon; + ESockMonitor closerMon; ERL_NIF_TERM closeRef; BOOLEAN_T closeLocal; @@ -2185,7 +2194,8 @@ static ERL_NIF_TERM acceptor_push(ErlNifEnv* env, static BOOLEAN_T acceptor_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref); static BOOLEAN_T acceptor_unqueue(ErlNifEnv* env, SocketDescriptor* descP, @@ -2201,7 +2211,8 @@ static ERL_NIF_TERM writer_push(ErlNifEnv* env, static BOOLEAN_T writer_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref); static BOOLEAN_T writer_unqueue(ErlNifEnv* env, SocketDescriptor* descP, @@ -2217,7 +2228,8 @@ static ERL_NIF_TERM reader_push(ErlNifEnv* env, static BOOLEAN_T reader_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref); static BOOLEAN_T reader_unqueue(ErlNifEnv* env, SocketDescriptor* descP, @@ -2230,8 +2242,27 @@ static void qpush(SocketRequestQueue* q, SocketRequestQueueElement* e); static SocketRequestQueueElement* qpop(SocketRequestQueue* q); static BOOLEAN_T qunqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const char* slogan, SocketRequestQueue* q, const ErlNifPid* pid); + +static int esock_monitor(const char* slogan, + ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid, + ESockMonitor* mon); +static int esock_demonitor(const char* slogan, + ErlNifEnv* env, + SocketDescriptor* descP, + ESockMonitor* monP); +static void esock_monitor_init(ESockMonitor* mon); +/* +static int esock_monitor_compare(const ErlNifMonitor* mon1, + const ESockMonitor* mon2); +*/ + + /* #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) static size_t my_strnlen(const char *s, size_t maxlen); @@ -2282,6 +2313,7 @@ static BOOLEAN_T extract_iow(ErlNifEnv* env, static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); + #if HAVE_IN6 # if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY # if HAVE_DECL_IN6ADDR_ANY_INIT @@ -2786,7 +2818,6 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, res = enif_make_resource(env, descP); enif_release_resource(descP); // We should really store a reference ... - /* Keep track of the creator * This should not be a problem but just in case * the *open* function is used with the wrong kind @@ -2795,9 +2826,10 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, if (enif_self(env, &descP->ctrlPid) == NULL) return esock_make_error(env, atom_exself); - if (MONP(env, descP, + if (MONP("nopen -> ctrl", + env, descP, &descP->ctrlPid, - &descP->ctrlMon) > 0) + &descP->ctrlMon) != 0) return esock_make_error(env, atom_exmon); @@ -3396,9 +3428,10 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "naccept_listening -> would block\r\n") ); descP->currentAcceptor.pid = caller; - if (MONP(env, descP, + if (MONP("naccept_listening -> current acceptor", + env, descP, &descP->currentAcceptor.pid, - &descP->currentAcceptor.mon) > 0) + &descP->currentAcceptor.mon) != 0) return esock_make_error(env, atom_exmon); descP->currentAcceptor.ref = enif_make_copy(descP->env, ref); @@ -3473,9 +3506,10 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, enif_release_resource(accDescP); // We should really store a reference ... accDescP->ctrlPid = caller; - if (MONP(env, accDescP, + if (MONP("naccept_listening -> ctrl", + env, accDescP, &accDescP->ctrlPid, - &accDescP->ctrlMon) > 0) { + &accDescP->ctrlMon) != 0) { sock_close(accSock); return esock_make_error(env, atom_exmon); } @@ -3589,7 +3623,8 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "naccept_accepting -> accept success\r\n") ); - DEMONP(env, descP, &descP->currentAcceptor.mon); + DEMONP("naccept_accepting -> current acceptor", + env, descP, &descP->currentAcceptor.mon); if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) { save_errno = sock_errno(); @@ -3611,9 +3646,10 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, enif_release_resource(accDescP); // We should really store a reference ... accDescP->ctrlPid = caller; - if (MONP(env, accDescP, + if (MONP("naccept_accepting -> ctrl", + env, accDescP, &accDescP->ctrlPid, - &accDescP->ctrlMon) > 0) { + &accDescP->ctrlMon) != 0) { sock_close(accSock); return esock_make_error(env, atom_exmon); } @@ -4728,7 +4764,11 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, int type = descP->type; int protocol = descP->protocol; - SSDBG( descP, ("SOCKET", "nclose -> [%d] entry\r\n", descP->sock) ); + SSDBG( descP, ("SOCKET", "nclose -> [%d] entry (0x%lX, 0x%lX, 0x%lX)\r\n", + descP->sock, + descP->currentWriterP, + descP->currentReaderP, + descP->currentAcceptorP) ); MLOCK(descP->closeMtx); @@ -4753,11 +4793,18 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, /* Monitor the caller, since we should complete this operation even if * the caller dies (for whatever reason). + * + * + * + * Can we actiually use this for anything? + * + * */ - if (MONP(env, descP, - &descP->closerPid, - &descP->closerMon) > 0) { + if (MONP("nclose -> closer", + env, descP, + &descP->closerPid, + &descP->closerMon) != 0) { MUNLOCK(descP->closeMtx); return esock_make_error(env, atom_exmon); } @@ -4778,6 +4825,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nclose -> [%d] stop was called\r\n", descP->sock) ); dec_socket(domain, type, protocol); + DEMONP("nclose -> closer", env, descP, &descP->closerMon); reply = esock_atom_ok; } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { /* The stop callback function has been *scheduled* which means that we @@ -4803,6 +4851,9 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, * */ + // No point in having this? + DEMONP("nclose -> closer", env, descP, &descP->closerMon); + reason = MKT2(env, atom_select, MKI(env, selectRes)); reply = esock_make_error(env, reason); } @@ -5160,7 +5211,8 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, ERL_NIF_TERM eVal) { ErlNifPid caller, newCtrlPid; - ErlNifMonitor newCtrlMon; + // ErlNifMonitor newCtrlMon; + ESockMonitor newCtrlMon; int xres; SSDBG( descP, @@ -5183,20 +5235,22 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, return esock_make_error(env, esock_atom_einval); } - if ((xres = MONP(env, descP, &newCtrlPid, &newCtrlMon)) != 0) { + if ((xres = MONP("nsetopt_otp_ctrl_proc -> (new) ctrl", + env, descP, &newCtrlPid, &newCtrlMon)) != 0) { esock_warning_msg("Failed monitor %d) (new) controlling process\r\n", xres); return esock_make_error(env, esock_atom_einval); } - if ((xres = DEMONP(env, descP, &descP->ctrlMon)) != 0) { + if ((xres = DEMONP("nsetopt_otp_ctrl_proc -> (old) ctrl", + env, descP, &descP->ctrlMon)) != 0) { esock_warning_msg("Failed demonitor (%d) " "old controlling process %T (%T)\r\n", xres, descP->ctrlPid, descP->ctrlMon); } descP->ctrlPid = newCtrlPid; - descP->ctrlMon = newCtrlMon; - + descP->ctrlMon = newCtrlMon; + SSDBG( descP, ("SOCKET", "nsetopt_otp_ctrl_proc -> done\r\n") ); return esock_atom_ok; @@ -11137,6 +11191,8 @@ ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "ncancel_accept_current -> entry\r\n") ); + DEMONP("ncancel_accept_current -> current acceptor", + env, descP, &descP->currentAcceptor.mon); res = ncancel_read_select(env, descP, descP->currentAcceptor.ref); SSDBG( descP, ("SOCKET", "ncancel_accept_current -> cancel res: %T\r\n", res) ); @@ -11255,6 +11311,8 @@ ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "ncancel_send_current -> entry\r\n") ); + DEMONP("ncancel_recv_current -> current writer", + env, descP, &descP->currentWriter.mon); res = ncancel_write_select(env, descP, descP->currentWriter.ref); SSDBG( descP, ("SOCKET", "ncancel_send_current -> cancel res: %T\r\n", res) ); @@ -11371,6 +11429,8 @@ ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "ncancel_recv_current -> entry\r\n") ); + DEMONP("ncancel_recv_current -> current reader", + env, descP, &descP->currentReader.mon); res = ncancel_read_select(env, descP, descP->currentReader.ref); SSDBG( descP, ("SOCKET", "ncancel_recv_current -> cancel res: %T\r\n", res) ); @@ -11568,7 +11628,8 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, cnt_inc(&descP->writePkgCnt, 1); cnt_inc(&descP->writeByteCnt, written); if (descP->currentWriterP != NULL) - DEMONP(env, descP, &descP->currentWriter.mon); + DEMONP("send_check_result -> current writer", + env, descP, &descP->currentWriter.mon); SSDBG( descP, ("SOCKET", "send_check_result -> " @@ -11607,7 +11668,8 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, if ((saveErrno != EAGAIN) && (saveErrno != EINTR)) { ErlNifPid pid; - ErlNifMonitor mon; + // ErlNifMonitor mon; + ESockMonitor mon; ERL_NIF_TERM ref, res; /* @@ -11622,13 +11684,15 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, res = esock_make_error_errno(env, saveErrno); if (descP->currentWriterP != NULL) { - DEMONP(env, descP, &descP->currentWriter.mon); + DEMONP("send_check_result -> current writer", + env, descP, &descP->currentWriter.mon); while (writer_pop(env, descP, &pid, &mon, &ref)) { SSDBG( descP, ("SOCKET", "send_check_result -> abort %T\r\n", pid) ); send_msg_nif_abort(env, ref, res, &pid); - DEMONP(env, descP, &mon); + DEMONP("send_check_result -> pop'ed writer", + env, descP, &mon); } } @@ -11660,9 +11724,10 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, if (enif_self(env, &caller) == NULL) return esock_make_error(env, atom_exself); descP->currentWriter.pid = caller; - if (MONP(env, descP, + if (MONP("send_check_result -> current writer", + env, descP, &descP->currentWriter.pid, - &descP->currentWriter.mon) > 0) + &descP->currentWriter.mon) != 0) return esock_make_error(env, atom_exmon); descP->currentWriter.ref = enif_make_copy(descP->env, sendRef); descP->currentWriterP = &descP->currentWriter; @@ -11747,9 +11812,10 @@ char* recv_init_current_reader(ErlNifEnv* env, return str_exself; descP->currentReader.pid = caller; - if (MONP(env, descP, + if (MONP("recv_init_current_reader -> current reader", + env, descP, &descP->currentReader.pid, - &descP->currentReader.mon) > 0) { + &descP->currentReader.mon) != 0) { return str_exmon; } descP->currentReader.ref = enif_make_copy(descP->env, recvRef); @@ -11774,7 +11840,8 @@ ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, { if (descP->currentReaderP != NULL) { - DEMONP(env, descP, &descP->currentReader.mon); + DEMONP("recv_update_current_reader -> current reader", + env, descP, &descP->currentReader.mon); if (reader_pop(env, descP, &descP->currentReader.pid, @@ -11822,16 +11889,19 @@ void recv_error_current_reader(ErlNifEnv* env, { if (descP->currentReaderP != NULL) { ErlNifPid pid; - ErlNifMonitor mon; + // ErlNifMonitor mon; + ESockMonitor mon; ERL_NIF_TERM ref; - DEMONP(env, descP, &descP->currentReader.mon); + DEMONP("recv_error_current_reader -> current reader", + env, descP, &descP->currentReader.mon); while (reader_pop(env, descP, &pid, &mon, &ref)) { SSDBG( descP, ("SOCKET", "recv_error_current_reader -> abort %T\r\n", pid) ); send_msg_nif_abort(env, ref, reason, &pid); - DEMONP(env, descP, &mon); + DEMONP("recv_error_current_reader -> pop'ed reader", + env, descP, &mon); } } } @@ -13975,6 +14045,11 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->sock = sock; descP->event = event; + MON_INIT(&descP->currentWriter.mon); + MON_INIT(&descP->currentReader.mon); + MON_INIT(&descP->currentAcceptor.mon); + MON_INIT(&descP->ctrlMon); + MON_INIT(&descP->closerMon); } return descP; @@ -14593,7 +14668,8 @@ ERL_NIF_TERM acceptor_push(ErlNifEnv* env, reqP->pid = pid; reqP->ref = enif_make_copy(descP->env, ref); - if (MONP(env, descP, &pid, &reqP->mon) > 0) { + if (MONP("acceptor_push -> acceptor request", + env, descP, &pid, &reqP->mon) != 0) { FREE(reqP); return esock_make_error(env, atom_exmon); } @@ -14613,7 +14689,8 @@ static BOOLEAN_T acceptor_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref) { SocketRequestQueueElement* e = qpop(&descP->acceptorsQ); @@ -14644,7 +14721,8 @@ BOOLEAN_T acceptor_unqueue(ErlNifEnv* env, SocketDescriptor* descP, const ErlNifPid* pid) { - return qunqueue(env, &descP->acceptorsQ, pid); + return qunqueue(env, descP, "qunqueue -> waiting acceptor", + &descP->acceptorsQ, pid); } @@ -14679,7 +14757,8 @@ ERL_NIF_TERM writer_push(ErlNifEnv* env, reqP->pid = pid; reqP->ref = enif_make_copy(descP->env, ref); - if (MONP(env, descP, &pid, &reqP->mon) > 0) { + if (MONP("writer_push -> writer request", + env, descP, &pid, &reqP->mon) != 0) { FREE(reqP); return esock_make_error(env, atom_exmon); } @@ -14699,7 +14778,8 @@ static BOOLEAN_T writer_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref) { SocketRequestQueueElement* e = qpop(&descP->writersQ); @@ -14730,7 +14810,8 @@ BOOLEAN_T writer_unqueue(ErlNifEnv* env, SocketDescriptor* descP, const ErlNifPid* pid) { - return qunqueue(env, &descP->writersQ, pid); + return qunqueue(env, descP, "qunqueue -> waiting writer", + &descP->writersQ, pid); } @@ -14765,7 +14846,8 @@ ERL_NIF_TERM reader_push(ErlNifEnv* env, reqP->pid = pid; reqP->ref = enif_make_copy(descP->env, ref); - if (MONP(env, descP, &pid, &reqP->mon) > 0) { + if (MONP("reader_push -> reader request", + env, descP, &pid, &reqP->mon) != 0) { FREE(reqP); return esock_make_error(env, atom_exmon); } @@ -14785,7 +14867,8 @@ static BOOLEAN_T reader_pop(ErlNifEnv* env, SocketDescriptor* descP, ErlNifPid* pid, - ErlNifMonitor* mon, + // ErlNifMonitor* mon, + ESockMonitor* mon, ERL_NIF_TERM* ref) { SocketRequestQueueElement* e = qpop(&descP->readersQ); @@ -14816,7 +14899,8 @@ BOOLEAN_T reader_unqueue(ErlNifEnv* env, SocketDescriptor* descP, const ErlNifPid* pid) { - return qunqueue(env, &descP->readersQ, pid); + return qunqueue(env, descP, "qunqueue -> waiting reader", + &descP->readersQ, pid); } @@ -14880,6 +14964,8 @@ SocketRequestQueueElement* qpop(SocketRequestQueue* q) static BOOLEAN_T qunqueue(ErlNifEnv* env, + SocketDescriptor* descP, + const char* slogan, SocketRequestQueue* q, const ErlNifPid* pid) { @@ -14892,6 +14978,8 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, /* We have a match */ + DEMONP(slogan, env, descP, &e->data.mon); + if (p != NULL) { /* Not the first, but could be the last */ if (q->last == e) { @@ -14965,6 +15053,97 @@ void cnt_dec(uint32_t* cnt, uint32_t dec) +/* ---------------------------------------------------------------------- + * M o n i t o r W r a p p e r F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +static +int esock_monitor(const char* slogan, + ErlNifEnv* env, + SocketDescriptor* descP, + const ErlNifPid* pid, + ESockMonitor* monP) +{ + int res; + + SSDBG( descP, ("SOCKET", "[%d] %s: try monitor", descP->sock, slogan) ); + // esock_dbg_printf("MONP", "[%d] %s\r\n", descP->sock, slogan); + res = enif_monitor_process(env, descP, pid, &monP->mon); + + if (res != 0) { + SSDBG( descP, ("SOCKET", "[%d] monitor failed: %d", descP->sock, res) ); + // esock_dbg_printf("MONP", "[%d] failed: %d\r\n", descP->sock, res); + } /* else { + esock_dbg_printf("MONP", + "[%d] success: " + "%u,%u,%u,%u\r\n", + descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + } */ + + return res; +} + + +static +int esock_demonitor(const char* slogan, + ErlNifEnv* env, + SocketDescriptor* descP, + ESockMonitor* monP) +{ + int res; + + SSDBG( descP, ("SOCKET", "[%d] %s: try demonitor\r\n", descP->sock, slogan) ); + /* + esock_dbg_printf("DEMONP", "[%d] %s: %u,%u,%u,%u\r\n", + descP->sock, slogan, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + */ + + res = enif_demonitor_process(env, descP, &monP->mon); + + if (res == 0) { + esock_monitor_init(monP); + } else { + SSDBG( descP, + ("SOCKET", "[%d] demonitor failed: %d\r\n", descP->sock, res) ); + /* + esock_dbg_printf("DEMONP", "[%d] failed: %d\r\n", descP->sock, res); + */ + } + + return res; +} + + +static +void esock_monitor_init(ESockMonitor* monP) +{ + int i; + + /* + * UGLY, + * but since we don't have a ERL_NIF_MONITOR_NULL, + * this will have to do for now... + */ + for (i = 0; i < 4; i++) + monP->raw[i] = 0; + +} + +/* +static +int esock_monitor_compare(const ErlNifMonitor* mon1, + const ESockMonitor* mon2) +{ + return enif_compare_monitors(mon1, &mon2->mon); +} +*/ + + /* ---------------------------------------------------------------------- * C a l l b a c k F u n c t i o n s * ---------------------------------------------------------------------- @@ -15015,13 +15194,15 @@ static void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) { SocketDescriptor* descP = (SocketDescriptor*) obj; - int dres; - + + /* + esock_dbg_printf("STOP", "[%d] begin\r\n", descP->sock); + */ + SSDBG( descP, - ("SOCKET", "socket_stop -> entry when" - "\r\n sock: %d (%d)" - "\r\n Is Direct Call: %s" - "\r\n", descP->sock, fd, B2S(is_direct_call)) ); + ("SOCKET", "socket_stop -> entry when %s" + "\r\n sock: %d (%d)" + "\r\n", ((is_direct_call) ? "called" : "scheduled"), descP->sock, fd) ); MLOCK(descP->writeMtx); MLOCK(descP->readMtx); @@ -15033,8 +15214,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) descP->state = SOCKET_STATE_CLOSING; // Just in case...??? descP->isReadable = FALSE; descP->isWritable = FALSE; - - + /* We should check that we actually have a monitor. * This *should* be done with a "NULL" monitor value, * which there currently is none... @@ -15042,24 +15222,19 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * its no point in demonitor. Also, we not actually have * a monitor in that case... */ - SSDBG( descP, - ("SOCKET", - "socket_stop -> demonitor (maybe) controlling process (%T)\r\n", - descP->ctrlPid) ); - dres = DEMONP(env, descP, &descP->ctrlMon); - SSDBG( descP, ("SOCKET", "socket_stop -> demonitor result: %d\r\n", dres) ); - - + DEMONP("socket_stop -> ctrl", env, descP, &descP->ctrlMon); + + /* + esock_dbg_printf("STOP", "[%d] maybe handle current writer (0x%lX)\r\n", + descP->sock, descP->currentReaderP); + */ if (descP->currentWriterP != NULL) { /* We have a (current) writer and *may* therefor also have * writers waiting. */ - SSDBG( descP, - ("SOCKET", - "socket_stop -> demonitor (maybe) current writer: %T, %T\r\n", - descP->currentWriter.pid, descP->currentWriter.ref) ); - DEMONP(env, descP, &descP->currentWriter.mon); + DEMONP("socket_stop -> current writer", + env, descP, &descP->currentWriter.mon); SSDBG( descP, ("SOCKET", "socket_stop -> handle current writer\r\n") ); if (!compare_pids(env, @@ -15082,18 +15257,19 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting writer(s)\r\n") ); inform_waiting_procs(env, descP, &descP->writersQ, TRUE, atom_closed); } - + + /* + esock_dbg_printf("STOP", "[%d] maybe handle current reader (0x%lX)\r\n", + descP->sock, descP->currentReaderP); + */ if (descP->currentReaderP != NULL) { /* We have a (current) reader and *may* therefor also have * readers waiting. */ - SSDBG( descP, - ("SOCKET", - "socket_stop -> demonitor (maybe) current reader: %T, %T\r\n", - descP->currentReader.pid, descP->currentReader.ref) ); - DEMONP(env, descP, &descP->currentReader.mon); + DEMONP("socket_stop -> current reader", + env, descP, &descP->currentReader.mon); SSDBG( descP, ("SOCKET", "socket_stop -> handle current reader\r\n") ); if (!compare_pids(env, @@ -15116,17 +15292,18 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting reader(s)\r\n") ); inform_waiting_procs(env, descP, &descP->readersQ, TRUE, atom_closed); } - + + /* + esock_dbg_printf("STOP", "[%d] maybe handle current acceptor (0x%lX)\r\n", + descP->sock, descP->currentReaderP); + */ if (descP->currentAcceptorP != NULL) { /* We have a (current) acceptor and *may* therefor also have * acceptors waiting. */ - SSDBG( descP, - ("SOCKET", - "socket_stop -> demonitor (maybe) current acceptor: %T, %T\r\n", - descP->currentAcceptor.pid, descP->currentAcceptor.ref) ); - DEMONP(env, descP, &descP->currentAcceptor.mon); + DEMONP("socket_stop -> current acceptor", + env, descP, &descP->currentAcceptor.mon); SSDBG( descP, ("SOCKET", "socket_stop -> handle current acceptor\r\n") ); if (!compare_pids(env, @@ -15178,7 +15355,8 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) send_msg(env, MKT2(env, atom_close, descP->closeRef), &descP->closerPid); - DEMONP(env, descP, &descP->closerMon); + DEMONP("socket_stop -> closer", + env, descP, &descP->closerMon); } else { @@ -15194,6 +15372,10 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) if (!is_direct_call) { + if (descP->closeLocal) { + DEMONP("socket_stop -> closer", + env, descP, &descP->closerMon); + } descP->sock = INVALID_SOCKET; descP->event = INVALID_EVENT; descP->state = SOCKET_STATE_CLOSED; @@ -15206,6 +15388,10 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) MUNLOCK(descP->readMtx); MUNLOCK(descP->writeMtx); + /* + esock_dbg_printf("STOP", "[%d] end\r\n", descP->sock); + */ + SSDBG( descP, ("SOCKET", "socket_stop -> done (%d, %d)\r\n", descP->sock, fd) ); @@ -15245,7 +15431,8 @@ void inform_waiting_procs(ErlNifEnv* env, currentP->data.ref, reason, ¤tP->data.pid)) ); - DEMONP(env, descP, ¤tP->data.mon); + DEMONP("inform_waiting_procs -> current 'request'", + env, descP, ¤tP->data.mon); nextP = currentP->nextP; if (free) FREE(currentP); currentP = nextP; @@ -15270,16 +15457,43 @@ void socket_down(ErlNifEnv* env, const ErlNifMonitor* mon) { SocketDescriptor* descP = (SocketDescriptor*) obj; + ESockMonitor* monP = (ESockMonitor*) mon; SSDBG( descP, ("SOCKET", "socket_down -> entry with" - "\r\n sock: %d" - "\r\n pid: %T" - "\r\n Closed: %s" - "\r\n Closing: %s" + "\r\n sock: %d" + "\r\n pid: %T" + "\r\n Close: %s (%s)" "\r\n", descP->sock, *pid, - B2S(IS_CLOSED(descP)), B2S(IS_CLOSING(descP))) ); + B2S(IS_CLOSED(descP)), + B2S(IS_CLOSING(descP))) ); + + /* + esock_dbg_printf("DOWN", + "[%d] begin %u,%u,%u,%d\r\n", + descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + */ + /* + if (MON_COMP(mon, &descP->ctrlMon) == 0) { + SSDBG( descP, ("SOCKET", "socket_down -> controlling process mon\r\n") ); + } else if (MON_COMP(mon, &descP->closerMon) == 0) { + SSDBG( descP, ("SOCKET", "socket_down -> closer mon\r\n") ); + } else if ((descP->currentWriterP != NULL) && + (MON_COMP(mon, &descP->currentWriter.mon) == 0)) { + SSDBG( descP, ("SOCKET", "socket_down -> current writer mon\r\n") ); + } else if ((descP->currentReaderP != NULL) && + (MON_COMP(mon, &descP->currentReader.mon) == 0)) { + SSDBG( descP, ("SOCKET", "socket_down -> current reader mon\r\n") ); + } else if ((descP->currentAcceptorP != NULL) && + (MON_COMP(mon, &descP->currentAcceptor.mon) == 0)) { + SSDBG( descP, ("SOCKET", "socket_down -> current acceptor mon\r\n") ); + } else { + SSDBG( descP, ("SOCKET", "socket_down -> OTHER mon\r\n") ); + } + */ if (compare_pids(env, &descP->ctrlPid, pid)) { int selectRes; @@ -15288,14 +15502,23 @@ void socket_down(ErlNifEnv* env, * we leave it to the stop callback function. */ - SSDBG( descP, ("SOCKET", "socket_down -> controlling process exit\r\n") ); + SSDBG( descP, + ("SOCKET", "socket_down -> controlling process exit\r\n") ); descP->state = SOCKET_STATE_CLOSING; descP->closeLocal = TRUE; descP->closerPid = *pid; - descP->closerMon = *mon; + descP->closerMon = (ESockMonitor) *mon; descP->closeRef = MKREF(env); // Do we really need this in this case? + /* + esock_dbg_printf("DOWN", + "[%d] select stop %u,%u,%u,%d\r\n", + descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + */ + selectRes = enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), descP, NULL, descP->closeRef); @@ -15374,7 +15597,10 @@ void socket_down(ErlNifEnv* env, "\r\n Select Res: %d" "\r\n Controlling Process: %T" "\r\n Descriptor: %d" - "\r\n", selectRes, pid, descP->sock); + "\r\n Monitor: %u.%u.%u.%u" + "\r\n", selectRes, pid, descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); } @@ -15400,6 +15626,14 @@ void socket_down(ErlNifEnv* env, MUNLOCK(descP->readMtx); } + + /* + esock_dbg_printf("DOWN", + "[%d] end %u,%u,%u,%d\r\n", + descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + */ SSDBG( descP, ("SOCKET", "socket_down -> done\r\n") ); -- cgit v1.2.3 From 3eb4d5160f6a9f7cca799c97daf33b1a8b154f5d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 18 Oct 2018 17:18:10 +0200 Subject: [socket-nif] Initiation of "current reader" missing for recvmsg When calling the recvmsg function when there is no data, we should be wait (for the specified amount of time). But this not not wotk because the "current reader" structure was not initiated. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 5d2cfbc10b..2eddcb5658 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -12334,9 +12334,13 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, } else if ((saveErrno == ERRNO_BLOCK) || (saveErrno == EAGAIN)) { + char* xres; SSDBG( descP, ("SOCKET", "recvmsg_check_result -> eagain\r\n") ); + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) + return esock_make_error_str(env, xres); + SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); -- cgit v1.2.3 From 466470070f7f22f5a1b42daf66390adfedd2cc28 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 18 Oct 2018 18:33:51 +0200 Subject: [socket-nif] setopt and getopt with level ipv6 The level argument for IPv6 can be either SOL_IPV6 or IPPROTO_IPV6, whichever is defined. But it was hard- coded to SOL_IPV6, which is not defined on FreeBSD. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 244 ++++++++++++++++++++++++++++----- 1 file changed, 212 insertions(+), 32 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index a137692d68..45462ff772 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -8533,7 +8533,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_DSTOPTS, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_bool_opt(env, descP, level, IPV6_DSTOPTS, eVal); } #endif @@ -8544,7 +8550,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_FLOWINFO, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_bool_opt(env, descP, level, IPV6_FLOWINFO, eVal); } #endif @@ -8555,7 +8567,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_HOPLIMIT, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_bool_opt(env, descP, level, IPV6_HOPLIMIT, eVal); } #endif @@ -8566,7 +8584,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_HOPOPTS, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_bool_opt(env, descP, level, IPV6_HOPOPTS, eVal); } #endif @@ -8577,7 +8601,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_MTU, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_int_opt(env, descP, level, IPV6_MTU, eVal); } #endif @@ -8602,8 +8632,14 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, result = esock_make_error_str(env, xres); } else { +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + - res = socket_setopt(descP->sock, SOL_IPV6, IPV6_MTU_DISCOVER, + res = socket_setopt(descP->sock, level, IPV6_MTU_DISCOVER, &val, sizeof(val)); if (res != 0) @@ -8624,7 +8660,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_HOPS, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_int_opt(env, descP, level, IPV6_MULTICAST_HOPS, eVal); } #endif @@ -8636,7 +8678,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_IF, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_int_opt(env, descP, level, IPV6_MULTICAST_IF, eVal); } #endif @@ -8648,7 +8696,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_LOOP, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_bool_opt(env, descP, level, IPV6_MULTICAST_LOOP, eVal); } #endif @@ -8659,7 +8713,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_RECVERR, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_bool_opt(env, descP, level, IPV6_RECVERR, eVal); } #endif @@ -8670,13 +8730,18 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif #if defined(IPV6_RECVPKTINFO) int opt = IPV6_RECVPKTINFO; #else int opt = IPV6_PKTINFO; #endif - return nsetopt_bool_opt(env, descP, SOL_IPV6, opt, eVal); + return nsetopt_bool_opt(env, descP, level, opt, eVal); } #endif @@ -8687,7 +8752,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_ROUTER_ALERT, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_int_opt(env, descP, level, IPV6_ROUTER_ALERT, eVal); } #endif @@ -8699,7 +8770,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_RTHDR, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_bool_opt(env, descP, level, IPV6_RTHDR, eVal); } #endif @@ -8710,7 +8787,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_int_opt(env, descP, SOL_IPV6, IPV6_UNICAST_HOPS, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_int_opt(env, descP, level, IPV6_UNICAST_HOPS, eVal); } #endif @@ -8722,7 +8805,13 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - return nsetopt_bool_opt(env, descP, SOL_IPV6, IPV6_V6ONLY, eVal); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return nsetopt_bool_opt(env, descP, level, IPV6_V6ONLY, eVal); } #endif @@ -8739,7 +8828,11 @@ ERL_NIF_TERM nsetopt_lvl_ipv6_update_membership(ErlNifEnv* env, char* xres; int res; size_t sz; +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else int level = IPPROTO_IPV6; +#endif // It must be a map if (!IS_MAP(env, eVal)) @@ -11581,7 +11674,12 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_DSTOPTS); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + return ngetopt_bool_opt(env, descP, level, IPV6_DSTOPTS); } #endif @@ -11591,7 +11689,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_FLOWINFO); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_FLOWINFO); } #endif @@ -11601,7 +11705,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_HOPLIMIT); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_HOPLIMIT); } #endif @@ -11611,7 +11721,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_HOPOPTS); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_HOPOPTS); } #endif @@ -11621,7 +11737,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_MTU); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_int_opt(env, descP, level, IPV6_MTU); } #endif @@ -11638,8 +11760,13 @@ ERL_NIF_TERM ngetopt_lvl_ipv6_mtu_discover(ErlNifEnv* env, int mtuDisc; SOCKOPTLEN_T mtuDiscSz = sizeof(mtuDisc); int res; +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif - res = sock_getopt(descP->sock, SOL_IPV6, IPV6_MTU_DISCOVER, + res = sock_getopt(descP->sock, level, IPV6_MTU_DISCOVER, &mtuDisc, &mtuDiscSz); if (res != 0) { @@ -11660,7 +11787,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_HOPS); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_int_opt(env, descP, level, IPV6_MULTICAST_HOPS); } #endif @@ -11670,7 +11803,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_IF); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_int_opt(env, descP, level, IPV6_MULTICAST_IF); } #endif @@ -11680,7 +11819,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_MULTICAST_LOOP); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_MULTICAST_LOOP); } #endif @@ -11690,7 +11835,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_RECVERR); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_RECVERR); } #endif @@ -11700,13 +11851,18 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_recvpktinfo(ErlNifEnv* env, SocketDescriptor* descP) { +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif #if defined(IPV6_RECVPKTINFO) - int opt = IPV6_RECVPKTINFO; + int opt = IPV6_RECVPKTINFO; #else - int opt = IPV6_PKTINFO; + int opt = IPV6_PKTINFO; #endif - return ngetopt_bool_opt(env, descP, SOL_IPV6, opt); + return ngetopt_bool_opt(env, descP, level, opt); } #endif @@ -11716,7 +11872,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_ROUTER_ALERT); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_int_opt(env, descP, level, IPV6_ROUTER_ALERT); } #endif @@ -11726,7 +11888,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_RTHDR); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_RTHDR); } #endif @@ -11736,7 +11904,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_int_opt(env, descP, SOL_IPV6, IPV6_UNICAST_HOPS); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_int_opt(env, descP, level, IPV6_UNICAST_HOPS); } #endif @@ -11746,7 +11920,13 @@ static ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, SocketDescriptor* descP) { - return ngetopt_bool_opt(env, descP, SOL_IPV6, IPV6_V6ONLY); +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_V6ONLY); } #endif -- cgit v1.2.3 From ada0029d11c33cef2b2aabc9ef2902a98f574ffb Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 22 Oct 2018 14:19:29 +0200 Subject: [socket-nif] Initiation of "current reader" missing for recvfrom When calling the recvfrom function when there is no data, we should be made wait (for the specified amount of time). But this did not work because the "current reader" structure was not initiated. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 45462ff772..f9eb041ad1 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -13377,21 +13377,24 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env, } if (!compare_pids(env, &descP->currentReader.pid, &caller)) { + ERL_NIF_TERM tmp; + /* Not the "current reader", so (maybe) push onto queue */ SSDBG( descP, ("SOCKET", "recv_check_reader -> not (current) reader\r\n") ); if (!reader_search4pid(env, descP, &caller)) - *checkResult = reader_push(env, descP, caller, ref); + tmp = reader_push(env, descP, caller, ref); else - *checkResult = esock_make_error(env, esock_atom_eagain); + tmp = esock_make_error(env, esock_atom_eagain); SSDBG( descP, ("SOCKET", - "recv_check_reader -> queue (push) result: %T\r\n", - checkResult) ); - + "recv_check_reader -> queue (push) result: %T\r\n", tmp) ); + + *checkResult = tmp; + return FALSE; } @@ -13769,6 +13772,7 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, unsigned int fromAddrLen, ERL_NIF_TERM recvRef) { + char* xres; ERL_NIF_TERM data; SSDBG( descP, @@ -13818,6 +13822,9 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recvfrom_check_result -> eagain\r\n") ); + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) + return esock_make_error_str(env, xres); + SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); -- cgit v1.2.3 From 3f46e8a184a503ead01674ee180e7222b3928712 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 30 Oct 2018 18:26:10 +0100 Subject: [socket-nif] Add a send and receive chunks test case The send and recv test case triggered a two bugs. One was that there was no re-selecting when only a portion of the data was received (which meant that we stopped reading). Also, the wrong 'current' (writer) was reset when demonitor current reader after a successful read (which meant that future readers would never have been monitored). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f9eb041ad1..27395b5cf6 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -13479,7 +13479,7 @@ ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, descP->currentReader.ref); } else { - descP->currentWriterP = NULL; + descP->currentReaderP = NULL; } } @@ -13744,14 +13744,23 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, } else { /* +++ We got only a part of what was expected +++ - * +++ => receive more later. +++ */ + * +++ => select for more more later and +++ + * +++ deliver what we got. +++ */ SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] " "only part of message - expect more\r\n", toRead) ); + /* SELECT for more data */ + + SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), + descP, NULL, recvRef); + cnt_inc(&descP->readByteCnt, read); - return esock_make_ok3(env, atom_false, MKBIN(env, bufP)); + data = MKBIN(env, bufP); + data = MKSBIN(env, data, 0, read); + + return esock_make_ok3(env, atom_false, data); } } } @@ -16709,7 +16718,7 @@ int esock_monitor(const char* slogan, int res; SSDBG( descP, ("SOCKET", "[%d] %s: try monitor", descP->sock, slogan) ); - // esock_dbg_printf("MONP", "[%d] %s\r\n", descP->sock, slogan); + /* esock_dbg_printf("MONP", "[%d] %s\r\n", descP->sock, slogan); */ res = enif_monitor_process(env, descP, pid, &monP->mon); if (res != 0) { @@ -16722,7 +16731,7 @@ int esock_monitor(const char* slogan, descP->sock, monP->raw[0], monP->raw[1], monP->raw[2], monP->raw[3]); - } */ + } */ return res; } @@ -16737,6 +16746,7 @@ int esock_demonitor(const char* slogan, int res; SSDBG( descP, ("SOCKET", "[%d] %s: try demonitor\r\n", descP->sock, slogan) ); + /* esock_dbg_printf("DEMONP", "[%d] %s: %u,%u,%u,%u\r\n", descP->sock, slogan, -- cgit v1.2.3 From 0c6a8375990e491405e2282e5e038d384727f1e2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 1 Nov 2018 18:30:09 +0100 Subject: [sock-nif|test] Add a ping-pong test case We got some kind of send hang... --- erts/emulator/nifs/common/socket_nif.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 27395b5cf6..389d43ee42 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -13314,12 +13314,6 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "send_check_result -> try again\r\n") ); - /* - * SHOULD RESULT IN {error, eagain}!!!! - * - */ - written = 0; - } } @@ -13349,7 +13343,8 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, descP, NULL, sendRef); SSDBG( descP, - ("SOCKET", "send_check_result -> not entire package written\r\n") ); + ("SOCKET", "send_check_result -> " + "not entire package written (%d of %d)\r\n", written, dataSize) ); return esock_make_ok2(env, MKI(env, written)); @@ -13687,15 +13682,26 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, } else if ((saveErrno == ERRNO_BLOCK) || (saveErrno == EAGAIN)) { + int sres; + SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] eagain\r\n", toRead) ); if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) return esock_make_error_str(env, xres); + SSDBG( descP, ("SOCKET", "recv_check_result -> SELECT for more\r\n") ); + + /* SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); + */ + sres = enif_select(env, descP->sock, (ERL_NIF_SELECT_READ), + descP, NULL, recvRef); + + SSDBG( descP, ("SOCKET", "recv_check_result -> SELECT res: %d\r\n", sres) ); + return esock_make_error(env, esock_atom_eagain); } else { ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); -- cgit v1.2.3 From d0df643ad994bbc609c52f83b814fc79624af501 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 2 Nov 2018 14:14:48 +0100 Subject: [socket-nif] Inherit buffer sizes when accepting An "accepted" socket will inherit the parent (listen) socket's buffer sizes (rBufSz, rCtrlSz and wCtrlSz). OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 389d43ee42..82bf8305fc 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -4919,6 +4919,9 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, accDescP->domain = descP->domain; accDescP->type = descP->type; accDescP->protocol = descP->protocol; + accDescP->rBufSz = descP->rBufSz; // Inherit buffer size + accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer siez + accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size accRef = enif_make_resource(env, accDescP); enif_release_resource(accDescP); // We should really store a reference ... @@ -9549,7 +9552,17 @@ ERL_NIF_TERM nsetopt_int_opt(ErlNifEnv* env, int val; if (GET_INT(env, eVal, &val)) { - int res = socket_setopt(descP->sock, level, opt, &val, sizeof(val)); + int res; + + /* + SSDBG( descP, + ("SOCKET", "nsetopt_int_opt -> set option" + "\r\n opt: %d" + "\r\n val: %d" + "\r\n", opt, val) ); + */ + + res = socket_setopt(descP->sock, level, opt, &val, sizeof(val)); if (res != 0) result = esock_make_error_errno(env, sock_errno()); -- cgit v1.2.3 From 6fcbb97c0b7f082c4934d7765c6b63222f317ef2 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 5 Nov 2018 14:37:39 +0100 Subject: [socket-nif] Make it possible to send (nif) debug to file Make it possible to open a file and send debug printouts to (instead of stdout) for debug printouts from the nif-code. OTP-14831 --- erts/emulator/nifs/common/socket_dbg.c | 24 +++++++++++++++++++++++- erts/emulator/nifs/common/socket_dbg.h | 8 ++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_dbg.c b/erts/emulator/nifs/common/socket_dbg.c index dd11fbca9b..fe9135e5a0 100644 --- a/erts/emulator/nifs/common/socket_dbg.c +++ b/erts/emulator/nifs/common/socket_dbg.c @@ -36,10 +36,32 @@ #define TNAME(__T__) enif_thread_name( __T__ ) #define TSNAME() TNAME(TSELF()) +static FILE* dbgout = NULL; + static int realtime(struct timespec* tsP); static int timespec2str(char *buf, unsigned int len, struct timespec *ts); +extern +void esock_dbg_init(char* filename) +{ + if (filename != NULL) { + if (strcmp(filename, ESOCK_DBGOUT_DEFAULT) == 0) { + dbgout = stdout; + } else if (strcmp(filename, ESOCK_DBGOUT_UNIQUE) == 0) { + char template[] = "/tmp/esock-dbg-XXXXXX"; + dbgout = fdopen(mkstemp(template), "w+"); + } else { + dbgout = fopen(filename, "w+"); + } + } else { + char template[] = "/tmp/esock-dbg-XXXXXX"; + dbgout = fdopen(mkstemp(template), "w+"); + } +} + + + /* * Print a debug format string *with* both a timestamp and the * the name of the *current* thread. @@ -70,7 +92,7 @@ void esock_dbg_printf( const char* prefix, const char* format, ... ) if (res > 0) { va_start (args, format); - enif_vfprintf (stdout, f, args); + enif_vfprintf (dbgout, f, args); va_end (args); fflush(stdout); } diff --git a/erts/emulator/nifs/common/socket_dbg.h b/erts/emulator/nifs/common/socket_dbg.h index ad0fcdada9..47739b46da 100644 --- a/erts/emulator/nifs/common/socket_dbg.h +++ b/erts/emulator/nifs/common/socket_dbg.h @@ -27,6 +27,10 @@ #ifndef SOCKET_DBG_H__ #define SOCKET_DBG_H__ +/* Used when calling the init function */ +#define ESOCK_DBGOUT_DEFAULT "stdout" +#define ESOCK_DBGOUT_UNIQUE "unique" + /* Used in debug printouts */ #ifdef __WIN32__ @@ -45,7 +49,7 @@ typedef unsigned long long llu_t; } -extern -void esock_dbg_printf( const char* prefix, const char* format, ... ); +extern void esock_dbg_init(char* filename); +extern void esock_dbg_printf( const char* prefix, const char* format, ... ); #endif // SOCKET_DBG_H__ -- cgit v1.2.3 From 868950ba50185d68075e0eb14708beb5a7a5a63f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 5 Nov 2018 14:40:14 +0100 Subject: [socket-nif] Sending when buffer is full failed When the send buffer was full (eagain), the send failed (with the rather useless return of {ok, -1}) instead of returning {error, eagain}. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 82bf8305fc..70a969e867 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -5583,7 +5583,7 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, if (IS_SOCKET_ERROR(written)) save_errno = sock_errno(); else - save_errno = -1; // The value does not actually matter in this case + save_errno = -1; // OK or not complete: this value should not matter in this case res = send_check_result(env, descP, written, dataSize, save_errno, sendRef); @@ -13327,7 +13327,12 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "send_check_result -> try again\r\n") ); + SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), descP, NULL, sendRef); + + return esock_make_error(env, esock_atom_eagain); + } + } /* We failed to write the *entire* packet (anything less then size @@ -13352,8 +13357,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, cnt_inc(&descP->writeWaits, 1); - SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), - descP, NULL, sendRef); + SELECT(env, descP->sock, (ERL_NIF_SELECT_WRITE), descP, NULL, sendRef); SSDBG( descP, ("SOCKET", "send_check_result -> " @@ -16736,12 +16740,12 @@ int esock_monitor(const char* slogan, { int res; - SSDBG( descP, ("SOCKET", "[%d] %s: try monitor", descP->sock, slogan) ); + SSDBG( descP, ("SOCKET", "[%d] %s: try monitor\r\n", descP->sock, slogan) ); /* esock_dbg_printf("MONP", "[%d] %s\r\n", descP->sock, slogan); */ res = enif_monitor_process(env, descP, pid, &monP->mon); if (res != 0) { - SSDBG( descP, ("SOCKET", "[%d] monitor failed: %d", descP->sock, res) ); + SSDBG( descP, ("SOCKET", "[%d] monitor failed: %d\r\n", descP->sock, res) ); // esock_dbg_printf("MONP", "[%d] failed: %d\r\n", descP->sock, res); } /* else { esock_dbg_printf("MONP", @@ -17595,6 +17599,9 @@ BOOLEAN_T extract_iow(ErlNifEnv* env, static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { + esock_dbg_init(ESOCK_DBGOUT_DEFAULT); + // esock_dbg_init(ESOCK_DBGOUT_UNIQUE); + data.dbg = extract_debug(env, load_info); data.iow = extract_iow(env, load_info); -- cgit v1.2.3 From c6e94046261a608ee536c18cf631e33191e71bb1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 6 Nov 2018 15:18:31 +0100 Subject: [socket-nif] Badly handled socket close for recvfrom and recvmsg When type = dgram, the functions recvfrom and recvmsg did not properly handle socket close, cuaing the caller to hang indefinitely. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 111 +++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 40 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 70a969e867..f657da3ace 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -13825,6 +13825,7 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, /* +++ Error handling +++ */ if (saveErrno == ECONNRESET) { + ERL_NIF_TERM res = esock_make_error(env, atom_closed); /* +++ Oups - closed +++ */ @@ -13842,12 +13843,14 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, descP->closeLocal = FALSE; descP->state = SOCKET_STATE_CLOSING; + recv_error_current_reader(env, descP, res); + SELECT(env, descP->sock, (ERL_NIF_SELECT_STOP), descP, NULL, recvRef); - return esock_make_error(env, atom_closed); + return res; } else if ((saveErrno == ERRNO_BLOCK) || (saveErrno == EAGAIN)) { @@ -13862,12 +13865,15 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, return esock_make_error(env, esock_atom_eagain); } else { + ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); SSDBG( descP, ("SOCKET", "recvfrom_check_result -> errno: %d\r\n", saveErrno) ); - return esock_make_error_errno(env, saveErrno); + recv_error_current_reader(env, descP, res); + + return res; } } else { @@ -13894,6 +13900,8 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, data = MKSBIN(env, data, 0, read); } + recv_update_current_reader(env, descP); + return esock_make_ok2(env, MKT2(env, eSockAddr, data)); } @@ -13957,6 +13965,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, /* +++ Error handling +++ */ if (saveErrno == ECONNRESET) { + ERL_NIF_TERM res = esock_make_error(env, atom_closed); /* +++ Oups - closed +++ */ @@ -13974,12 +13983,14 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, descP->closeLocal = FALSE; descP->state = SOCKET_STATE_CLOSING; + recv_error_current_reader(env, descP, res); + SELECT(env, descP->sock, (ERL_NIF_SELECT_STOP), descP, NULL, recvRef); - return esock_make_error(env, atom_closed); + return res; } else if ((saveErrno == ERRNO_BLOCK) || (saveErrno == EAGAIN)) { @@ -13995,12 +14006,15 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, return esock_make_error(env, esock_atom_eagain); } else { + ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); SSDBG( descP, ("SOCKET", "recvmsg_check_result -> errno: %d\r\n", saveErrno) ); - return esock_make_error_errno(env, saveErrno); + recv_error_current_reader(env, descP, res); + + return res; } } else { @@ -14029,6 +14043,8 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, "recvmsg_check_result -> " "(msghdr) encode failed: %s\r\n", xres) ); + recv_update_current_reader(env, descP); + return esock_make_error_str(env, xres); } else { @@ -14037,6 +14053,8 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, "recvmsg_check_result -> " "(msghdr) encode ok: %T\r\n", eMsgHdr) ); + recv_update_current_reader(env, descP); + return esock_make_ok2(env, eMsgHdr); } @@ -16913,18 +16931,22 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> handle current writer\r\n") ); if (!compare_pids(env, &descP->closerPid, - &descP->currentWriter.pid) && - send_msg_nif_abort(env, - descP->currentWriter.ref, - atom_closed, - &descP->currentWriter.pid) != NULL) { - /* Shall we really do this? - * This happens if the controlling process has been killed! - */ - esock_warning_msg("Failed sending abort (%T) message to " - "current writer %T\r\n", - descP->currentWriter.ref, - descP->currentWriter.pid); + &descP->currentWriter.pid)) { + SSDBG( descP, ("SOCKET", "socket_stop -> " + "send abort message to current writer %T\r\n", + descP->currentWriter.pid) ); + if (send_msg_nif_abort(env, + descP->currentWriter.ref, + atom_closed, + &descP->currentWriter.pid) != NULL) { + /* Shall we really do this? + * This happens if the controlling process has been killed! + */ + esock_warning_msg("Failed sending abort (%T) message to " + "current writer %T\r\n", + descP->currentWriter.ref, + descP->currentWriter.pid); + } } /* And also deal with the waiting writers (in the same way) */ @@ -16948,18 +16970,22 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> handle current reader\r\n") ); if (!compare_pids(env, &descP->closerPid, - &descP->currentReader.pid) && - send_msg_nif_abort(env, - descP->currentReader.ref, - atom_closed, - &descP->currentReader.pid) != NULL) { - /* Shall we really do this? - * This happens if the controlling process has been killed! - */ - esock_warning_msg("Failed sending abort (%T) message to " - "current reader %T\r\n", - descP->currentReader.ref, - descP->currentReader.pid); + &descP->currentReader.pid)) { + SSDBG( descP, ("SOCKET", "socket_stop -> " + "send abort message to current reader %T\r\n", + descP->currentReader.pid) ); + if (send_msg_nif_abort(env, + descP->currentReader.ref, + atom_closed, + &descP->currentReader.pid) != NULL) { + /* Shall we really do this? + * This happens if the controlling process has been killed! + */ + esock_warning_msg("Failed sending abort (%T) message to " + "current reader %T\r\n", + descP->currentReader.ref, + descP->currentReader.pid); + } } /* And also deal with the waiting readers (in the same way) */ @@ -16982,18 +17008,22 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> handle current acceptor\r\n") ); if (!compare_pids(env, &descP->closerPid, - &descP->currentAcceptor.pid) && - send_msg_nif_abort(env, - descP->currentAcceptor.ref, - atom_closed, - &descP->currentAcceptor.pid) != NULL) { - /* Shall we really do this? - * This happens if the controlling process has been killed! - */ - esock_warning_msg("Failed sending abort (%T) message to " - "current acceptor %T\r\n", - descP->currentAcceptor.ref, - descP->currentAcceptor.pid); + &descP->currentAcceptor.pid)) { + SSDBG( descP, ("SOCKET", "socket_stop -> " + "send abort message to current acceptor %T\r\n", + descP->currentWriter.pid) ); + if (send_msg_nif_abort(env, + descP->currentAcceptor.ref, + atom_closed, + &descP->currentAcceptor.pid) != NULL) { + /* Shall we really do this? + * This happens if the controlling process has been killed! + */ + esock_warning_msg("Failed sending abort (%T) message to " + "current acceptor %T\r\n", + descP->currentAcceptor.ref, + descP->currentAcceptor.pid); + } } /* And also deal with the waiting acceptors (in the same way) */ @@ -17105,6 +17135,7 @@ void inform_waiting_procs(ErlNifEnv* env, currentP->data.ref, reason, ¤tP->data.pid)) ); + DEMONP("inform_waiting_procs -> current 'request'", env, descP, ¤tP->data.mon); nextP = currentP->nextP; -- cgit v1.2.3 From b7e5753c4e2eb22d231f2b64291ba7d1aec4d444 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 26 Nov 2018 18:31:49 +0100 Subject: [socket-nif] Conditional use of sctp struct field Added config checks for the sctp struct field: sctp_event_subscribe.sctp_sender_dry_event Also, if-def'ed the code accordingly. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f657da3ace..cc58918f43 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -9225,7 +9225,10 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, ERL_NIF_TERM result; ERL_NIF_TERM eDataIn, eAssoc, eAddr, eSndFailure; ERL_NIF_TERM ePeerError, eShutdown, ePartialDelivery; - ERL_NIF_TERM eAdaptLayer, eAuth, eSndDry; + ERL_NIF_TERM eAdaptLayer, eAuth; +#if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_SENDER_DRY_EVENT) + ERL_NIF_TERM eSndDry; +#endif struct sctp_event_subscribe events; int res; size_t sz; @@ -9273,8 +9276,10 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, if (!GET_MAP_VAL(env, eVal, atom_authentication, &eAuth)) return esock_make_error(env, esock_atom_einval); +#if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_SENDER_DRY_EVENT) if (!GET_MAP_VAL(env, eVal, atom_sender_dry, &eSndDry)) return esock_make_error(env, esock_atom_einval); +#endif SSDBG( descP, ("SOCKET", "nsetopt_lvl_sctp_events -> decode attributes\r\n") ); @@ -9288,7 +9293,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, events.sctp_partial_delivery_event = esock_decode_bool(ePartialDelivery); events.sctp_adaptation_layer_event = esock_decode_bool(eAdaptLayer); events.sctp_authentication_event = esock_decode_bool(eAuth); +#if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_SENDER_DRY_EVENT) events.sctp_sender_dry_event = esock_decode_bool(eSndDry); +#endif SSDBG( descP, ("SOCKET", "nsetopt_lvl_sctp_events -> set events option\r\n") ); -- cgit v1.2.3 From cdc39e8040e5817bb15c2d035c48cec812047035 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 27 Nov 2018 11:59:28 +0100 Subject: [socket-nif] Conditional use of sctp struct field Added config checks for the sctp struct field: sctp_event_subscribe.sctp_authentication_event Also, if-def'ed the code accordingly. If-def'ed code to handle the (non-) existence of IP_PMTUDISC_PROBE and IPV6_PMTUDISC_PROBE for the IP and IPv6 MTU_DISCOVER options. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index cc58918f43..cdd9f0fb15 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -9273,8 +9273,10 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, if (!GET_MAP_VAL(env, eVal, atom_adaptation_layer, &eAdaptLayer)) return esock_make_error(env, esock_atom_einval); +#if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_AUTHENTICATION_EVENT) if (!GET_MAP_VAL(env, eVal, atom_authentication, &eAuth)) return esock_make_error(env, esock_atom_einval); +#endif #if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_SENDER_DRY_EVENT) if (!GET_MAP_VAL(env, eVal, atom_sender_dry, &eSndDry)) @@ -9292,7 +9294,9 @@ ERL_NIF_TERM nsetopt_lvl_sctp_events(ErlNifEnv* env, events.sctp_shutdown_event = esock_decode_bool(eShutdown); events.sctp_partial_delivery_event = esock_decode_bool(ePartialDelivery); events.sctp_adaptation_layer_event = esock_decode_bool(eAdaptLayer); +#if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_AUTHENTICATION_EVENT) events.sctp_authentication_event = esock_decode_bool(eAuth); +#endif #if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_SENDER_DRY_EVENT) events.sctp_sender_dry_event = esock_decode_bool(eSndDry); #endif @@ -15418,8 +15422,10 @@ char* decode_ip_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) *val = IP_PMTUDISC_DONT; } else if (COMPARE(eVal, atom_do) == 0) { *val = IP_PMTUDISC_DO; +#if defined(IP_PMTUDISC_PROBE) } else if (COMPARE(eVal, atom_probe) == 0) { *val = IP_PMTUDISC_PROBE; +#endif } else { *val = -1; res = ESOCK_STR_EINVAL; @@ -15469,8 +15475,10 @@ char* decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) *val = IPV6_PMTUDISC_DONT; } else if (COMPARE(eVal, atom_do) == 0) { *val = IPV6_PMTUDISC_DO; +#if defined(IPV6_PMTUDISC_PROBE) } else if (COMPARE(eVal, atom_probe) == 0) { *val = IPV6_PMTUDISC_PROBE; +#endif } else { *val = -1; res = ESOCK_STR_EINVAL; @@ -15523,9 +15531,11 @@ void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) *eVal = atom_do; break; +#if defined(IP_PMTUDISC_PROBE) case IP_PMTUDISC_PROBE: *eVal = atom_probe; break; +#endif default: *eVal = MKI(env, val); @@ -15565,9 +15575,11 @@ void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) *eVal = atom_do; break; +#if defined(IPV6_PMTUDISC_PROBE) case IPV6_PMTUDISC_PROBE: *eVal = atom_probe; break; +#endif default: *eVal = MKI(env, val); -- cgit v1.2.3 From c4e29690e5a57004f494d35ae7d5b9e00f624d76 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 26 Nov 2018 12:33:09 +0100 Subject: [socket-nif] Increased the default read buffer size The default read buffer size was 2K and has now been increased to 8K. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index f657da3ace..cc11bb159f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -436,7 +436,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define SOCKET_RECV_FLAG_LOW SOCKET_RECV_FLAG_CMSG_CLOEXEC #define SOCKET_RECV_FLAG_HIGH SOCKET_RECV_FLAG_TRUNC -#define SOCKET_RECV_BUFFER_SIZE_DEFAULT 2048 +#define SOCKET_RECV_BUFFER_SIZE_DEFAULT 8192 #define SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024 #define SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT 1024 -- cgit v1.2.3 From 151bb05cd61987723f8de9f0c7ac71b4b5430307 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 22 Nov 2018 18:00:26 +0100 Subject: [socket-nif] Fixed two minor problems with socket close and down * Socket close callback Only demonitor the closer if its local (close) and its not a direct call. There was a spurious warning message. * Down callback Only process this event if the socket is not closed or closing. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 279 +++++++++++++++++++-------------- 1 file changed, 157 insertions(+), 122 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index cc11bb159f..ee1aa61c35 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -775,7 +775,7 @@ typedef struct { ErlNifEnv* env; /* +++ Controller (owner) process +++ */ - ErlNifPid ctrlPid; + ErlNifPid ctrlPid; // ErlNifMonitor ctrlMon; ESockMonitor ctrlMon; @@ -5232,7 +5232,6 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, else save_errno = -1; // The value does not actually matter in this case - return send_check_result(env, descP, written, sndDataP->size, save_errno, sendRef); @@ -6185,8 +6184,10 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, int type = descP->type; int protocol = descP->protocol; - SSDBG( descP, ("SOCKET", "nclose -> [%d] entry (0x%lX, 0x%lX, 0x%lX)\r\n", + SSDBG( descP, ("SOCKET", + "nclose -> [%d] entry (0x%lX, 0x%lX, 0x%lX, 0x%lX)\r\n", descP->sock, + descP->state, descP->currentWriterP, descP->currentReaderP, descP->currentAcceptorP) ); @@ -6246,7 +6247,6 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nclose -> [%d] stop was called\r\n", descP->sock) ); dec_socket(domain, type, protocol); - DEMONP("nclose -> closer", env, descP, &descP->closerMon); reply = esock_atom_ok; } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { /* The stop callback function has been *scheduled* which means that we @@ -6284,8 +6284,9 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nclose -> [%d] done when: " + "\r\n state: 0x%lX" "\r\n reply: %T" - "\r\n", descP->sock, reply) ); + "\r\n", descP->sock, descP->state, reply) ); return reply; } @@ -16901,8 +16902,29 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) MLOCK(descP->accMtx); MLOCK(descP->closeMtx); - SSDBG( descP, ("SOCKET", "socket_stop -> all mutex(s) locked\r\n") ); - + SSDBG( descP, ("SOCKET", "socket_stop -> " + "[%d, %T] all mutex(s) locked when counters:" + "\r\n writePkgCnt: %u" + "\r\n writeByteCnt: %u" + "\r\n writeTries: %u" + "\r\n writeWaits: %u" + "\r\n writeFails: %u" + "\r\n readPkgCnt: %u" + "\r\n readByteCnt: %u" + "\r\n readTries: %u" + "\r\n readWaits: %u" + "\r\n", + descP->sock, descP->ctrlPid, + descP->writePkgCnt, + descP->writeByteCnt, + descP->writeTries, + descP->writeWaits, + descP->writeFails, + descP->readPkgCnt, + descP->readByteCnt, + descP->readTries, + descP->readWaits) ); + descP->state = SOCKET_STATE_CLOSING; // Just in case...??? descP->isReadable = FALSE; descP->isWritable = FALSE; @@ -17044,7 +17066,8 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * */ - if (descP->closeLocal) { + if (descP->closeLocal && + !is_direct_call) { /* +++ send close message to the waiting process +++ * @@ -17054,13 +17077,18 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * * WHAT HAPPENS IF THE RECEIVER HAS DIED IN THE MEANTIME???? * + * Also, we should really *always* use a tag unique to this + * (nif-) module. Some like (in this case): + * + * {'$socket', close, CloseRef} + * * */ - send_msg(env, MKT2(env, atom_close, descP->closeRef), &descP->closerPid); + send_msg(env, + MKT2(env, atom_close, descP->closeRef), &descP->closerPid); - DEMONP("socket_stop -> closer", - env, descP, &descP->closerMon); + DEMONP("socket_stop -> closer", env, descP, &descP->closerMon); } else { @@ -17199,137 +17227,144 @@ void socket_down(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "socket_down -> OTHER mon\r\n") ); } */ - - if (compare_pids(env, &descP->ctrlPid, pid)) { - int selectRes; - - /* We don't bother with the queue cleanup here - - * we leave it to the stop callback function. - */ - - SSDBG( descP, - ("SOCKET", "socket_down -> controlling process exit\r\n") ); - descP->state = SOCKET_STATE_CLOSING; - descP->closeLocal = TRUE; - descP->closerPid = *pid; - descP->closerMon = (ESockMonitor) *mon; - descP->closeRef = MKREF(env); // Do we really need this in this case? - - /* - esock_dbg_printf("DOWN", - "[%d] select stop %u,%u,%u,%d\r\n", - descP->sock, - monP->raw[0], monP->raw[1], - monP->raw[2], monP->raw[3]); - */ + if (!IS_CLOSED(descP)) { + if (compare_pids(env, &descP->ctrlPid, pid)) { + int selectRes; - selectRes = enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), - descP, NULL, descP->closeRef); + /* We don't bother with the queue cleanup here - + * we leave it to the stop callback function. + */ - if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { - /* We are done - wwe can finalize (socket close) directly */ SSDBG( descP, - ("SOCKET", "socket_down -> [%d] stop called\r\n", descP->sock) ); - dec_socket(descP->domain, descP->type, descP->protocol); - descP->state = SOCKET_STATE_CLOSED; - - /* And finally close the socket. - * Since we close the socket because of an exiting owner, - * we do not need to wait for buffers to sync (linger). - * If the owner wish to ensure the buffer are written, - * it should have closed teh socket explicitly... - */ - if (sock_close(descP->sock) != 0) { - int save_errno = sock_errno(); - - esock_warning_msg("Failed closing socket for terminating " - "controlling process: " - "\r\n Controlling Process: %T" - "\r\n Descriptor: %d" - "\r\n Errno: %d" - "\r\n", pid, descP->sock, save_errno); - } - sock_close_event(descP->event); + ("SOCKET", "socket_down -> controlling process exit\r\n") ); + + descP->state = SOCKET_STATE_CLOSING; + descP->closeLocal = TRUE; + descP->closerPid = *pid; + descP->closerMon = (ESockMonitor) *mon; + descP->closeRef = MKREF(env); // Do we really need this in this case? - descP->sock = INVALID_SOCKET; - descP->event = INVALID_EVENT; + /* + esock_dbg_printf("DOWN", + "[%d] select stop %u,%u,%u,%d\r\n", + descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); + */ - descP->state = SOCKET_STATE_CLOSED; + selectRes = enif_select(env, descP->sock, (ERL_NIF_SELECT_STOP), + descP, NULL, descP->closeRef); + + if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { + /* We are done - we can finalize (socket close) directly */ + SSDBG( descP, + ("SOCKET", + "socket_down -> [%d] stop called\r\n", descP->sock) ); + dec_socket(descP->domain, descP->type, descP->protocol); + descP->state = SOCKET_STATE_CLOSED; + + /* And finally close the socket. + * Since we close the socket because of an exiting owner, + * we do not need to wait for buffers to sync (linger). + * If the owner wish to ensure the buffer are written, + * it should have closed the socket explicitly... + */ + if (sock_close(descP->sock) != 0) { + int save_errno = sock_errno(); + + esock_warning_msg("Failed closing socket for terminating " + "controlling process: " + "\r\n Controlling Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d" + "\r\n", pid, descP->sock, save_errno); + } + sock_close_event(descP->event); - } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { - /* The stop callback function has been *scheduled* which means that - * "should" wait for it to complete. But since we are in a callback - * (down) function, we cannot... - * So, we must close the socket - */ - SSDBG( descP, - ("SOCKET", - "socket_down -> [%d] stop scheduled\r\n", descP->sock) ); - dec_socket(descP->domain, descP->type, descP->protocol); + descP->sock = INVALID_SOCKET; + descP->event = INVALID_EVENT; - /* And now what? We can't wait for the stop function here... - * So, we simply close it here and leave the rest of the "close" - * for later (when the stop function actually gets called... - */ + descP->state = SOCKET_STATE_CLOSED; - if (sock_close(descP->sock) != 0) { - int save_errno = sock_errno(); - - esock_warning_msg("Failed closing socket for terminating " - "controlling process: " + } else if (selectRes & ERL_NIF_SELECT_STOP_SCHEDULED) { + /* The stop callback function has been *scheduled* which means + * that "should" wait for it to complete. But since we are in + * a callback (down) function, we cannot... + * So, we must close the socket + */ + SSDBG( descP, + ("SOCKET", + "socket_down -> [%d] stop scheduled\r\n", + descP->sock) ); + dec_socket(descP->domain, descP->type, descP->protocol); + + /* And now what? We can't wait for the stop function here... + * So, we simply close it here and leave the rest of the "close" + * for later (when the stop function actually gets called... + */ + + if (sock_close(descP->sock) != 0) { + int save_errno = sock_errno(); + + esock_warning_msg("Failed closing socket for terminating " + "controlling process: " + "\r\n Controlling Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d" + "\r\n", pid, descP->sock, save_errno); + } + sock_close_event(descP->event); + + } else { + + /* + * + * + * WE SHOULD REALLY HAVE A WAY TO CLOBBER THE SOCKET, + * SO WE DON'T LET STUFF LEAK. + * NOW, BECAUSE WE FAILED TO SELECT, WE CANNOT FINISH + * THE CLOSE, WHAT TO DO? ABORT? + * + * + */ + esock_warning_msg("Failed selecting stop when handling down " + "of controlling process: " + "\r\n Select Res: %d" "\r\n Controlling Process: %T" "\r\n Descriptor: %d" - "\r\n Errno: %d" - "\r\n", pid, descP->sock, save_errno); + "\r\n Monitor: %u.%u.%u.%u" + "\r\n", selectRes, pid, descP->sock, + monP->raw[0], monP->raw[1], + monP->raw[2], monP->raw[3]); } - sock_close_event(descP->event); - + } else { - /* - * + /* check all operation queue(s): acceptor, writer and reader. * - * WE SHOULD REALLY HAVE A WAY TO CLOBBER THE SOCKET, - * SO WE DON'T LET STUFF LEAK. - * NOW, BECAUSE WE FAILED TO SELECT, WE CANNOT FINISH - * THE CLOSE, WHAT TO DO? ABORT? + * Is it really any point in doing this if the socket is closed? * - * */ - esock_warning_msg("Failed selecting stop when handling down " - "of controlling process: " - "\r\n Select Res: %d" - "\r\n Controlling Process: %T" - "\r\n Descriptor: %d" - "\r\n Monitor: %u.%u.%u.%u" - "\r\n", selectRes, pid, descP->sock, - monP->raw[0], monP->raw[1], - monP->raw[2], monP->raw[3]); - } - - } else { - - /* check all operation queue(s): acceptor, writer and reader. */ - - SSDBG( descP, ("SOCKET", "socket_down -> other process term\r\n") ); + SSDBG( descP, ("SOCKET", "socket_down -> other process term\r\n") ); - MLOCK(descP->accMtx); - if (descP->currentAcceptorP != NULL) - socket_down_acceptor(env, descP, pid); - MUNLOCK(descP->accMtx); - - MLOCK(descP->writeMtx); - if (descP->currentWriterP != NULL) - socket_down_writer(env, descP, pid); - MUNLOCK(descP->writeMtx); - - MLOCK(descP->readMtx); - if (descP->currentReaderP != NULL) - socket_down_reader(env, descP, pid); - MUNLOCK(descP->readMtx); + MLOCK(descP->accMtx); + if (descP->currentAcceptorP != NULL) + socket_down_acceptor(env, descP, pid); + MUNLOCK(descP->accMtx); + + MLOCK(descP->writeMtx); + if (descP->currentWriterP != NULL) + socket_down_writer(env, descP, pid); + MUNLOCK(descP->writeMtx); + MLOCK(descP->readMtx); + if (descP->currentReaderP != NULL) + socket_down_reader(env, descP, pid); + MUNLOCK(descP->readMtx); + + } } /* -- cgit v1.2.3 From 7923c2c7ce4deba75d845501eb96ddce07dc5ea0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 6 Dec 2018 12:40:37 +0100 Subject: [socket-nif] Valgrind: plugged memory leaks in nif_recv Running valgrind, found a couple of cases when allocated binaries where not released. Mostly when the recv failed for some reason. Also clear and free the environment associated with the socket (resource) (in the _dtor callback function). OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 1 + erts/emulator/nifs/common/socket_nif.c | 41 ++++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 7 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index 7d223b8259..ec17e45f25 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -378,6 +378,7 @@ extern ERL_NIF_TERM esock_atom_einval; #define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) #define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) +#define FREE_BIN(BP) enif_release_binary((BP)) #endif // SOCKET_INT_H__ diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 3da895e644..57ce92d77e 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -4233,8 +4233,13 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, descP->type = type; descP->protocol = protocol; + /* + * Should we keep track of sockets (resources) in some way? + * Doing it here will require mutex to ensure data integrity, + * which will be costly. Send it somewhere? + */ res = enif_make_resource(env, descP); - enif_release_resource(descP); // We should really store a reference ... + enif_release_resource(descP); /* Keep track of the creator * This should not be a problem but just in case @@ -13592,6 +13597,8 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, recv_error_current_reader(env, descP, res); + FREE_BIN(bufP); + return res; } @@ -13633,6 +13640,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) return esock_make_error_str(env, xres); + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ data = MKBIN(env, bufP); SSDBG( descP, @@ -13664,6 +13674,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, recv_update_current_reader(env, descP); + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ data = MKBIN(env, bufP); return esock_make_ok3(env, atom_true, data); @@ -13707,6 +13720,8 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, (ERL_NIF_SELECT_STOP), descP, NULL, recvRef); + FREE_BIN(bufP); + return res; } else if ((saveErrno == ERRNO_BLOCK) || @@ -13721,16 +13736,14 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_result -> SELECT for more\r\n") ); - /* - SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), - descP, NULL, recvRef); - */ - sres = enif_select(env, descP->sock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); - SSDBG( descP, ("SOCKET", "recv_check_result -> SELECT res: %d\r\n", sres) ); + SSDBG( descP, + ("SOCKET", "recv_check_result -> SELECT res: %d\r\n", sres) ); + FREE_BIN(bufP); + return esock_make_error(env, esock_atom_eagain); } else { ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); @@ -13740,6 +13753,8 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, recv_error_current_reader(env, descP, res); + FREE_BIN(bufP); + return res; } @@ -13768,6 +13783,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, recv_update_current_reader(env, descP); + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ data = MKBIN(env, bufP); data = MKSBIN(env, data, 0, read); @@ -13792,6 +13810,9 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, cnt_inc(&descP->readByteCnt, read); + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ data = MKBIN(env, bufP); data = MKSBIN(env, data, 0, read); @@ -15713,6 +15734,7 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) if ((descP = enif_alloc_resource(sockets, sizeof(SocketDescriptor))) != NULL) { char buf[64]; /* Buffer used for building the mutex name */ + // This needs to be released when the socket is closed! descP->env = enif_alloc_env(); sprintf(buf, "socket[w,%d]", sock); @@ -16870,10 +16892,15 @@ void socket_dtor(ErlNifEnv* env, void* obj) { SocketDescriptor* descP = (SocketDescriptor*) obj; + enif_clear_env(descP->env); + enif_free_env(descP->env); + descP->env = NULL; + MDESTROY(descP->writeMtx); MDESTROY(descP->readMtx); MDESTROY(descP->accMtx); MDESTROY(descP->closeMtx); + } -- cgit v1.2.3 From c5378517cccf29d1708c71a0949664605743b478 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 6 Dec 2018 14:24:06 +0100 Subject: [socket-nif] Valgrind: plugged memory leaks in nif_[recvfromsock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); + FREE_BIN(bufP); + return esock_make_error(env, esock_atom_eagain); + } else { ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); @@ -13906,6 +13911,8 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, recv_error_current_reader(env, descP, res); + FREE_BIN(bufP); + return res; } @@ -13983,6 +13990,9 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, * *We* do never actually try to read 0 bytes from a stream socket! */ + + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + return esock_make_error(env, atom_closed); } @@ -14023,6 +14033,8 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, (ERL_NIF_SELECT_STOP), descP, NULL, recvRef); + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + return res; } else if ((saveErrno == ERRNO_BLOCK) || @@ -14037,7 +14049,10 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, SELECT(env, descP->sock, (ERL_NIF_SELECT_READ), descP, NULL, recvRef); + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + return esock_make_error(env, esock_atom_eagain); + } else { ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); @@ -14047,6 +14062,8 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, recv_error_current_reader(env, descP, res); + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + return res; } @@ -14078,6 +14095,8 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, recv_update_current_reader(env, descP); + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + return esock_make_error_str(env, xres); } else { -- cgit v1.2.3 From 342d35f457c15a9cea426e8ca83bfd52b0ec2f2e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 12 Dec 2018 18:37:55 +0100 Subject: [socket-nif] Message interface between socket.erl and nif updated Previously the "message interface" between the functions in socket.erl and the nif-code (socket_nif.c) was "ad hoc". This has now been changed so that we have a unified message {'$socket', SockRef | undefined, Tag, Info} This also has the added advantage of preparing the code for when we start using the new select-fucntions (with which its possible to specify your own message). This will be used in order to get around our eterm "leak" (we will use a simple counter, maintained in the nif, instead of the [Recv|Send|Acc]Ref we generate in the erlang code today. OTP-14831 --- erts/emulator/nifs/common/socket_int.h | 3 + erts/emulator/nifs/common/socket_nif.c | 314 ++++++++++++++++++++++----------- 2 files changed, 217 insertions(+), 100 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index ec17e45f25..0f973855ae 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -103,6 +103,7 @@ typedef unsigned int BOOLEAN_T; /* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * "Global" atoms */ +extern ERL_NIF_TERM esock_atom_abort; extern ERL_NIF_TERM esock_atom_accept; extern ERL_NIF_TERM esock_atom_acceptconn; extern ERL_NIF_TERM esock_atom_acceptfilter; @@ -126,6 +127,7 @@ extern ERL_NIF_TERM esock_atom_block_source; extern ERL_NIF_TERM esock_atom_broadcast; extern ERL_NIF_TERM esock_atom_busy_poll; extern ERL_NIF_TERM esock_atom_checksum; +extern ERL_NIF_TERM esock_atom_close; extern ERL_NIF_TERM esock_atom_connect; extern ERL_NIF_TERM esock_atom_congestion; extern ERL_NIF_TERM esock_atom_context; @@ -269,6 +271,7 @@ extern ERL_NIF_TERM esock_atom_sndbufforce; extern ERL_NIF_TERM esock_atom_sndlowat; extern ERL_NIF_TERM esock_atom_sndtimeo; extern ERL_NIF_TERM esock_atom_socket; +extern ERL_NIF_TERM esock_atom_socket_tag; extern ERL_NIF_TERM esock_atom_spec_dst; extern ERL_NIF_TERM esock_atom_status; extern ERL_NIF_TERM esock_atom_stream; diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 1e0533535c..80903c487f 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -760,24 +760,24 @@ typedef struct { typedef struct { /* +++ The actual socket +++ */ - SOCKET sock; - HANDLE event; + SOCKET sock; + HANDLE event; /* +++ Stuff "about" the socket +++ */ - int domain; - int type; - int protocol; + int domain; + int type; + int protocol; - unsigned int state; - SocketAddress remote; - unsigned int addrLen; + unsigned int state; + SocketAddress remote; + unsigned int addrLen; - ErlNifEnv* env; + ErlNifEnv* env; /* +++ Controller (owner) process +++ */ - ErlNifPid ctrlPid; - // ErlNifMonitor ctrlMon; - ESockMonitor ctrlMon; + ErlNifPid ctrlPid; + // ErlNifMonitor ctrlMon; + ESockMonitor ctrlMon; /* +++ Write stuff +++ */ ErlNifMutex* writeMtx; @@ -996,11 +996,13 @@ static ERL_NIF_TERM naccept(ErlNifEnv* env, ERL_NIF_TERM ref); static ERL_NIF_TERM nsend(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM sendRef, ErlNifBinary* dataP, int flags); static ERL_NIF_TERM nsendto(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM sendRef, ErlNifBinary* dataP, int flags, @@ -1008,21 +1010,25 @@ static ERL_NIF_TERM nsendto(ErlNifEnv* env, unsigned int toAddrLen); static ERL_NIF_TERM nsendmsg(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM sendRef, ERL_NIF_TERM eMsgHdr, int flags); static ERL_NIF_TERM nrecv(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sendRef, ERL_NIF_TERM recvRef, int len, int flags); static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, uint16_t bufSz, int flags); static ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, uint16_t bufLen, uint16_t ctrlLen, @@ -1986,6 +1992,7 @@ static ERL_NIF_TERM send_check_result(ErlNifEnv* env, ssize_t written, ssize_t dataSize, int saveErrno, + ERL_NIF_TERM sockRef, ERL_NIF_TERM sendRef); static BOOLEAN_T recv_check_reader(ErlNifEnv* env, SocketDescriptor* descP, @@ -1998,6 +2005,7 @@ static ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, SocketDescriptor* descP); static void recv_error_current_reader(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM reason); static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -2005,6 +2013,7 @@ static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, int toRead, int saveErrno, ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef); static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -2013,6 +2022,7 @@ static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, ErlNifBinary* bufP, SocketAddress* fromAddrP, unsigned int fromAddrLen, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef); static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, SocketDescriptor* descP, @@ -2021,6 +2031,7 @@ static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, struct msghdr* msgHdrP, ErlNifBinary* dataBufP, ErlNifBinary* ctrlBufP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef); static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, @@ -2310,22 +2321,22 @@ static void socket_down_reader(ErlNifEnv* env, SocketDescriptor* descP, const ErlNifPid* pid); -/* -static char* send_msg_error_closed(ErlNifEnv* env, +static char* esock_send_close_msg(ErlNifEnv* env, + ERL_NIF_TERM closeRef, + ErlNifPid* pid); +static char* esock_send_abort_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + ERL_NIF_TERM reason, + ErlNifPid* pid); +static char* esock_send_socket_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM tag, + ERL_NIF_TERM info, ErlNifPid* pid); -*/ -/* -static char* send_msg_error(ErlNifEnv* env, - ERL_NIF_TERM reason, +static char* esock_send_msg(ErlNifEnv* env, + ERL_NIF_TERM msg, ErlNifPid* pid); -*/ -static char* send_msg_nif_abort(ErlNifEnv* env, - ERL_NIF_TERM ref, - ERL_NIF_TERM reason, - ErlNifPid* pid); -static char* send_msg(ErlNifEnv* env, - ERL_NIF_TERM msg, - ErlNifPid* pid); static BOOLEAN_T extract_debug(ErlNifEnv* env, ERL_NIF_TERM map); @@ -2394,7 +2405,7 @@ static char str_max_rxt[] = "max_rxt"; static char str_min[] = "min"; static char str_mode[] = "mode"; static char str_multiaddr[] = "multiaddr"; -static char str_nif_abort[] = "nif_abort"; +// static char str_nif_abort[] = "nif_abort"; static char str_null[] = "null"; static char str_num_dlocal[] = "num_domain_local"; static char str_num_dinet[] = "num_domain_inet"; @@ -2436,6 +2447,7 @@ static char str_exsend[] = "exsend"; // failed send /* *** "Global" Atoms *** */ +ERL_NIF_TERM esock_atom_abort; ERL_NIF_TERM esock_atom_accept; ERL_NIF_TERM esock_atom_acceptconn; ERL_NIF_TERM esock_atom_acceptfilter; @@ -2459,6 +2471,7 @@ ERL_NIF_TERM esock_atom_block_source; ERL_NIF_TERM esock_atom_broadcast; ERL_NIF_TERM esock_atom_busy_poll; ERL_NIF_TERM esock_atom_checksum; +ERL_NIF_TERM esock_atom_close; ERL_NIF_TERM esock_atom_connect; ERL_NIF_TERM esock_atom_congestion; ERL_NIF_TERM esock_atom_context; @@ -2598,6 +2611,7 @@ ERL_NIF_TERM esock_atom_seqpacket; ERL_NIF_TERM esock_atom_setfib; ERL_NIF_TERM esock_atom_set_peer_primary_addr; ERL_NIF_TERM esock_atom_socket; +ERL_NIF_TERM esock_atom_socket_tag; ERL_NIF_TERM esock_atom_sndbuf; ERL_NIF_TERM esock_atom_sndbufforce; ERL_NIF_TERM esock_atom_sndlowat; @@ -2665,7 +2679,7 @@ static ERL_NIF_TERM atom_max_rxt; static ERL_NIF_TERM atom_min; static ERL_NIF_TERM atom_mode; static ERL_NIF_TERM atom_multiaddr; -static ERL_NIF_TERM atom_nif_abort; +// static ERL_NIF_TERM atom_nif_abort; static ERL_NIF_TERM atom_null; static ERL_NIF_TERM atom_num_dinet; static ERL_NIF_TERM atom_num_dinet6; @@ -4929,7 +4943,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size accRef = enif_make_resource(env, accDescP); - enif_release_resource(accDescP); // We should really store a reference ... + enif_release_resource(accDescP); accDescP->ctrlPid = caller; if (MONP("naccept_listening -> ctrl", @@ -5143,7 +5157,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { SocketDescriptor* descP; - ERL_NIF_TERM sendRef; + ERL_NIF_TERM sockRef, sendRef; ErlNifBinary sndData; unsigned int eflags; int flags; @@ -5154,13 +5168,17 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, /* Extract arguments and perform preliminary validation */ if ((argc != 4) || - !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !GET_BIN(env, argv[2], &sndData) || !GET_UINT(env, argv[3], &eflags)) { return enif_make_badarg(env); } + sockRef = argv[0]; // We need this in case we send in case we send abort sendRef = argv[1]; + if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) return esock_make_error(env, atom_closed); @@ -5170,7 +5188,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, "\r\n SendRef: %T" "\r\n Size of data: %d" "\r\n eFlags: %d" - "\r\n", descP->sock, argv[0], sendRef, sndData.size, eflags) ); + "\r\n", descP->sock, sockRef, sendRef, sndData.size, eflags) ); if (!IS_CONNECTED(descP)) return esock_make_error(env, atom_enotconn); @@ -5193,7 +5211,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, * time we do. */ - res = nsend(env, descP, sendRef, &sndData, flags); + res = nsend(env, descP, sockRef, sendRef, &sndData, flags); MUNLOCK(descP->writeMtx); @@ -5211,6 +5229,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, static ERL_NIF_TERM nsend(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM sendRef, ErlNifBinary* sndDataP, int flags) @@ -5238,7 +5257,8 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, save_errno = -1; // The value does not actually matter in this case return send_check_result(env, descP, - written, sndDataP->size, save_errno, sendRef); + written, sndDataP->size, save_errno, + sockRef, sendRef); } @@ -5264,7 +5284,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { SocketDescriptor* descP; - ERL_NIF_TERM sendRef; + ERL_NIF_TERM sockRef, sendRef; ErlNifBinary sndData; unsigned int eflags; int flags; @@ -5284,9 +5304,14 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, !GET_UINT(env, argv[4], &eflags)) { return enif_make_badarg(env); } + sockRef = argv[0]; // We need this in case we send in case we send abort sendRef = argv[1]; eSockAddr = argv[3]; + if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) return esock_make_error(env, atom_closed); @@ -5298,7 +5323,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, "\r\n eSockAddr: %T" "\r\n eflags: %d" "\r\n", - descP->sock, argv[0], sendRef, sndData.size, eSockAddr, eflags) ); + descP->sock, sockRef, sendRef, sndData.size, eSockAddr, eflags) ); /* THIS TEST IS NOT CORRECT!!! */ if (!IS_OPEN(descP)) { @@ -5320,7 +5345,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, MLOCK(descP->writeMtx); - res = nsendto(env, descP, sendRef, &sndData, flags, + res = nsendto(env, descP, sockRef, sendRef, &sndData, flags, &remoteAddr, remoteAddrLen); MUNLOCK(descP->writeMtx); @@ -5336,6 +5361,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, static ERL_NIF_TERM nsendto(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM sendRef, ErlNifBinary* dataP, int flags, @@ -5372,7 +5398,8 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, else save_errno = -1; // The value does not actually matter in this case - return send_check_result(env, descP, written, dataP->size, save_errno, sendRef); + return send_check_result(env, descP, written, dataP->size, save_errno, + sockRef, sendRef); } @@ -5395,7 +5422,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM res, sendRef, eMsgHdr; + ERL_NIF_TERM res, sockRef, sendRef, eMsgHdr; SocketDescriptor* descP; unsigned int eflags; int flags; @@ -5405,14 +5432,18 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, /* Extract arguments and perform preliminary validation */ if ((argc != 4) || - !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !IS_MAP(env, argv[2]) || !GET_UINT(env, argv[3], &eflags)) { return enif_make_badarg(env); } + sockRef = argv[0]; // We need this in case we send in case we send abort sendRef = argv[1]; eMsgHdr = argv[2]; + if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) return esock_make_error(env, atom_closed); @@ -5433,7 +5464,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, MLOCK(descP->writeMtx); - res = nsendmsg(env, descP, sendRef, eMsgHdr, flags); + res = nsendmsg(env, descP, sockRef, sendRef, eMsgHdr, flags); MUNLOCK(descP->writeMtx); @@ -5449,6 +5480,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, static ERL_NIF_TERM nsendmsg(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM sendRef, ERL_NIF_TERM eMsgHdr, int flags) @@ -5589,7 +5621,8 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, else save_errno = -1; // OK or not complete: this value should not matter in this case - res = send_check_result(env, descP, written, dataSize, save_errno, sendRef); + res = send_check_result(env, descP, written, dataSize, save_errno, + sockRef, sendRef); FREE(iovBins); FREE(iov); @@ -5691,20 +5724,24 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { SocketDescriptor* descP; - ERL_NIF_TERM recvRef; + ERL_NIF_TERM sockRef, recvRef; int len; unsigned int eflags; int flags; ERL_NIF_TERM res; if ((argc != 4) || - !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !GET_INT(env, argv[2], &len) || !GET_UINT(env, argv[3], &eflags)) { return enif_make_badarg(env); } + sockRef = argv[0]; // We need this in case we send in case we send abort recvRef = argv[1]; + if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) return esock_make_error(env, atom_closed); @@ -5729,7 +5766,7 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, * time we do. */ - res = nrecv(env, descP, recvRef, len, flags); + res = nrecv(env, descP, sockRef, recvRef, len, flags); MUNLOCK(descP->readMtx); @@ -5746,6 +5783,7 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, static ERL_NIF_TERM nrecv(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, int len, int flags) @@ -5794,6 +5832,7 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, read, len, save_errno, &buf, + sockRef, recvRef); } @@ -5829,7 +5868,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { SocketDescriptor* descP; - ERL_NIF_TERM recvRef; + ERL_NIF_TERM sockRef, recvRef; unsigned int bufSz; unsigned int eflags; int flags; @@ -5840,12 +5879,16 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, /* Extract arguments and perform preliminary validation */ if ((argc != 4) || - !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !GET_UINT(env, argv[2], &bufSz) || !GET_UINT(env, argv[3], &eflags)) { return enif_make_badarg(env); } - recvRef = argv[1]; + sockRef = argv[0]; // We need this in case we send in case we send abort + recvRef = argv[1]; + + if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + return enif_make_badarg(env); + } if (IS_CLOSED(descP) || IS_CLOSING(descP)) return esock_make_error(env, atom_closed); @@ -5883,7 +5926,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, * */ - res = nrecvfrom(env, descP, recvRef, bufSz, flags); + res = nrecvfrom(env, descP, sockRef, recvRef, bufSz, flags); MUNLOCK(descP->readMtx); @@ -5900,6 +5943,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, uint16_t len, int flags) @@ -5951,6 +5995,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, save_errno, &buf, &fromAddr, addrLen, + sockRef, recvRef); } @@ -5990,7 +6035,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { SocketDescriptor* descP; - ERL_NIF_TERM recvRef; + ERL_NIF_TERM sockRef, recvRef; unsigned int bufSz; unsigned int ctrlSz; unsigned int eflags; @@ -6002,14 +6047,18 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, /* Extract arguments and perform preliminary validation */ if ((argc != 5) || - !enif_get_resource(env, argv[0], sockets, (void**) &descP) || !GET_UINT(env, argv[2], &bufSz) || !GET_UINT(env, argv[3], &ctrlSz) || !GET_UINT(env, argv[4], &eflags)) { return enif_make_badarg(env); } - recvRef = argv[1]; + sockRef = argv[0]; // We need this in case we send in case we send abort + recvRef = argv[1]; + if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + if (IS_CLOSED(descP) || IS_CLOSING(descP)) return esock_make_error(env, atom_closed); @@ -6047,7 +6096,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, * */ - res = nrecvmsg(env, descP, recvRef, bufSz, ctrlSz, flags); + res = nrecvmsg(env, descP, sockRef, recvRef, bufSz, ctrlSz, flags); MUNLOCK(descP->readMtx); @@ -6064,6 +6113,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, static ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, uint16_t bufLen, uint16_t ctrlLen, @@ -6144,6 +6194,7 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, &msgHdr, data, // Needed for iov encode &ctrl, // Needed for ctrl header encode + sockRef, recvRef); } @@ -13254,6 +13305,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, ssize_t written, ssize_t dataSize, int saveErrno, + ERL_NIF_TERM sockRef, ERL_NIF_TERM sendRef) { SSDBG( descP, @@ -13330,7 +13382,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, while (writer_pop(env, descP, &pid, &mon, &ref)) { SSDBG( descP, ("SOCKET", "send_check_result -> abort %T\r\n", pid) ); - send_msg_nif_abort(env, ref, res, &pid); + esock_send_abort_msg(env, sockRef, ref, res, &pid); DEMONP("send_check_result -> pop'ed writer", env, descP, &mon); } @@ -13527,6 +13579,7 @@ ERL_NIF_TERM recv_update_current_reader(ErlNifEnv* env, static void recv_error_current_reader(ErlNifEnv* env, SocketDescriptor* descP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM reason) { if (descP->currentReaderP != NULL) { @@ -13541,7 +13594,7 @@ void recv_error_current_reader(ErlNifEnv* env, while (reader_pop(env, descP, &pid, &mon, &ref)) { SSDBG( descP, ("SOCKET", "recv_error_current_reader -> abort %T\r\n", pid) ); - send_msg_nif_abort(env, ref, reason, &pid); + esock_send_abort_msg(env, sockRef, ref, reason, &pid); DEMONP("recv_error_current_reader -> pop'ed reader", env, descP, &mon); } @@ -13561,6 +13614,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, int toRead, int saveErrno, ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef) { char* xres; @@ -13595,7 +13649,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * We must also notify any waiting readers! */ - recv_error_current_reader(env, descP, res); + recv_error_current_reader(env, descP, sockRef, res); FREE_BIN(bufP); @@ -13713,7 +13767,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, descP->closeLocal = FALSE; descP->state = SOCKET_STATE_CLOSING; - recv_error_current_reader(env, descP, res); + recv_error_current_reader(env, descP, sockRef, res); SELECT(env, descP->sock, @@ -13751,7 +13805,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] errno: %d\r\n", toRead, saveErrno) ); - recv_error_current_reader(env, descP, res); + recv_error_current_reader(env, descP, sockRef, res); FREE_BIN(bufP); @@ -13835,6 +13889,7 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, ErlNifBinary* bufP, SocketAddress* fromAddrP, unsigned int fromAddrLen, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef) { char* xres; @@ -13876,7 +13931,7 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, descP->closeLocal = FALSE; descP->state = SOCKET_STATE_CLOSING; - recv_error_current_reader(env, descP, res); + recv_error_current_reader(env, descP, sockRef, res); SELECT(env, descP->sock, @@ -13909,7 +13964,7 @@ ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, ("SOCKET", "recvfrom_check_result -> errno: %d\r\n", saveErrno) ); - recv_error_current_reader(env, descP, res); + recv_error_current_reader(env, descP, sockRef, res); FREE_BIN(bufP); @@ -13962,6 +14017,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, struct msghdr* msgHdrP, ErlNifBinary* dataBufP, ErlNifBinary* ctrlBufP, + ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef) { @@ -14026,7 +14082,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, descP->closeLocal = FALSE; descP->state = SOCKET_STATE_CLOSING; - recv_error_current_reader(env, descP, res); + recv_error_current_reader(env, descP, sockRef, res); SELECT(env, descP->sock, @@ -14060,7 +14116,7 @@ ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, ("SOCKET", "recvmsg_check_result -> errno: %d\r\n", saveErrno) ); - recv_error_current_reader(env, descP, res); + recv_error_current_reader(env, descP, sockRef, res); FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); @@ -16350,37 +16406,89 @@ char* send_msg_error(ErlNifEnv* env, */ -/* Send an (nif-) abort message to the specified process: +/* Send an close message to the specified process: * A message in the form: * - * {nif_abort, Ref, Reason} + * {'$socket', SockRef, close, CloseRef} * - * This message is for processes that are waiting in the + * This message is for processes that is waiting in the + * erlang API (close-) function for the socket to be "closed" + * (actually that the 'stop' callback function has been called). + */ +static +char* esock_send_close_msg(ErlNifEnv* env, + ERL_NIF_TERM closeRef, + ErlNifPid* pid) +{ + return esock_send_socket_msg(env, + esock_atom_undefined, + esock_atom_close, closeRef, pid); +} + + +/* Send an abort message to the specified process: + * A message in the form: + * + * {'$socket', SockRef, abort, {RecvRef, Reason}} + * + * This message is for processes that is waiting in the * erlang API functions for a select message. */ static -char* send_msg_nif_abort(ErlNifEnv* env, - ERL_NIF_TERM ref, - ERL_NIF_TERM reason, - ErlNifPid* pid) +char* esock_send_abort_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + ERL_NIF_TERM reason, + ErlNifPid* pid) { - ERL_NIF_TERM msg = MKT3(env, atom_nif_abort, ref, reason); + ERL_NIF_TERM info = MKT2(env, recvRef, reason); - return send_msg(env, msg, pid); + /* + esock_dbg_printf("SEND MSG", + "try send abort message to %T:\r\n", + "\r\n sockRef: %T" + "\r\n recvRef: %T" + "\r\n reason: %T" + "\r\n", *pid, sockRef, recvRef, reason); + */ + + return esock_send_socket_msg(env, sockRef, esock_atom_abort, info, pid); +} + + +/* *** esock_send_socket_msg *** + * + * This function sends a general purpose socket message to an erlang + * process. A general 'socket' message has the form: + * + * {'$socket', SockRef, Tag, Info} + * + */ + +static +char* esock_send_socket_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM tag, + ERL_NIF_TERM info, + ErlNifPid* pid) +{ + ERL_NIF_TERM msg = MKT4(env, esock_atom_socket_tag, sockRef, tag, info); + + return esock_send_msg(env, msg, pid); } /* Send a message to the specified process. */ static -char* send_msg(ErlNifEnv* env, - ERL_NIF_TERM msg, - ErlNifPid* pid) +char* esock_send_msg(ErlNifEnv* env, + ERL_NIF_TERM msg, + ErlNifPid* pid) { - if (!enif_send(env, pid, NULL, msg)) - return str_exsend; - else - return NULL; + if (!enif_send(env, pid, NULL, msg)) + return str_exsend; + else + return NULL; } @@ -17022,10 +17130,11 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> " "send abort message to current writer %T\r\n", descP->currentWriter.pid) ); - if (send_msg_nif_abort(env, - descP->currentWriter.ref, - atom_closed, - &descP->currentWriter.pid) != NULL) { + if (esock_send_abort_msg(env, + esock_atom_undefined, + descP->currentWriter.ref, + atom_closed, + &descP->currentWriter.pid) != NULL) { /* Shall we really do this? * This happens if the controlling process has been killed! */ @@ -17061,10 +17170,11 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> " "send abort message to current reader %T\r\n", descP->currentReader.pid) ); - if (send_msg_nif_abort(env, - descP->currentReader.ref, - atom_closed, - &descP->currentReader.pid) != NULL) { + if (esock_send_abort_msg(env, + esock_atom_undefined, + descP->currentReader.ref, + atom_closed, + &descP->currentReader.pid) != NULL) { /* Shall we really do this? * This happens if the controlling process has been killed! */ @@ -17099,10 +17209,11 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> " "send abort message to current acceptor %T\r\n", descP->currentWriter.pid) ); - if (send_msg_nif_abort(env, - descP->currentAcceptor.ref, - atom_closed, - &descP->currentAcceptor.pid) != NULL) { + if (esock_send_abort_msg(env, + esock_atom_undefined, + descP->currentAcceptor.ref, + atom_closed, + &descP->currentAcceptor.pid) != NULL) { /* Shall we really do this? * This happens if the controlling process has been killed! */ @@ -17145,13 +17256,12 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * Also, we should really *always* use a tag unique to this * (nif-) module. Some like (in this case): * - * {'$socket', close, CloseRef} + * {'$socket', undefined, close, CloseRef} * * */ - - send_msg(env, - MKT2(env, atom_close, descP->closeRef), &descP->closerPid); + + esock_send_close_msg(env, descP->closeRef, &descP->closerPid); DEMONP("socket_stop -> closer", env, descP, &descP->closerMon); @@ -17221,13 +17331,14 @@ void inform_waiting_procs(ErlNifEnv* env, */ SSDBG( descP, - ("SOCKET", "inform_waiting_procs -> abort %T (%T)\r\n", + ("SOCKET", "inform_waiting_procs -> abort request %T (from %T)\r\n", currentP->data.ref, currentP->data.pid) ); - ESOCK_ASSERT( (NULL == send_msg_nif_abort(env, - currentP->data.ref, - reason, - ¤tP->data.pid)) ); + ESOCK_ASSERT( (NULL == esock_send_abort_msg(env, + esock_atom_undefined, + currentP->data.ref, + reason, + ¤tP->data.pid)) ); DEMONP("inform_waiting_procs -> current 'request'", env, descP, ¤tP->data.mon); @@ -17783,7 +17894,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_min = MKA(env, str_min); atom_mode = MKA(env, str_mode); atom_multiaddr = MKA(env, str_multiaddr); - atom_nif_abort = MKA(env, str_nif_abort); + // atom_nif_abort = MKA(env, str_nif_abort); atom_null = MKA(env, str_null); atom_num_dinet = MKA(env, str_num_dinet); atom_num_dinet6 = MKA(env, str_num_dinet6); @@ -17813,6 +17924,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_want = MKA(env, str_want); /* Global atom(s) */ + esock_atom_abort = MKA(env, "abort"); esock_atom_accept = MKA(env, "accept"); esock_atom_acceptconn = MKA(env, "acceptconn"); esock_atom_acceptfilter = MKA(env, "acceptfilter"); @@ -17836,6 +17948,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_broadcast = MKA(env, "broadcast"); esock_atom_busy_poll = MKA(env, "busy_poll"); esock_atom_checksum = MKA(env, "checksum"); + esock_atom_close = MKA(env, "close"); esock_atom_connect = MKA(env, "connect"); esock_atom_congestion = MKA(env, "congestion"); esock_atom_context = MKA(env, "context"); @@ -17979,6 +18092,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_sndlowat = MKA(env, "sndlowat"); esock_atom_sndtimeo = MKA(env, "sndtimeo"); esock_atom_socket = MKA(env, "socket"); + esock_atom_socket_tag = MKA(env, "$socket"); esock_atom_spec_dst = MKA(env, "spec_dst"); esock_atom_status = MKA(env, "status"); esock_atom_stream = MKA(env, "stream"); -- cgit v1.2.3 From cc553264b4a6e06377b34c06858f75f3a17319de Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 19 Dec 2018 13:04:28 +0100 Subject: [socket-nif] Fixed OpenBSD config The macro (define) IPTOS_MINCOST does not exist on OpenBSD 6.3. OTP-14831 --- erts/emulator/nifs/common/socket_nif.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 80903c487f..6dcd4ae623 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -15198,9 +15198,11 @@ char* encode_cmsghdr_data_ip(ErlNifEnv* env, case IPTOS_RELIABILITY: *eCMsgHdrData = esock_atom_reliability; break; +#if defined(IPTOS_MINCOST) case IPTOS_MINCOST: *eCMsgHdrData = esock_atom_mincost; break; +#endif default: *eCMsgHdrData = MKUI(env, tos); break; @@ -15467,9 +15469,11 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) } else if (COMPARE(eVal, esock_atom_reliability) == 0) { *val = IPTOS_RELIABILITY; result = TRUE; +#if defined(IPTOS_MINCOST) } else if (COMPARE(eVal, esock_atom_mincost) == 0) { *val = IPTOS_MINCOST; result = TRUE; +#endif } else { *val = -1; result = FALSE; @@ -15781,9 +15785,11 @@ ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val) result = esock_make_ok2(env, esock_atom_reliability); break; +#if defined(IPTOS_MINCOST) case IPTOS_MINCOST: result = esock_make_ok2(env, esock_atom_mincost); break; +#endif default: result = esock_make_ok2(env, MKI(env, val)); -- cgit v1.2.3 From 8a820377bb3e2e506cdff6011057d78507544142 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 21 Jan 2019 14:46:07 +0100 Subject: [socket-nif] Add support for otp option fd Add a way to *get* the file descriptor (fd) of a socket. Useful mostly for debugging. OTP-15528 --- erts/emulator/nifs/common/socket_nif.c | 35 ++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 6dcd4ae623..44aa44f5fc 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2018-2018. All Rights Reserved. + * Copyright Ericsson AB 2018-2019. 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. @@ -519,6 +519,7 @@ typedef union { #define SOCKET_OPT_OTP_RCVBUF 4 #define SOCKET_OPT_OTP_RCVCTRLBUF 6 #define SOCKET_OPT_OTP_SNDCTRLBUF 7 +#define SOCKET_OPT_OTP_FD 8 #define SOCKET_OPT_OTP_DOMAIN 0xFF01 // INTERNAL AND ONLY GET #define SOCKET_OPT_OTP_TYPE 0xFF02 // INTERNAL AND ONLY GET #define SOCKET_OPT_OTP_PROTOCOL 0xFF03 // INTERNAL AND ONLY GET @@ -1561,6 +1562,8 @@ static ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, SocketDescriptor* descP); +static ERL_NIF_TERM ngetopt_otp_fd(ErlNifEnv* env, + SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_otp_domain(ErlNifEnv* env, SocketDescriptor* descP); static ERL_NIF_TERM ngetopt_otp_type(ErlNifEnv* env, @@ -10031,6 +10034,10 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, result = ngetopt_otp_sndctrlbuf(env, descP); break; + case SOCKET_OPT_OTP_FD: + result = ngetopt_otp_fd(env, descP); + break; + /* *** INTERNAL *** */ case SOCKET_OPT_OTP_DOMAIN: result = ngetopt_otp_domain(env, descP); @@ -10058,7 +10065,7 @@ ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, } -/* ngetopt_otp_debug - Handle the OTP (level) debug options +/* ngetopt_otp_debug - Handle the OTP (level) debug option */ static ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, @@ -10070,7 +10077,7 @@ ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, } -/* ngetopt_otp_iow - Handle the OTP (level) iow options +/* ngetopt_otp_iow - Handle the OTP (level) iow option */ static ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, @@ -10082,7 +10089,7 @@ ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, } -/* ngetopt_otp_ctrl_proc - Handle the OTP (level) controlling_process options +/* ngetopt_otp_ctrl_proc - Handle the OTP (level) controlling_process option */ static ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, @@ -10095,7 +10102,7 @@ ERL_NIF_TERM ngetopt_otp_ctrl_proc(ErlNifEnv* env, -/* ngetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf options +/* ngetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option */ static ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, @@ -10107,7 +10114,7 @@ ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, } -/* ngetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf options +/* ngetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option */ static ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, @@ -10119,7 +10126,7 @@ ERL_NIF_TERM ngetopt_otp_rcvctrlbuf(ErlNifEnv* env, } -/* ngetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf options +/* ngetopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option */ static ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, @@ -10131,7 +10138,19 @@ ERL_NIF_TERM ngetopt_otp_sndctrlbuf(ErlNifEnv* env, } -/* ngetopt_otp_domain - Handle the OTP (level) domain options. +/* ngetopt_otp_fd - Handle the OTP (level) fd option + */ +static +ERL_NIF_TERM ngetopt_otp_fd(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = MKI(env, descP->sock); + + return esock_make_ok2(env, eVal); +} + + +/* ngetopt_otp_domain - Handle the OTP (level) domain option */ static ERL_NIF_TERM ngetopt_otp_domain(ErlNifEnv* env, -- cgit v1.2.3 From fe540a9079ea58f8b9b85069ff1558ce7a98f915 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 25 Jan 2019 11:36:33 +0100 Subject: [socket-nif] Correct state checks when calling read and write functions The read and write functions now *only* use the isReadable and isWritable fields, respectively, to decide if the "socket" can be read from and written to (previously the state field was used outside of the mutex lock). Also made it possible to enable / disable test suite groups individually, via environment variables (ESOCK_TEST_...). Specifically, the ttest group has been excluded by default (takes to long a time). OTP-15549 --- erts/emulator/nifs/common/socket_nif.c | 79 +++++++++++++++------------------- 1 file changed, 34 insertions(+), 45 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 44aa44f5fc..9375e9c005 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -4250,6 +4250,12 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, descP->type = type; descP->protocol = protocol; + /* Does this apply to other types? Such as RAW? */ + if (type == SOCK_DGRAM) { + descP->isReadable = TRUE; + descP->isWritable = TRUE; + } + /* * Should we keep track of sockets (resources) in some way? * Doing it here will require mutex to ensure data integrity, @@ -4544,20 +4550,27 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, { int code, save_errno = 0; - /* Verify that we are where in the proper state */ + /* + * + * + * We should look both the read and write mutex:es... + * + * + * + * Verify that we are where in the proper state */ if (!IS_OPEN(descP)) { - SSDBG( descP, ("SOCKET", "nif_sendto -> not open\r\n") ); + SSDBG( descP, ("SOCKET", "nif_connect -> not open\r\n") ); return esock_make_error(env, atom_exbadstate); } if (IS_CONNECTED(descP)) { - SSDBG( descP, ("SOCKET", "nif_sendto -> already connected\r\n") ); + SSDBG( descP, ("SOCKET", "nif_connect -> already connected\r\n") ); return esock_make_error(env, atom_eisconn); } if (IS_CONNECTING(descP)) { - SSDBG( descP, ("SOCKET", "nif_sendto -> already connecting\r\n") ); + SSDBG( descP, ("SOCKET", "nif_connect -> already connecting\r\n") ); return esock_make_error(env, esock_atom_einval); } @@ -4566,7 +4579,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, descP->addrLen); save_errno = sock_errno(); - SSDBG( descP, ("SOCKET", "nif_sendto -> connect result: %d, %d\r\n", + SSDBG( descP, ("SOCKET", "nif_connect -> connect result: %d, %d\r\n", code, save_errno) ); if (IS_SOCKET_ERROR(code) && @@ -4580,7 +4593,9 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, descP, NULL, ref); return esock_make_ok2(env, ref); } else if (code == 0) { /* ok we are connected */ - descP->state = SOCKET_STATE_CONNECTED; + descP->state = SOCKET_STATE_CONNECTED; + descP->isReadable = TRUE; + descP->isWritable = TRUE; /* Do we need to do somthing for "active" mode? * Is there even such a thing *here*? */ @@ -4637,7 +4652,9 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, return esock_make_error_errno(env, error); } - descP->state = SOCKET_STATE_CONNECTED; + descP->state = SOCKET_STATE_CONNECTED; + descP->isReadable = TRUE; + descP->isWritable = TRUE; return esock_atom_ok; } @@ -4968,7 +4985,9 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, descP, NULL, esock_atom_undefined); #endif - accDescP->state = SOCKET_STATE_CONNECTED; + accDescP->state = SOCKET_STATE_CONNECTED; + accDescP->isReadable = TRUE; + accDescP->isWritable = TRUE; return esock_make_ok2(env, accRef); } @@ -5108,7 +5127,9 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, descP, NULL, esock_atom_undefined); #endif - accDescP->state = SOCKET_STATE_CONNECTED; + accDescP->state = SOCKET_STATE_CONNECTED; + accDescP->isReadable = TRUE; + accDescP->isWritable = TRUE; /* Check if there are waiting acceptors (popping the acceptor queue) */ @@ -5182,9 +5203,6 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, return enif_make_badarg(env); } - if (IS_CLOSED(descP) || IS_CLOSING(descP)) - return esock_make_error(env, atom_closed); - SSDBG( descP, ("SOCKET", "nif_send -> args when sock = %d:" "\r\n Socket: %T" @@ -5193,9 +5211,6 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, "\r\n eFlags: %d" "\r\n", descP->sock, sockRef, sendRef, sndData.size, eflags) ); - if (!IS_CONNECTED(descP)) - return esock_make_error(env, atom_enotconn); - if (!esendflags2sendflags(eflags, &flags)) return enif_make_badarg(env); @@ -5315,9 +5330,6 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, return enif_make_badarg(env); } - if (IS_CLOSED(descP) || IS_CLOSING(descP)) - return esock_make_error(env, atom_closed); - SSDBG( descP, ("SOCKET", "nif_sendto -> args when sock = %d:" "\r\n Socket: %T" @@ -5328,12 +5340,6 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, "\r\n", descP->sock, sockRef, sendRef, sndData.size, eSockAddr, eflags) ); - /* THIS TEST IS NOT CORRECT!!! */ - if (!IS_OPEN(descP)) { - SSDBG( descP, ("SOCKET", "nif_sendto -> not open (%u)\r\n", descP->state) ); - return esock_make_error(env, esock_atom_einval); - } - if (!esendflags2sendflags(eflags, &flags)) { SSDBG( descP, ("SOCKET", "nif_sendto -> sendflags decode failed\r\n") ); return esock_make_error(env, esock_atom_einval); @@ -5447,9 +5453,6 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, return enif_make_badarg(env); } - if (IS_CLOSED(descP) || IS_CLOSING(descP)) - return esock_make_error(env, atom_closed); - SSDBG( descP, ("SOCKET", "nif_sendmsg -> args when sock = %d:" "\r\n Socket: %T" @@ -5458,10 +5461,6 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, "\r\n", descP->sock, argv[0], sendRef, eflags) ); - /* THIS TEST IS NOT CORRECT!!! */ - if (!IS_OPEN(descP)) - return esock_make_error(env, esock_atom_einval); - if (!esendflags2sendflags(eflags, &flags)) return esock_make_error(env, esock_atom_einval); @@ -5745,12 +5744,6 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, return enif_make_badarg(env); } - if (IS_CLOSED(descP) || IS_CLOSING(descP)) - return esock_make_error(env, atom_closed); - - if (!IS_CONNECTED(descP)) - return esock_make_error(env, atom_enotconn); - if (!erecvflags2recvflags(eflags, &flags)) return enif_make_badarg(env); @@ -5893,9 +5886,6 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, return enif_make_badarg(env); } - if (IS_CLOSED(descP) || IS_CLOSING(descP)) - return esock_make_error(env, atom_closed); - SSDBG( descP, ("SOCKET", "nif_recvfrom -> args when sock = %d:" "\r\n Socket: %T" @@ -6062,9 +6052,6 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, return enif_make_badarg(env); } - if (IS_CLOSED(descP) || IS_CLOSING(descP)) - return esock_make_error(env, atom_closed); - SSDBG( descP, ("SOCKET", "nif_recvmsg -> args when sock = %d:" "\r\n Socket: %T" @@ -6292,6 +6279,8 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, descP->closeLocal = TRUE; descP->state = SOCKET_STATE_CLOSING; + descP->isReadable = FALSE; + descP->isWritable = FALSE; doClose = TRUE; } @@ -15842,7 +15831,7 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->currentWriterP = NULL; // currentWriter not used descP->writersQ.first = NULL; descP->writersQ.last = NULL; - descP->isWritable = TRUE; + descP->isWritable = FALSE; // TRUE; descP->writePkgCnt = 0; descP->writeByteCnt = 0; descP->writeTries = 0; @@ -15854,7 +15843,7 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->currentReaderP = NULL; // currentReader not used descP->readersQ.first = NULL; descP->readersQ.last = NULL; - descP->isReadable = TRUE; + descP->isReadable = FALSE; // TRUE; descP->readPkgCnt = 0; descP->readByteCnt = 0; descP->readTries = 0; -- cgit v1.2.3 From 3d166efe4f3ee6a93edf361a9d72633a00fb486f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 29 Jan 2019 12:41:19 +0100 Subject: [socket-nif] The otp rcvbuf option updated Its now possible to set a rcvbuf (otp) option value of {N :: pos_integer(), BufSz :: pos_integer()}. This value is used for type stream and protocol tcp, when calling the function recv with length = 0 (zero). The second value, BufSz, is the actual size of the receive buffer used when calling the socket recv function, and the first value, N, is the max number of possible reads that will be performed (at most), even if there is more data to read. This is limit the effect of DoS attacks. OTP-15497 --- erts/emulator/nifs/common/socket_nif.c | 190 ++++++++++++++++++++++++++------- 1 file changed, 149 insertions(+), 41 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 9375e9c005..fc218f5163 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -812,11 +812,20 @@ typedef struct { SocketRequestQueue acceptorsQ; /* +++ Config & Misc stuff +++ */ - size_t rBufSz; // Read buffer size (when data length = 0 is specified) - size_t rCtrlSz; // Read control buffer size - size_t wCtrlSz; // Write control buffer size - BOOLEAN_T iow; // Inform On (counter) Wrap - BOOLEAN_T dbg; + size_t rBufSz; // Read buffer size (when data length = 0) + /* rNum and rNumCnt are used (together with rBufSz) when calling the recv + * function with the Length argument set to 0 (zero). + * If rNum is 0 (zero), then rNumCnt is not used and only *one* read will + * be done. Also, when get'ing the value of the option (rcvbuf) with + * getopt, the value will be reported as an integer. If the rNum has a + * value greater then 0 (zero), then it will instead be reported as {N, BufSz}. + */ + unsigned int rNum; // recv: Number of reads using rBufSz + unsigned int rNumCnt; // recv: Current number of reads (so far) + size_t rCtrlSz; // Read control buffer size + size_t wCtrlSz; // Write control buffer size + BOOLEAN_T iow; // Inform On (counter) Wrap + BOOLEAN_T dbg; /* +++ Close stuff +++ */ ErlNifMutex* closeMtx; @@ -4959,6 +4968,8 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, accDescP->type = descP->type; accDescP->protocol = descP->protocol; accDescP->rBufSz = descP->rBufSz; // Inherit buffer size + accDescP->rNum = descP->rNum; // Inherit buffer uses + accDescP->rNumCnt = 0; accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer siez accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size @@ -5737,8 +5748,8 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, !GET_UINT(env, argv[3], &eflags)) { return enif_make_badarg(env); } - sockRef = argv[0]; // We need this in case we send in case we send abort - recvRef = argv[1]; + sockRef = argv[0]; // We need this in case we case we send abort + recvRef = argv[1]; if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) { return enif_make_badarg(env); @@ -5791,9 +5802,9 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, int bufSz = (len ? len : descP->rBufSz); SSDBG( descP, ("SOCKET", "nrecv -> entry with" - "\r\n len: %d (%d)" + "\r\n len: %d (%d:%d)" "\r\n flags: %d" - "\r\n", len, bufSz, flags) ); + "\r\n", len, descP->rNumCnt, bufSz, flags) ); if (!descP->isReadable) return enif_make_badarg(env); @@ -5817,10 +5828,11 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, // If it fails (read = -1), we need errno... SSDBG( descP, ("SOCKET", "nrecv -> try read (%d)\r\n", buf.size) ); read = sock_recv(descP->sock, buf.data, buf.size, flags); - if (IS_SOCKET_ERROR(read)) + if (IS_SOCKET_ERROR(read)) { save_errno = sock_errno(); - else + } else { save_errno = -1; // The value does not actually matter in this case + } SSDBG( descP, ("SOCKET", "nrecv -> read: %d (%d)\r\n", read, save_errno) ); @@ -6719,7 +6731,7 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, } descP->ctrlPid = newCtrlPid; - descP->ctrlMon = newCtrlMon; + descP->ctrlMon = newCtrlMon; SSDBG( descP, ("SOCKET", "nsetopt_otp_ctrl_proc -> done\r\n") ); @@ -6729,22 +6741,60 @@ ERL_NIF_TERM nsetopt_otp_ctrl_proc(ErlNifEnv* env, /* nsetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option + * The (otp) rcvbuf option is provided as: + * + * BufSz :: integer() | {N :: pos_integer(), BufSz :: pod_integer()} + * + * Where N is the max number of reads. */ static ERL_NIF_TERM nsetopt_otp_rcvbuf(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM eVal) { - size_t val; - char* xres; + const ERL_NIF_TERM* t; // The array of the elements of the tuple + int tsz; // The size of the tuple - should be 2 + unsigned int n; + size_t bufSz; + char* xres; - if ((xres = esock_decode_bufsz(env, - eVal, - SOCKET_RECV_BUFFER_SIZE_DEFAULT, &val)) != NULL) - return esock_make_error_str(env, xres); + if (IS_NUM(env, eVal)) { + + /* This will have the effect that the buffer size will be + * reported as an integer (getopt). + */ + n = 0; - descP->rBufSz = val; + if ((xres = esock_decode_bufsz(env, + eVal, + SOCKET_RECV_BUFFER_SIZE_DEFAULT, + &bufSz)) != NULL) + return esock_make_error_str(env, xres); + + } else if (IS_TUPLE(env, eVal)) { + + if (!GET_TUPLE(env, eVal, &tsz, &t)) + return enif_make_badarg(env); // We should use a "proper" error value... + + if (tsz != 2) + return enif_make_badarg(env); // We should use a "proper" error value... + if (!GET_UINT(env, t[0], &n)) + return enif_make_badarg(env); // We should use a "proper" error value... + + if ((xres = esock_decode_bufsz(env, + t[1], + SOCKET_RECV_BUFFER_SIZE_DEFAULT, + &bufSz)) != NULL) + return esock_make_error_str(env, xres); + + } else { + return enif_make_badarg(env); // We should use a "proper" error value... + } + + descP->rNum = n; + descP->rBufSz = bufSz; + return esock_atom_ok; } @@ -10097,7 +10147,13 @@ static ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, SocketDescriptor* descP) { - ERL_NIF_TERM eVal = MKI(env, descP->rBufSz); + ERL_NIF_TERM eVal; + + if (descP->rNum == 0) { + eVal = MKI(env, descP->rBufSz); + } else { + eVal = MKT2(env, MKI(env, descP->rNum), MKI(env, descP->rBufSz)); + } return esock_make_ok2(env, eVal); } @@ -13681,7 +13737,8 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, if (toRead == 0) { - /* +++ Give us everything you have got => needs to continue +++ */ + /* +++ Give us everything you have got => * + * (maybe) needs to continue +++ */ /* How do we do this? * Either: @@ -13695,36 +13752,83 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, * (continuous binary realloc "here"). * * => We choose alt 1 for now. + * + * Also, we need to check if the rNumCnt has reached its max (rNum), + * in which case we will assume the read to be done! */ cnt_inc(&descP->readByteCnt, read); - if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) - return esock_make_error_str(env, xres); + SSDBG( descP, + ("SOCKET", "recv_check_result -> shall we continue reading" + "\r\n read: %d" + "\r\n rNum: %d" + "\r\n rNumCnt: %d" + "\r\n", read, descP->rNum, descP->rNumCnt) ); - /* This transfers "ownership" of the *allocated* binary to an - * erlang term (no need for an explicit free). - */ - data = MKBIN(env, bufP); + if (descP->rNum > 0) { - SSDBG( descP, - ("SOCKET", - "recv_check_result -> [%d] " - "we are done for now - read more\r\n", toRead) ); + descP->rNumCnt++; + if (descP->rNumCnt >= descP->rNum) { - return esock_make_ok3(env, atom_false, data); + descP->rNumCnt = 0; - } else { + cnt_inc(&descP->readPkgCnt, 1); + + recv_update_current_reader(env, descP); + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(env, bufP); + + return esock_make_ok3(env, atom_true, data); - /* +++ We got exactly as much as we requested +++ */ + } else { - /* - * WE NEED TO INFORM ANY WAITING READERS - * - * DEMONP of the current reader! - * - * - */ + /* Yes, we *do* need to continue reading */ + + if ((xres = recv_init_current_reader(env, + descP, recvRef)) != NULL) { + descP->rNumCnt = 0; + return esock_make_error_str(env, xres); + } + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(env, bufP); + + return esock_make_ok3(env, atom_false, data); + + } + + } else { + + /* Yes, we *do* need to continue reading */ + + if ((xres = recv_init_current_reader(env, + descP, recvRef)) != NULL) { + descP->rNumCnt = 0; + return esock_make_error_str(env, xres); + } + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(env, bufP); + + SSDBG( descP, + ("SOCKET", + "recv_check_result -> [%d] " + "we are done for now - read more\r\n", toRead) ); + + return esock_make_ok3(env, atom_false, data); + } + + } else { + + /* +++ We got exactly as much as we requested => We are done +++ */ cnt_inc(&descP->readPkgCnt, 1); cnt_inc(&descP->readByteCnt, read); @@ -13793,6 +13897,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] eagain\r\n", toRead) ); + descP->rNumCnt = 0; if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) return esock_make_error_str(env, xres); @@ -13840,6 +13945,7 @@ ERL_NIF_TERM recv_check_result(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] split buffer\r\n", toRead) ); + descP->rNumCnt = 0; cnt_inc(&descP->readPkgCnt, 1); cnt_inc(&descP->readByteCnt, read); @@ -15859,6 +15965,8 @@ SocketDescriptor* alloc_descriptor(SOCKET sock, HANDLE event) descP->closeMtx = MCREATE(buf); descP->rBufSz = SOCKET_RECV_BUFFER_SIZE_DEFAULT; + descP->rNum = 0; + descP->rNumCnt = 0; descP->rCtrlSz = SOCKET_RECV_CTRL_BUFFER_SIZE_DEFAULT; descP->wCtrlSz = SOCKET_SEND_CTRL_BUFFER_SIZE_DEFAULT; descP->iow = FALSE; -- cgit v1.2.3 From 060338c0a6904cc4aaf5226dc7fb321ad3d23862 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 30 Jan 2019 14:17:52 +0100 Subject: [socket-nif] Preliminary windows adaptions Added preliminary, and temporary, windows adaptions. Basically they amount to letting all nif-callback functions returning badarg for all calls if on windows (this has been accomplished by if-defing the nif-code; if win32 then badarg else do-something). OTP-15526 --- erts/emulator/nifs/common/net_nif.c | 63 ++++++++++- erts/emulator/nifs/common/socket_nif.c | 201 +++++++++++++++++++++++++++++++-- 2 files changed, 249 insertions(+), 15 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index f7eeee45ac..f51975b564 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -421,7 +421,6 @@ static ERL_NIF_TERM atom_esystem; /* *** net *** */ static ErlNifResourceType* net; -/* Maybe all of these whould be NULL? */ static ErlNifResourceTypeInit netInit = { NULL, // net_dtor, NULL, // net_stop, @@ -462,6 +461,9 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else ERL_NIF_TERM info, tmp; NDBG( ("NET", "info -> entry\r\n") ); @@ -473,6 +475,7 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, NDBG( ("NET", "info -> done: %T\r\n", info) ); return info; +#endif } @@ -494,6 +497,9 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else ERL_NIF_TERM ecmd, result; NDBG( ("NET", "command -> entry (%d)\r\n", argc) ); @@ -510,6 +516,7 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env, NDBG( ("NET", "command -> result: %T\r\n", result) ); return result; +#endif } @@ -518,6 +525,7 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env, * The command can, in principle, be anything, though currently we only * support a debug command. */ +#if !defined(__WIN32__) static ERL_NIF_TERM ncommand(ErlNifEnv* env, ERL_NIF_TERM cmd) @@ -544,6 +552,7 @@ ERL_NIF_TERM ncommand(ErlNifEnv* env, } } +#endif @@ -559,6 +568,9 @@ ERL_NIF_TERM nif_gethostname(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else ERL_NIF_TERM result; NDBG( ("NET", "nif_gethostname -> entry (%d)\r\n", argc) ); @@ -571,9 +583,11 @@ ERL_NIF_TERM nif_gethostname(ErlNifEnv* env, NDBG( ("NET", "nif_gethostname -> done when result: %T\r\n", result) ); return result; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM ngethostname(ErlNifEnv* env) { @@ -609,6 +623,7 @@ ERL_NIF_TERM ngethostname(ErlNifEnv* env) return result; } +#endif @@ -629,6 +644,9 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else ERL_NIF_TERM result; ERL_NIF_TERM eSockAddr, eFlags; int flags = 0; // Just in case... @@ -661,6 +679,7 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, "\r\n %T\r\n", result) ); return result; +#endif } @@ -668,6 +687,7 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, /* Given the provided sock(et) address (and honts), retreive the host and * service info. */ +#if !defined(__WIN32__) static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, const SocketAddress* saP, @@ -753,6 +773,7 @@ ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, return result; } +#endif @@ -773,6 +794,9 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else ERL_NIF_TERM result, eHostName, eServName; //, eHints; char* hostName; char* servName; @@ -826,9 +850,11 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, "\r\n %T\r\n", result) ); return result; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, char* host, @@ -931,6 +957,8 @@ ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, return result; } +#endif + /* ---------------------------------------------------------------------- @@ -948,6 +976,9 @@ ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else ERL_NIF_TERM eifn, result; char ifn[IF_NAMESIZE+1]; @@ -971,10 +1002,12 @@ ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, NDBG( ("NET", "nif_if_name2index -> done when result: %T\r\n", result) ); return result; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nif_name2index(ErlNifEnv* env, char* ifn) @@ -987,12 +1020,16 @@ ERL_NIF_TERM nif_name2index(ErlNifEnv* env, NDBG( ("NET", "nif_name2index -> idx: %d\r\n", idx) ); - if (idx == 0) - return esock_make_error_errno(env, get_errno()); - else + if (idx == 0) { + int save_errno = get_errno(); + NDBG( ("NET", "nif_name2index -> failed: %d\r\n", save_errno) ); + return esock_make_error_errno(env, save_errno); + } else { return esock_make_ok2(env, MKI(env, idx)); + } } +#endif @@ -1011,6 +1048,9 @@ ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else ERL_NIF_TERM result; unsigned int idx; @@ -1030,10 +1070,12 @@ ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env, NDBG( ("NET", "nif_if_index2name -> done when result: %T\r\n", result) ); return result; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nif_index2name(ErlNifEnv* env, unsigned int idx) @@ -1054,6 +1096,7 @@ ERL_NIF_TERM nif_index2name(ErlNifEnv* env, return result; } +#endif @@ -1070,6 +1113,9 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else ERL_NIF_TERM result; NDBG( ("NET", "nif_if_names -> entry (%d)\r\n", argc) ); @@ -1083,10 +1129,12 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env, NDBG( ("NET", "nif_if_names -> done when result: %T\r\n", result) ); return result; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nif_names(ErlNifEnv* env) { @@ -1159,6 +1207,7 @@ unsigned int nif_names_length(struct if_nameindex* p) return len; } +#endif // if !defined(__WIN32__) @@ -1171,6 +1220,7 @@ unsigned int nif_names_length(struct if_nameindex* p) * A special case is when there is no flags, which is * represented by the atom undefined. */ +#if !defined(__WIN32__) static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, const ERL_NIF_TERM eflags, @@ -1477,6 +1527,7 @@ char* make_address_info(ErlNifEnv* env, return NULL; } } +#endif // if !defined(__WIN32__) @@ -1553,6 +1604,7 @@ ErlNifFunc net_funcs[] = }; +#if !defined(__WIN32__) static BOOLEAN_T extract_debug(ErlNifEnv* env, ERL_NIF_TERM map) @@ -1565,6 +1617,7 @@ BOOLEAN_T extract_debug(ErlNifEnv* env, return esock_extract_bool_from_map(env, map, debug, NET_NIF_DEBUG_DEFAULT); } +#endif /* ======================================================================= @@ -1574,10 +1627,12 @@ BOOLEAN_T extract_debug(ErlNifEnv* env, static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { +#if !defined(__WIN32__) // We should make it possible to use load_info to get default values data.debug = extract_debug(env, load_info); NDBG( ("NET", "on_load -> entry\r\n") ); +#endif /* +++ Misc atoms +++ */ atom_address_info = MKA(env, str_address_info); diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index fc218f5163..7ccc9cf349 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -2798,6 +2798,9 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else if (argc != 0) { return enif_make_badarg(env); } else { @@ -2831,6 +2834,7 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, return info; } +#endif } @@ -2859,6 +2863,9 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else int key; SGDBG( ("SOCKET", "nif_supports -> entry with %d args\r\n", argc) ); @@ -2871,6 +2878,7 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env, } return nsupports(env, key); +#endif } @@ -2883,6 +2891,7 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env, * by the sockets own debug flag. But since we don't even have a socket * yet, we must use the global debug flag. */ +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports(ErlNifEnv* env, int key) { @@ -2910,8 +2919,10 @@ ERL_NIF_TERM nsupports(ErlNifEnv* env, int key) return result; } +#endif +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports_options(ErlNifEnv* env) { @@ -2935,9 +2946,11 @@ ERL_NIF_TERM nsupports_options(ErlNifEnv* env) return optsL; } +#endif +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env) { @@ -3197,9 +3210,11 @@ ERL_NIF_TERM nsupports_options_socket(ErlNifEnv* env) return optsL; } +#endif +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env) { @@ -3496,9 +3511,11 @@ ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env) return optsL; } +#endif +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env) { @@ -3746,9 +3763,11 @@ ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env) return optsL; } +#endif +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env) { @@ -3841,9 +3860,11 @@ ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env) return optsL; } +#endif +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env) { @@ -3864,9 +3885,11 @@ ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env) return optsL; } +#endif +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env) { @@ -4070,9 +4093,11 @@ ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env) return optsL; } +#endif +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports_sctp(ErlNifEnv* env) { @@ -4086,9 +4111,11 @@ ERL_NIF_TERM nsupports_sctp(ErlNifEnv* env) return supports; } +#endif +#if !defined(__WIN32__) static ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env) { @@ -4103,6 +4130,7 @@ ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env) return supports; } +#endif @@ -4125,6 +4153,9 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else int edomain, etype, eproto; int domain, type, proto; char* netns; @@ -4183,6 +4214,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, "\r\n", result) ); return result; +#endif } @@ -4194,6 +4226,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, * by the sockets own debug flag. But since we don't even have a socket * yet, we must use the global debug flag. */ +#if !defined(__WIN32__) static ERL_NIF_TERM nopen(ErlNifEnv* env, int domain, int type, int protocol, @@ -4310,6 +4343,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, return esock_make_ok2(env, res); } +#endif @@ -4320,6 +4354,7 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, * Retreive the current namespace and set the new. * Return result and previous namespace if successfull. */ +#if !defined(__WIN32__) static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err) { @@ -4368,11 +4403,13 @@ BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err) return TRUE; } } +#endif /* *** restore network namespace *** * Restore the previous namespace (see above). */ +#if !defined(__WIN32__) static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err) { @@ -4408,7 +4445,8 @@ BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err) *err = 0; return TRUE; } -#endif +#endif // if !defined(__WIN32__) +#endif // ifdef HAVE_SETNS @@ -4427,6 +4465,9 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM eSockAddr; SocketAddress sockAddr; @@ -4462,9 +4503,11 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, return esock_make_error_str(env, xres); return nbind(env, descP, &sockAddr, addrLen); +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nbind(ErlNifEnv* env, SocketDescriptor* descP, @@ -4500,6 +4543,7 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, return esock_make_ok2(env, MKI(env, ntohs_port)); } +#endif @@ -4521,6 +4565,9 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM eSockAddr; char* xres; @@ -4550,9 +4597,11 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, } return nconnect(env, descP); +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nconnect(ErlNifEnv* env, SocketDescriptor* descP) @@ -4614,6 +4663,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, } } +#endif /* ---------------------------------------------------------------------- @@ -4630,6 +4680,9 @@ ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; /* Extract arguments and perform preliminary validation */ @@ -4641,12 +4694,14 @@ ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, return nfinalize_connection(env, descP); +#endif } /* *** nfinalize_connection *** * Perform the final check to verify a connection. */ +#if !defined(__WIN32__) static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, SocketDescriptor* descP) @@ -4667,11 +4722,13 @@ ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, return esock_atom_ok; } +#endif /* *** verify_is_connected *** * Check if a connection has been established. */ +#if !defined(__WIN32__) static BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err) { @@ -4718,6 +4775,7 @@ BOOLEAN_T verify_is_connected(SocketDescriptor* descP, int* err) return TRUE; } +#endif @@ -4737,6 +4795,9 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; int backlog; @@ -4760,10 +4821,12 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, "\r\n", descP->sock, argv[0], backlog) ); return nlisten(env, descP, backlog); +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nlisten(ErlNifEnv* env, SocketDescriptor* descP, @@ -4782,6 +4845,7 @@ ERL_NIF_TERM nlisten(ErlNifEnv* env, return esock_atom_ok; } +#endif @@ -4801,6 +4865,9 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM ref; @@ -4810,8 +4877,8 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, if ((argc != 2) || !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { - return enif_make_badarg(env); - } + return enif_make_badarg(env); + } ref = argv[1]; if (IS_CLOSED(descP) || IS_CLOSING(descP)) @@ -4824,9 +4891,11 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, "\r\n", descP->sock, argv[0], ref) ); return naccept(env, descP, ref); +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM naccept(ErlNifEnv* env, SocketDescriptor* descP, @@ -4854,11 +4923,13 @@ ERL_NIF_TERM naccept(ErlNifEnv* env, return res; } +#endif /* *** naccept_listening *** * We have no active acceptor and no acceptors in queue. */ +#if !defined(__WIN32__) static ERL_NIF_TERM naccept_listening(ErlNifEnv* env, SocketDescriptor* descP, @@ -5003,6 +5074,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, return esock_make_ok2(env, accRef); } } +#endif /* *** naccept_accepting *** @@ -5010,6 +5082,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, * If the pid of the calling process is not the pid of the "current process", * push the requester onto the queue. */ +#if !defined(__WIN32__) static ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, SocketDescriptor* descP, @@ -5170,6 +5243,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, return esock_make_ok2(env, accRef); } } +#endif @@ -5191,6 +5265,9 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM sockRef, sendRef; ErlNifBinary sndData; @@ -5245,9 +5322,11 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, MUNLOCK(descP->writeMtx); return res; +#endif } + /* *** nsend *** * * Do the actual send. @@ -5255,6 +5334,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, * analyze the result. If we are done, another writer may be * scheduled (if there is one in the writer queue). */ +#if !defined(__WIN32__) static ERL_NIF_TERM nsend(ErlNifEnv* env, SocketDescriptor* descP, @@ -5290,6 +5370,7 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, sockRef, sendRef); } +#endif @@ -5312,6 +5393,9 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM sockRef, sendRef; ErlNifBinary sndData; @@ -5375,9 +5459,11 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, "\r\n", res) ); return res; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nsendto(ErlNifEnv* env, SocketDescriptor* descP, @@ -5421,6 +5507,7 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, return send_check_result(env, descP, written, dataP->size, save_errno, sockRef, sendRef); } +#endif @@ -5442,6 +5529,9 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else ERL_NIF_TERM res, sockRef, sendRef, eMsgHdr; SocketDescriptor* descP; unsigned int eflags; @@ -5487,9 +5577,11 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, "\r\n", res) ); return res; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nsendmsg(ErlNifEnv* env, SocketDescriptor* descP, @@ -5643,6 +5735,7 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, return res; } +#endif @@ -5736,6 +5829,9 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; int len; @@ -5778,7 +5874,7 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; - +#endif } @@ -5787,6 +5883,7 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, * allocating a binary (of the specified or default * size) and then throwing it away... */ +#if !defined(__WIN32__) static ERL_NIF_TERM nrecv(ErlNifEnv* env, SocketDescriptor* descP, @@ -5843,6 +5940,7 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, sockRef, recvRef); } +#endif @@ -5875,6 +5973,9 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; unsigned int bufSz; @@ -5936,7 +6037,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; - +#endif } @@ -5945,6 +6046,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, * allocating a binary (of the specified or default * size) and then throwing it away... */ +#if !defined(__WIN32__) static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, SocketDescriptor* descP, @@ -6003,6 +6105,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, sockRef, recvRef); } +#endif @@ -6039,6 +6142,9 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; unsigned int bufSz; @@ -6103,7 +6209,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; - +#endif } @@ -6112,6 +6218,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, * allocating a binary (of the specified or default * size) and then throwing it away... */ +#if !defined(__WIN32__) static ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, SocketDescriptor* descP, @@ -6199,6 +6306,7 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, sockRef, recvRef); } +#endif @@ -6217,6 +6325,9 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; if ((argc != 1) || @@ -6228,9 +6339,11 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, return esock_make_error(env, atom_closed); return nclose(env, descP); +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nclose(ErlNifEnv* env, SocketDescriptor* descP) @@ -6350,6 +6463,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, return reply; } +#endif @@ -6368,6 +6482,9 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; /* Extract arguments and perform preliminary validation */ @@ -6378,13 +6495,14 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, } return nfinalize_close(env, descP); - +#endif } /* *** nfinalize_close *** * Perform the final step in the socket close. */ +#if !defined(__WIN32__) static ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, SocketDescriptor* descP) @@ -6428,6 +6546,7 @@ ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, return reply; } +#endif @@ -6447,6 +6566,9 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; unsigned int ehow; int how; @@ -6464,10 +6586,12 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, return enif_make_badarg(env); return nshutdown(env, descP, how); +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nshutdown(ErlNifEnv* env, SocketDescriptor* descP, @@ -6495,6 +6619,7 @@ ERL_NIF_TERM nshutdown(ErlNifEnv* env, return reply; } +#endif @@ -6522,6 +6647,9 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP = NULL; int eLevel, level = -1; int eOpt; @@ -6578,9 +6706,11 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, "\r\n", result) ); return result; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nsetopt(ErlNifEnv* env, SocketDescriptor* descP, @@ -9926,6 +10056,7 @@ int socket_setopt(int sock, int level, int opt, return res; } +#endif // if !defined(__WIN32__) @@ -9951,6 +10082,9 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; int eLevel, level = -1; ERL_NIF_TERM eIsEncoded, eOpt; @@ -9984,10 +10118,12 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, return esock_make_error(env, esock_atom_einval); return ngetopt(env, descP, isEncoded, isOTP, level, eOpt); +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM ngetopt(ErlNifEnv* env, SocketDescriptor* descP, @@ -12646,6 +12782,7 @@ ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, return result; } +#endif // if !defined(__WIN32__) @@ -12664,6 +12801,9 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM res; @@ -12689,10 +12829,12 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nif_sockname -> done with res = %T\r\n", res) ); return res; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM nsockname(ErlNifEnv* env, SocketDescriptor* descP) @@ -12714,6 +12856,7 @@ ERL_NIF_TERM nsockname(ErlNifEnv* env, return esock_make_ok2(env, esa); } } +#endif @@ -12732,6 +12875,9 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM res; @@ -12757,10 +12903,12 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nif_peername -> done with res = %T\r\n", res) ); return res; +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM npeername(ErlNifEnv* env, SocketDescriptor* descP) @@ -12782,6 +12930,7 @@ ERL_NIF_TERM npeername(ErlNifEnv* env, return esock_make_ok2(env, esa); } } +#endif @@ -12801,6 +12950,9 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { +#if defined(__WIN32__) + return enif_make_badarg(env); +#else SocketDescriptor* descP; ERL_NIF_TERM op, opRef, result; @@ -12832,10 +12984,11 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, "\r\n", result) ); return result; - +#endif } +#if !defined(__WIN32__) static ERL_NIF_TERM ncancel(ErlNifEnv* env, SocketDescriptor* descP, @@ -13294,6 +13447,8 @@ ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, } } +#endif // if !defined(__WIN32__) + @@ -13307,6 +13462,7 @@ ERL_NIF_TERM ncancel_mode_select(ErlNifEnv* env, * Checks if we have a current writer and if that is us. If not, then we must * be made to wait for our turn. This is done by pushing us unto the writer queue. */ +#if !defined(__WIN32__) static BOOLEAN_T send_check_writer(ErlNifEnv* env, SocketDescriptor* descP, @@ -16087,6 +16243,8 @@ int compare_pids(ErlNifEnv* env, return enif_is_identical(p1, p2); } +#endif // if !defined(__WIN32__) + /* ---------------------------------------------------------------------- @@ -16098,6 +16256,7 @@ int compare_pids(ErlNifEnv* env, * * Note that only a subset is supported. */ +#if !defined(__WIN32__) static BOOLEAN_T edomain2domain(int edomain, int* domain) { @@ -16612,6 +16771,7 @@ char* esock_send_msg(ErlNifEnv* env, else return NULL; } +#endif // #if defined(__WIN32__) @@ -16624,6 +16784,7 @@ char* esock_send_msg(ErlNifEnv* env, * * Search for a pid in the acceptor queue. */ +#if !defined(__WIN32__) static BOOLEAN_T acceptor_search4pid(ErlNifEnv* env, SocketDescriptor* descP, @@ -16993,6 +17154,7 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, return FALSE; } +#endif // if !defined(__WIN32__) @@ -17001,6 +17163,7 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, * ---------------------------------------------------------------------- */ +#if !defined(__WIN32__) static BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc) { @@ -17032,6 +17195,8 @@ void cnt_dec(uint32_t* cnt, uint32_t dec) return; } +#endif // if !defined(__WIN32__) + @@ -17040,6 +17205,7 @@ void cnt_dec(uint32_t* cnt, uint32_t dec) * ---------------------------------------------------------------------- */ +#if !defined(__WIN32__) static int esock_monitor(const char* slogan, ErlNifEnv* env, @@ -17116,6 +17282,8 @@ void esock_monitor_init(ESockMonitor* monP) monP->raw[i] = 0; } +#endif // if !defined(__WIN32__) + /* static @@ -17139,6 +17307,7 @@ int esock_monitor_compare(const ErlNifMonitor* mon1, static void socket_dtor(ErlNifEnv* env, void* obj) { +#if !defined(__WIN32__) SocketDescriptor* descP = (SocketDescriptor*) obj; enif_clear_env(descP->env); @@ -17149,7 +17318,7 @@ void socket_dtor(ErlNifEnv* env, void* obj) MDESTROY(descP->readMtx); MDESTROY(descP->accMtx); MDESTROY(descP->closeMtx); - +#endif } @@ -17181,6 +17350,7 @@ void socket_dtor(ErlNifEnv* env, void* obj) static void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) { +#if !defined(__WIN32__) SocketDescriptor* descP = (SocketDescriptor*) obj; /* @@ -17423,7 +17593,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) SSDBG( descP, ("SOCKET", "socket_stop -> done (%d, %d)\r\n", descP->sock, fd) ); - +#endif // if !defined(__WIN32__) } @@ -17431,6 +17601,7 @@ void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) * nif_abort message with the specified reason to each member, * and if the 'free' argument is TRUE, the queue will be emptied. */ +#if !defined(__WIN32__) static void inform_waiting_procs(ErlNifEnv* env, SocketDescriptor* descP, @@ -17474,7 +17645,7 @@ void inform_waiting_procs(ErlNifEnv* env, q->last = NULL; } } - +#endif // if !defined(__WIN32__) /* ========================================================================= @@ -17487,6 +17658,7 @@ void socket_down(ErlNifEnv* env, const ErlNifPid* pid, const ErlNifMonitor* mon) { +#if !defined(__WIN32__) SocketDescriptor* descP = (SocketDescriptor*) obj; ESockMonitor* monP = (ESockMonitor*) mon; @@ -17675,6 +17847,7 @@ void socket_down(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "socket_down -> done\r\n") ); +#endif // if !defined(__WIN32__) } @@ -17684,6 +17857,7 @@ void socket_down(ErlNifEnv* env, * Check and then handle a downed acceptor process. * */ +#if !defined(__WIN32__) static void socket_down_acceptor(ErlNifEnv* env, SocketDescriptor* descP, @@ -17878,6 +18052,7 @@ void socket_down_reader(ErlNifEnv* env, reader_unqueue(env, descP, pid); } } +#endif // if !defined(__WIN32__) @@ -17928,6 +18103,7 @@ ErlNifFunc socket_funcs[] = }; +#if !defined(__WIN32__) static BOOLEAN_T extract_debug(ErlNifEnv* env, ERL_NIF_TERM map) @@ -17953,6 +18129,7 @@ BOOLEAN_T extract_iow(ErlNifEnv* env, return esock_extract_bool_from_map(env, map, iow, SOCKET_NIF_IOW_DEFAULT); } +#endif // if !defined(__WIN32__) @@ -17963,6 +18140,7 @@ BOOLEAN_T extract_iow(ErlNifEnv* env, static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { +#if !defined(__WIN32__) esock_dbg_init(ESOCK_DBGOUT_DEFAULT); // esock_dbg_init(ESOCK_DBGOUT_UNIQUE); @@ -17982,6 +18160,7 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) data.numProtoTCP = 0; data.numProtoUDP = 0; data.numProtoSCTP = 0; +#endif /* +++ Misc atoms +++ */ atom_adaptation_layer = MKA(env, str_adaptation_layer); -- cgit v1.2.3 From 875825874d4a8d52ec5cc593f5024afc696c29df Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 31 Jan 2019 11:34:03 +0100 Subject: [socket-nif] nosup expection of win32 and type(s) replacements The nif callback functions (nif_open) now instead cause an 'nosup' exception if called (instead of badarg). The basic type uint16_t, uint32_t and int32_t (C99) replaced "own" (that is, defined by "us") types Uint16, Uint32 and Sint32. The point of this is that our Windows build system seems to be a bit lacking when it comes to types... Removed "some stuff" that was if-defed. Different solution when win32 support for sockets has been improved. Make sure the socket_*.c util modules are not included in the building for windows. OTP-15526 --- erts/emulator/nifs/common/net_nif.c | 18 +- erts/emulator/nifs/common/socket_nif.c | 263 +++++++++++++----------------- erts/emulator/nifs/common/socket_tarray.c | 24 +-- erts/emulator/nifs/common/socket_tarray.h | 6 +- erts/emulator/nifs/common/socket_util.c | 8 +- 5 files changed, 147 insertions(+), 172 deletions(-) (limited to 'erts/emulator/nifs') diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c index f51975b564..d48112133f 100644 --- a/erts/emulator/nifs/common/net_nif.c +++ b/erts/emulator/nifs/common/net_nif.c @@ -462,7 +462,7 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else ERL_NIF_TERM info, tmp; @@ -498,7 +498,7 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else ERL_NIF_TERM ecmd, result; @@ -569,7 +569,7 @@ ERL_NIF_TERM nif_gethostname(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else ERL_NIF_TERM result; @@ -645,7 +645,7 @@ ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else ERL_NIF_TERM result; ERL_NIF_TERM eSockAddr, eFlags; @@ -795,7 +795,7 @@ ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else ERL_NIF_TERM result, eHostName, eServName; //, eHints; char* hostName; @@ -977,7 +977,7 @@ ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else ERL_NIF_TERM eifn, result; char ifn[IF_NAMESIZE+1]; @@ -1049,7 +1049,7 @@ ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else ERL_NIF_TERM result; unsigned int idx; @@ -1114,7 +1114,7 @@ ERL_NIF_TERM nif_if_names(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else ERL_NIF_TERM result; @@ -1673,7 +1673,9 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) ERL_NIF_RT_CREATE, NULL); +#if !defined(__WIN32__) NDBG( ("NET", "on_load -> done\r\n") ); +#endif return !net; } diff --git a/erts/emulator/nifs/common/socket_nif.c b/erts/emulator/nifs/common/socket_nif.c index 7ccc9cf349..4b8d65ce6a 100644 --- a/erts/emulator/nifs/common/socket_nif.c +++ b/erts/emulator/nifs/common/socket_nif.c @@ -112,7 +112,7 @@ -#else /* !__WIN32__ */ +#else /* ifdef __WIN32__ */ #include #ifdef NETDB_H_NEEDS_IN_H @@ -305,7 +305,12 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #endif #include "sys.h" -#endif /* !__WIN32__ */ +/* Socket stuff */ +#define INVALID_SOCKET -1 +// #define INVALID_EVENT -1 +#define SOCKET_ERROR -1 + +#endif /* ifdef __WIN32__ */ #include @@ -332,9 +337,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; /* Socket stuff */ -#define INVALID_SOCKET -1 #define INVALID_EVENT -1 -#define SOCKET_ERROR -1 #define SOCKET int #define HANDLE long int @@ -737,7 +740,7 @@ static unsigned long one_value = 1; typedef union { ErlNifMonitor mon; - uint32_t raw[4]; + Uint32 raw[4]; } ESockMonitor; @@ -786,11 +789,11 @@ typedef struct { SocketRequestor* currentWriterP; // NULL or points to currentWriter SocketRequestQueue writersQ; BOOLEAN_T isWritable; - uint32_t writePkgCnt; - uint32_t writeByteCnt; - uint32_t writeTries; - uint32_t writeWaits; - uint32_t writeFails; + Uint32 writePkgCnt; + Uint32 writeByteCnt; + Uint32 writeTries; + Uint32 writeWaits; + Uint32 writeFails; /* +++ Read stuff +++ */ ErlNifMutex* readMtx; @@ -799,11 +802,11 @@ typedef struct { SocketRequestQueue readersQ; BOOLEAN_T isReadable; ErlNifBinary rbuffer; // DO WE NEED THIS - uint32_t readCapacity; // DO WE NEED THIS - uint32_t readPkgCnt; - uint32_t readByteCnt; - uint32_t readTries; - uint32_t readWaits; + Uint32 readCapacity; // DO WE NEED THIS + Uint32 readPkgCnt; + Uint32 readByteCnt; + Uint32 readTries; + Uint32 readWaits; /* +++ Accept stuff +++ */ ErlNifMutex* accMtx; @@ -875,17 +878,17 @@ typedef struct { BOOLEAN_T iow; ErlNifMutex* cntMtx; - uint32_t numSockets; - uint32_t numTypeStreams; - uint32_t numTypeDGrams; - uint32_t numTypeSeqPkgs; - uint32_t numDomainInet; - uint32_t numDomainInet6; - uint32_t numDomainLocal; - uint32_t numProtoIP; - uint32_t numProtoTCP; - uint32_t numProtoUDP; - uint32_t numProtoSCTP; + Uint32 numSockets; + Uint32 numTypeStreams; + Uint32 numTypeDGrams; + Uint32 numTypeSeqPkgs; + Uint32 numDomainInet; + Uint32 numDomainInet6; + Uint32 numDomainLocal; + Uint32 numProtoIP; + Uint32 numProtoTCP; + Uint32 numProtoUDP; + Uint32 numProtoSCTP; } SocketData; @@ -1034,14 +1037,14 @@ static ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, - uint16_t bufSz, + Uint16 bufSz, int flags); static ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, - uint16_t bufLen, - uint16_t ctrlLen, + Uint16 bufLen, + Uint16 ctrlLen, int flags); static ERL_NIF_TERM nclose(ErlNifEnv* env, SocketDescriptor* descP); @@ -1237,7 +1240,7 @@ static ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, ERL_NIF_TERM eVal); static BOOLEAN_T decode_ip_msfilter_mode(ErlNifEnv* env, ERL_NIF_TERM eVal, - uint32_t* mode); + Uint32* mode); static ERL_NIF_TERM nsetopt_lvl_ip_msfilter_set(ErlNifEnv* env, SOCKET sock, struct ip_msfilter* msfP, @@ -2177,7 +2180,7 @@ static BOOLEAN_T decode_bool(ErlNifEnv* env, static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, int* opt, - uint16_t* valueType, + Uint16* valueType, int* valueSz); // static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal); static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val); @@ -2222,8 +2225,8 @@ static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err); static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err); #endif -static BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc); -static void cnt_dec(uint32_t* cnt, uint32_t dec); +static BOOLEAN_T cnt_inc(Uint32* cnt, Uint32 inc); +static void cnt_dec(Uint32* cnt, Uint32 dec); static void inc_socket(int domain, int type, int protocol); static void dec_socket(int domain, int type, int protocol); @@ -2799,7 +2802,7 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else if (argc != 0) { return enif_make_badarg(env); @@ -2864,7 +2867,7 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else int key; @@ -4154,7 +4157,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else int edomain, etype, eproto; int domain, type, proto; @@ -4214,7 +4217,7 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, "\r\n", result) ); return result; -#endif +#endif // if defined(__WIN32__) } @@ -4321,29 +4324,11 @@ ERL_NIF_TERM nopen(ErlNifEnv* env, return esock_make_error(env, atom_exmon); -#ifdef __WIN32__ - /* - */ - SELECT(env, - event, - (ERL_NIF_SELECT_READ), - descP, NULL, esock_atom_undefined); -#endif - inc_socket(domain, type, protocol); return esock_make_ok2(env, res); } -#endif +#endif // if !defined(__WIN32__) @@ -4403,7 +4388,7 @@ BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err) return TRUE; } } -#endif +#endif // if !defined(__WIN32__) /* *** restore network namespace *** @@ -4466,7 +4451,7 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM eSockAddr; @@ -4503,7 +4488,7 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, return esock_make_error_str(env, xres); return nbind(env, descP, &sockAddr, addrLen); -#endif +#endif // if defined(__WIN32__) } @@ -4543,7 +4528,7 @@ ERL_NIF_TERM nbind(ErlNifEnv* env, return esock_make_ok2(env, MKI(env, ntohs_port)); } -#endif +#endif // if !defined(__WIN32__) @@ -4566,7 +4551,7 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM eSockAddr; @@ -4597,7 +4582,7 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, } return nconnect(env, descP); -#endif +#endif // if !defined(__WIN32__) } @@ -4663,7 +4648,7 @@ ERL_NIF_TERM nconnect(ErlNifEnv* env, } } -#endif +#endif // if !defined(__WIN32__) /* ---------------------------------------------------------------------- @@ -4681,7 +4666,7 @@ ERL_NIF_TERM nif_finalize_connection(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; @@ -4796,7 +4781,7 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; int backlog; @@ -4821,7 +4806,7 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, "\r\n", descP->sock, argv[0], backlog) ); return nlisten(env, descP, backlog); -#endif +#endif // if defined(__WIN32__) } @@ -4845,7 +4830,7 @@ ERL_NIF_TERM nlisten(ErlNifEnv* env, return esock_atom_ok; } -#endif +#endif // if !defined(__WIN32__) @@ -4866,7 +4851,7 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM ref; @@ -4891,7 +4876,7 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, "\r\n", descP->sock, argv[0], ref) ); return naccept(env, descP, ref); -#endif +#endif // if defined(__WIN32__) } @@ -4923,7 +4908,7 @@ ERL_NIF_TERM naccept(ErlNifEnv* env, return res; } -#endif +#endif // if !defined(__WIN32__) /* *** naccept_listening *** @@ -5059,14 +5044,6 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, accDescP->remote = remote; SET_NONBLOCKING(accDescP->sock); -#ifdef __WIN32__ - /* See 'What is the point of this?' above */ - SELECT(env, - descP->sock, - (ERL_NIF_SELECT_READ), - descP, NULL, esock_atom_undefined); -#endif - accDescP->state = SOCKET_STATE_CONNECTED; accDescP->isReadable = TRUE; accDescP->isWritable = TRUE; @@ -5074,7 +5051,7 @@ ERL_NIF_TERM naccept_listening(ErlNifEnv* env, return esock_make_ok2(env, accRef); } } -#endif +#endif // if !defined(__WIN32__) /* *** naccept_accepting *** @@ -5203,14 +5180,6 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, accDescP->remote = remote; SET_NONBLOCKING(accDescP->sock); -#ifdef __WIN32__ - /* See 'What is the point of this?' above */ - SELECT(env, - descP->sock, - (ERL_NIF_SELECT_READ), - descP, NULL, esock_atom_undefined); -#endif - accDescP->state = SOCKET_STATE_CONNECTED; accDescP->isReadable = TRUE; accDescP->isWritable = TRUE; @@ -5243,7 +5212,7 @@ ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, return esock_make_ok2(env, accRef); } } -#endif +#endif // if !defined(__WIN32__) @@ -5266,7 +5235,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM sockRef, sendRef; @@ -5322,7 +5291,7 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, MUNLOCK(descP->writeMtx); return res; -#endif +#endif // if defined(__WIN32__) } @@ -5370,7 +5339,7 @@ ERL_NIF_TERM nsend(ErlNifEnv* env, sockRef, sendRef); } -#endif +#endif // if !defined(__WIN32__) @@ -5394,7 +5363,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM sockRef, sendRef; @@ -5459,7 +5428,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, "\r\n", res) ); return res; -#endif +#endif // if defined(__WIN32__) } @@ -5507,7 +5476,7 @@ ERL_NIF_TERM nsendto(ErlNifEnv* env, return send_check_result(env, descP, written, dataP->size, save_errno, sockRef, sendRef); } -#endif +#endif // if !defined(__WIN32__) @@ -5530,7 +5499,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else ERL_NIF_TERM res, sockRef, sendRef, eMsgHdr; SocketDescriptor* descP; @@ -5577,7 +5546,7 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, "\r\n", res) ); return res; -#endif +#endif // if defined(__WIN32__) } @@ -5735,7 +5704,7 @@ ERL_NIF_TERM nsendmsg(ErlNifEnv* env, return res; } -#endif +#endif // if !defined(__WIN32__) @@ -5830,7 +5799,7 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; @@ -5874,7 +5843,7 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; -#endif +#endif // if defined(__WIN32__) } @@ -5940,7 +5909,7 @@ ERL_NIF_TERM nrecv(ErlNifEnv* env, sockRef, recvRef); } -#endif +#endif // if !defined(__WIN32__) @@ -5974,7 +5943,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; @@ -6037,7 +6006,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; -#endif +#endif // if defined(__WIN32__) } @@ -6052,7 +6021,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, - uint16_t len, + Uint16 len, int flags) { SocketAddress fromAddr; @@ -6105,7 +6074,7 @@ ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, sockRef, recvRef); } -#endif +#endif // if !defined(__WIN32__) @@ -6143,7 +6112,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; @@ -6209,7 +6178,7 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; -#endif +#endif // if defined(__WIN32__) } @@ -6224,8 +6193,8 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, SocketDescriptor* descP, ERL_NIF_TERM sockRef, ERL_NIF_TERM recvRef, - uint16_t bufLen, - uint16_t ctrlLen, + Uint16 bufLen, + Uint16 ctrlLen, int flags) { unsigned int addrLen; @@ -6306,7 +6275,7 @@ ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, sockRef, recvRef); } -#endif +#endif // if !defined(__WIN32__) @@ -6326,7 +6295,7 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; @@ -6339,7 +6308,7 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, return esock_make_error(env, atom_closed); return nclose(env, descP); -#endif +#endif // if defined(__WIN32__) } @@ -6463,7 +6432,7 @@ ERL_NIF_TERM nclose(ErlNifEnv* env, return reply; } -#endif +#endif // if !defined(__WIN32__) @@ -6483,7 +6452,7 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; @@ -6495,7 +6464,7 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, } return nfinalize_close(env, descP); -#endif +#endif // if defined(__WIN32__) } @@ -6546,7 +6515,7 @@ ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, return reply; } -#endif +#endif // if !defined(__WIN32__) @@ -6567,7 +6536,7 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; unsigned int ehow; @@ -6586,7 +6555,7 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, return enif_make_badarg(env); return nshutdown(env, descP, how); -#endif +#endif // if defined(__WIN32__) } @@ -6619,7 +6588,7 @@ ERL_NIF_TERM nshutdown(ErlNifEnv* env, return reply; } -#endif +#endif // if !defined(__WIN32__) @@ -6648,7 +6617,7 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP = NULL; int eLevel, level = -1; @@ -6706,7 +6675,7 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, "\r\n", result) ); return result; -#endif +#endif // if defined(__WIN32__) } @@ -7840,7 +7809,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, return nsetopt_lvl_ip_msfilter_set(env, descP->sock, NULL, 0); } else { struct ip_msfilter* msfP; - uint32_t msfSz; + Uint32 msfSz; ERL_NIF_TERM eMultiAddr, eInterface, eFMode, eSList, elem, tail; size_t sz; unsigned int slistLen, idx; @@ -7913,7 +7882,7 @@ ERL_NIF_TERM nsetopt_lvl_ip_msfilter(ErlNifEnv* env, static BOOLEAN_T decode_ip_msfilter_mode(ErlNifEnv* env, ERL_NIF_TERM eVal, - uint32_t* mode) + Uint32* mode) { BOOLEAN_T result; @@ -9325,7 +9294,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, int res; size_t sz; unsigned int tmp; - int32_t tmpAssocId; + Sint32 tmpAssocId; SSDBG( descP, ("SOCKET", "nsetopt_lvl_sctp_associnfo -> entry with" @@ -9380,11 +9349,11 @@ ERL_NIF_TERM nsetopt_lvl_sctp_associnfo(ErlNifEnv* env, if (!GET_UINT(env, eMaxRxt, &tmp)) return esock_make_error(env, esock_atom_einval); - assocParams.sasoc_asocmaxrxt = (uint16_t) tmp; + assocParams.sasoc_asocmaxrxt = (Uint16) tmp; if (!GET_UINT(env, eNumPeerDests, &tmp)) return esock_make_error(env, esock_atom_einval); - assocParams.sasoc_number_peer_destinations = (uint16_t) tmp; + assocParams.sasoc_number_peer_destinations = (Uint16) tmp; if (!GET_UINT(env, ePeerRWND, &assocParams.sasoc_peer_rwnd)) return esock_make_error(env, esock_atom_einval); @@ -9600,19 +9569,19 @@ ERL_NIF_TERM nsetopt_lvl_sctp_initmsg(ErlNifEnv* env, if (!GET_UINT(env, eNumOut, &tmp)) return esock_make_error(env, esock_atom_einval); - initMsg.sinit_num_ostreams = (uint16_t) tmp; + initMsg.sinit_num_ostreams = (Uint16) tmp; if (!GET_UINT(env, eMaxIn, &tmp)) return esock_make_error(env, esock_atom_einval); - initMsg.sinit_max_instreams = (uint16_t) tmp; + initMsg.sinit_max_instreams = (Uint16) tmp; if (!GET_UINT(env, eMaxAttempts, &tmp)) return esock_make_error(env, esock_atom_einval); - initMsg.sinit_max_attempts = (uint16_t) tmp; + initMsg.sinit_max_attempts = (Uint16) tmp; if (!GET_UINT(env, eMaxInitTO, &tmp)) return esock_make_error(env, esock_atom_einval); - initMsg.sinit_max_init_timeo = (uint16_t) tmp; + initMsg.sinit_max_init_timeo = (Uint16) tmp; SSDBG( descP, ("SOCKET", "nsetopt_lvl_sctp_initmsg -> set initmsg option\r\n") ); @@ -9675,7 +9644,7 @@ ERL_NIF_TERM nsetopt_lvl_sctp_rtoinfo(ErlNifEnv* env, struct sctp_rtoinfo rtoInfo; int res; size_t sz; - int32_t tmpAssocId; + Sint32 tmpAssocId; SSDBG( descP, ("SOCKET", "nsetopt_lvl_sctp_rtoinfo -> entry with" @@ -10083,7 +10052,7 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; int eLevel, level = -1; @@ -10118,7 +10087,7 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, return esock_make_error(env, esock_atom_einval); return ngetopt(env, descP, isEncoded, isOTP, level, eOpt); -#endif +#endif // if defined(__WIN32__) } @@ -10461,7 +10430,7 @@ ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, { ERL_NIF_TERM result = enif_make_badarg(env); int opt; - uint16_t valueType; + Uint16 valueType; SOCKOPTLEN_T valueSz; SSDBG( descP, @@ -12802,7 +12771,7 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM res; @@ -12829,7 +12798,7 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nif_sockname -> done with res = %T\r\n", res) ); return res; -#endif +#endif // if defined(__WIN32__) } @@ -12856,7 +12825,7 @@ ERL_NIF_TERM nsockname(ErlNifEnv* env, return esock_make_ok2(env, esa); } } -#endif +#endif // if !defined(__WIN32__) @@ -12876,7 +12845,7 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM res; @@ -12903,7 +12872,7 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, SSDBG( descP, ("SOCKET", "nif_peername -> done with res = %T\r\n", res) ); return res; -#endif +#endif // if defined(__WIN32__) } @@ -12930,7 +12899,7 @@ ERL_NIF_TERM npeername(ErlNifEnv* env, return esock_make_ok2(env, esa); } } -#endif +#endif // if !defined(__WIN32__) @@ -12951,7 +12920,7 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, const ERL_NIF_TERM argv[]) { #if defined(__WIN32__) - return enif_make_badarg(env); + return enif_raise_exception(env, MKA(env, "nosup")); #else SocketDescriptor* descP; ERL_NIF_TERM op, opRef, result; @@ -12984,7 +12953,7 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, "\r\n", result) ); return result; -#endif +#endif // if !defined(__WIN32__) } @@ -15974,7 +15943,7 @@ void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) */ static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, - int* opt, uint16_t* valueType, int* valueSz) + int* opt, Uint16* valueType, int* valueSz) { const ERL_NIF_TERM* nativeOptT; int nativeOptTSz; @@ -17165,11 +17134,11 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, #if !defined(__WIN32__) static -BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc) +BOOLEAN_T cnt_inc(Uint32* cnt, Uint32 inc) { BOOLEAN_T wrap; - uint32_t max = 0xFFFFFFFF; - uint32_t current = *cnt; + Uint32 max = 0xFFFFFFFF; + Uint32 current = *cnt; if ((max - inc) >= current) { *cnt += inc; @@ -17184,9 +17153,9 @@ BOOLEAN_T cnt_inc(uint32_t* cnt, uint32_t inc) static -void cnt_dec(uint32_t* cnt, uint32_t dec) +void cnt_dec(Uint32* cnt, Uint32 dec) { - uint32_t current = *cnt; + Uint32 current = *cnt; if (dec > current) *cnt = 0; // The counter cannot be < 0 so this is the best we can do... diff --git a/erts/emulator/nifs/common/socket_tarray.c b/erts/emulator/nifs/common/socket_tarray.c index a327e014c4..def22c4919 100644 --- a/erts/emulator/nifs/common/socket_tarray.c +++ b/erts/emulator/nifs/common/socket_tarray.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2018-2018. All Rights Reserved. + * Copyright Ericsson AB 2018-2019. 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. @@ -24,12 +24,16 @@ * */ -#include +/* #ifdef HAVE_CONFIG_H */ +/* #include "config.h" */ +/* #endif */ + #include #include #include "socket_int.h" +#include #include "socket_util.h" #include "socket_tarray.h" @@ -40,8 +44,8 @@ */ typedef struct { - uint32_t sz; - uint32_t idx; + Uint32 sz; + Uint32 idx; ERL_NIF_TERM* array; } SocketTArrayInt; @@ -51,7 +55,7 @@ typedef struct { */ static void esock_tarray_add1(SocketTArrayInt* taP, ERL_NIF_TERM t); -static void esock_tarray_ensure_fits(SocketTArrayInt* taP, uint32_t needs); +static void esock_tarray_ensure_fits(SocketTArrayInt* taP, Uint32 needs); /* ---------------------------------------------------------------------- @@ -59,7 +63,7 @@ static void esock_tarray_ensure_fits(SocketTArrayInt* taP, uint32_t needs); */ extern -void* esock_tarray_create(uint32_t sz) +void* esock_tarray_create(Uint32 sz) { SocketTArrayInt* tarrayP; @@ -87,7 +91,7 @@ void esock_tarray_delete(SocketTArray ta) extern -uint32_t esock_tarray_sz(SocketTArray a) +Uint32 esock_tarray_sz(SocketTArray a) { return ( ((SocketTArrayInt*) a)->idx ); } @@ -125,11 +129,11 @@ void esock_tarray_add1(SocketTArrayInt* taP, ERL_NIF_TERM t) } static -void esock_tarray_ensure_fits(SocketTArrayInt* taP, uint32_t needs) +void esock_tarray_ensure_fits(SocketTArrayInt* taP, Uint32 needs) { if (taP->sz < (taP->idx + needs)) { - uint32_t newSz = (needs < taP->sz) ? 2*taP->sz : 2*needs; - void* mem = REALLOC(taP->array, newSz * sizeof(ERL_NIF_TERM)); + Uint32 newSz = (needs < taP->sz) ? 2*taP->sz : 2*needs; + void* mem = REALLOC(taP->array, newSz * sizeof(ERL_NIF_TERM)); ESOCK_ASSERT( (mem != NULL) ); diff --git a/erts/emulator/nifs/common/socket_tarray.h b/erts/emulator/nifs/common/socket_tarray.h index 4d78d2ccb7..4f1152fb9e 100644 --- a/erts/emulator/nifs/common/socket_tarray.h +++ b/erts/emulator/nifs/common/socket_tarray.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2018-2018. All Rights Reserved. + * Copyright Ericsson AB 2018-2019. 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. @@ -29,9 +29,9 @@ typedef void* SocketTArray; -extern SocketTArray esock_tarray_create(uint32_t sz); +extern SocketTArray esock_tarray_create(Uint32 sz); extern void esock_tarray_delete(SocketTArray ta); -extern uint32_t esock_tarray_sz(SocketTArray ta); +extern Uint32 esock_tarray_sz(SocketTArray ta); extern void esock_tarray_add(SocketTArray ta, ERL_NIF_TERM t); extern void esock_tarray_tolist(SocketTArray ta, ErlNifEnv* env, diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 766d3724c1..f6e4781977 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2018-2018. All Rights Reserved. + * Copyright Ericsson AB 2018-2019. 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. @@ -31,9 +31,9 @@ #include #include "socket_int.h" +#include "sys.h" #include "socket_util.h" #include "socket_dbg.h" -#include "sys.h" /* We don't have a "debug flag" to check here, so we * should use the compile debug flag, whatever that is... @@ -89,7 +89,7 @@ char* esock_encode_iov(ErlNifEnv* env, ERL_NIF_TERM* eIOV) { int rem = read; - uint16_t i; + Uint16 i; BOOLEAN_T done = FALSE; ERL_NIF_TERM a[len]; // At most this length @@ -158,7 +158,7 @@ char* esock_decode_iov(ErlNifEnv* env, size_t len, ssize_t* totSize) { - uint16_t i; + Uint16 i; ssize_t sz; ERL_NIF_TERM elem, tail, list; -- cgit v1.2.3