diff options
Diffstat (limited to 'erts/emulator/nifs/common')
-rw-r--r-- | erts/emulator/nifs/common/net_nif.c | 1656 | ||||
-rw-r--r-- | erts/emulator/nifs/common/prim_file_nif.c | 112 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_dbg.c | 148 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_dbg.h | 55 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_int.h | 392 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_nif.c | 18287 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_tarray.c | 143 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_tarray.h | 47 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.c | 1670 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.h | 205 |
10 files changed, 22650 insertions, 65 deletions
diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c new file mode 100644 index 0000000000..252aa3c835 --- /dev/null +++ b/erts/emulator/nifs/common/net_nif.c @@ -0,0 +1,1656 @@ +/* + * %CopyrightBegin% + * + * 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. + * 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. + * ---------------------------------------------------------------------- + * + */ + +#define STATIC_ERLANG_NIF 1 + + +#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 <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <ctype.h> +#include <sys/types.h> +#include <errno.h> +#include <time.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef HAVE_NET_IF_DL_H +#include <net/if_dl.h> +#endif + +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +#include <netpacket/packet.h> +#endif + +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#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 <winsock2.h> +#endif +#include <windows.h> +#include <Ws2tcpip.h> /* 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 <sdkddkver.h> +# ifdef NTDDI_VERSION +# undef NTDDI_VERSION +# endif +# define NTDDI_VERSION NTDDI_WINXP +#endif +#include <iphlpapi.h> + +#undef WANT_NONBLOCKING +#include "sys.h" + +#else /* !__WIN32__ */ + +#include <sys/time.h> +#ifdef NETDB_H_NEEDS_IN_H +#include <netinet/in.h> +#endif +#include <netdb.h> + +#include <sys/socket.h> +#include <netinet/in.h> + +#ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H +#include <rpc/types.h> +#endif + +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <arpa/inet.h> + +#include <sys/param.h> +#ifdef HAVE_ARPA_NAMESER_H +#include <arpa/nameser.h> +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#include <net/if.h> + +#ifdef HAVE_SCHED_H +#include <sched.h> +#endif + +#ifdef HAVE_SETNS_H +#include <setns.h> +#endif + +#define HAVE_UDP + +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif +#include "sys.h" + +#endif + +#include <erl_nif.h> + +#include "socket_dbg.h" +#include "socket_int.h" +#include "socket_util.h" + + +/* All platforms fail on malloc errors. */ +#define FATAL_MALLOC + + +#ifdef __WIN32__ +#define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__)) +#else +#define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__)) +#endif // __WIN32__ + + + +/* *** Misc macros and defines *** */ + +#ifdef __WIN32__ +#define get_errno() WSAGetLastError() +#else +#define get_errno() errno +#endif + + +#define HOSTNAME_LEN 256 +#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 * + * * + * =================================================================== */ + + +#ifdef HAVE_SOCKLEN_T +# define SOCKLEN_T socklen_t +#else +# define SOCKLEN_T size_t +#endif + +/* Debug stuff... */ +#define NET_NIF_DEBUG_DEFAULT FALSE + +#define NDBG( proto ) ESOCK_DBG_PRINTF( data.debug , proto ) + + +typedef struct { + BOOLEAN_T debug; +} NetData; + + + +/* =================================================================== * + * * + * Static data * + * * + * =================================================================== */ + + +static NetData data; + + + +/* ---------------------------------------------------------------------- + * F o r w a r d s + * ---------------------------------------------------------------------- + */ + +/* THIS IS JUST TEMPORARY */ +extern char* erl_errno_id(int error); + +/* All the nif "callback" functions for the net API has + * the exact same API: + * + * nif_<funcname>(ErlNifEnv* env, + * int argc, + * const ERL_NIF_TERM argv[]); + * + * So, to simplify, use some macro magic to define those. + * + * These are the functions making up the "official" API. + */ + +#define ENET_NIF_FUNCS \ + ENET_NIF_FUNC_DEF(info); \ + ENET_NIF_FUNC_DEF(command); \ + ENET_NIF_FUNC_DEF(gethostname); \ + ENET_NIF_FUNC_DEF(getnameinfo); \ + ENET_NIF_FUNC_DEF(getaddrinfo); \ + ENET_NIF_FUNC_DEF(if_name2index); \ + ENET_NIF_FUNC_DEF(if_index2name); \ + ENET_NIF_FUNC_DEF(if_names); + +#define ENET_NIF_FUNC_DEF(F) \ + static ERL_NIF_TERM nif_##F(ErlNifEnv* env, \ + int argc, \ + const ERL_NIF_TERM argv[]); +ENET_NIF_FUNCS +#undef ENET_NIF_FUNC_DEF + + +/* And here comes the functions that does the actual work (for the most part) */ +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 SocketAddress* saP, + 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, + 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_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 +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_infos(ErlNifEnv* env, + struct addrinfo* addrInfo); +static ERL_NIF_TERM encode_address_info(ErlNifEnv* env, + struct addrinfo* addrInfoP); +static unsigned int address_info_length(struct addrinfo* addrInfoP); + +static ERL_NIF_TERM encode_address_info_family(ErlNifEnv* env, + int family); +static ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env, + int socktype); +static ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, + int proto); + +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 BOOLEAN_T extract_debug(ErlNifEnv* env, + ERL_NIF_TERM map); +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 */ + + + +/* *** Local atoms *** */ + +#define LOCAL_ATOMS \ + LOCAL_ATOM_DECL(address_info); \ + LOCAL_ATOM_DECL(debug); \ + LOCAL_ATOM_DECL(host); \ + LOCAL_ATOM_DECL(idn); \ + LOCAL_ATOM_DECL(idna_allow_unassigned); \ + LOCAL_ATOM_DECL(idna_use_std3_ascii_rules); \ + LOCAL_ATOM_DECL(namereqd); \ + LOCAL_ATOM_DECL(name_info); \ + LOCAL_ATOM_DECL(nofqdn); \ + LOCAL_ATOM_DECL(numerichost); \ + LOCAL_ATOM_DECL(numericserv); \ + LOCAL_ATOM_DECL(service); + +#define LOCAL_ERROR_REASON_ATOMS \ + LOCAL_ATOM_DECL(eaddrfamily); \ + LOCAL_ATOM_DECL(ebadflags); \ + LOCAL_ATOM_DECL(efail); \ + LOCAL_ATOM_DECL(efamily); \ + LOCAL_ATOM_DECL(efault); \ + LOCAL_ATOM_DECL(emem); \ + LOCAL_ATOM_DECL(enametoolong); \ + LOCAL_ATOM_DECL(enodata); \ + LOCAL_ATOM_DECL(enoname); \ + LOCAL_ATOM_DECL(enxio); \ + LOCAL_ATOM_DECL(eoverflow); \ + LOCAL_ATOM_DECL(eservice); \ + LOCAL_ATOM_DECL(esocktype); \ + LOCAL_ATOM_DECL(esystem); + +#define LOCAL_ATOM_DECL(A) static ERL_NIF_TERM atom_##A +LOCAL_ATOMS +LOCAL_ERROR_REASON_ATOMS +#undef LOCAL_ATOM_DECL + + +/* *** net *** */ +static ErlNifResourceType* net; +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_info/0 + * nif_command/1 + * + * The "proper" net functions: + * ------------------------------ + * nif_gethostname/0 + * nif_getnameinfo/2 + * nif_getaddrinfo/3 + * nif_if_name2index/1 + * nif_if_index2name/1 + * nif_if_names/0 + * + */ + + +/* ---------------------------------------------------------------------- + * nif_info + * + * Description: + * This is currently just a placeholder... + */ +static +ERL_NIF_TERM nif_info(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + ERL_NIF_TERM info, tmp; + + 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( ("NET", "info -> done: %T\r\n", info) ); + + return info; +#endif +} + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + ERL_NIF_TERM ecmd, result; + + NDBG( ("NET", "command -> entry (%d)\r\n", argc) ); + + if (argc != 1) + return enif_make_badarg(env); + + ecmd = argv[0]; + + NDBG( ("NET", "command -> ecmd: %T\r\n", ecmd) ); + + result = ncommand(env, ecmd); + + NDBG( ("NET", "command -> result: %T\r\n", result) ); + + return result; +#endif +} + + + +/* + * 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) +{ + 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 esock_make_error(env, esock_atom_einval); + + if (tsz != 2) + return esock_make_error(env, esock_atom_einval); + + /* First element should be the atom 'debug' */ + if (COMPARE(t[0], atom_debug) != 0) + return esock_make_error(env, esock_atom_einval); + + return decode_bool(env, t[1], &data.debug); + + } else { + return esock_make_error(env, esock_atom_einval); + } + +} +#endif + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + ERL_NIF_TERM result; + + NDBG( ("NET", "nif_gethostname -> entry (%d)\r\n", argc) ); + + if (argc != 0) + return enif_make_badarg(env); + + result = ngethostname(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) +{ + ERL_NIF_TERM result; + char buf[NET_MAXHOSTNAMELEN + 1]; + int res; + + res = net_gethostname(buf, sizeof(buf)); + + NDBG( ("NET", "ngethostname -> gethostname res: %d\r\n", res) ); + + switch (res) { + case 0: + result = esock_make_ok2(env, MKS(env, buf)); + break; + + case EFAULT: + result = esock_make_error(env, atom_efault); + break; + + case EINVAL: + result = esock_make_error(env, esock_atom_einval); + break; + + case ENAMETOOLONG: + result = esock_make_error(env, atom_enametoolong); + break; + + default: + result = esock_make_error(env, MKI(env, res)); + break; + } + + return result; +} +#endif + + + + +/* ---------------------------------------------------------------------- + * 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(). + */ + +static +ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + ERL_NIF_TERM result; + ERL_NIF_TERM eSockAddr, eFlags; + int flags = 0; // Just in case... + SocketAddress sa; + SOCKLEN_T saLen = 0; // Just in case... + char* xres; + + NDBG( ("NET", "nif_getnameinfo -> entry (%d)\r\n", argc) ); + + if (argc != 2) + return enif_make_badarg(env); + eSockAddr = argv[0]; + eFlags = argv[1]; + + NDBG( ("NET", + "nif_getnameinfo -> " + "\r\n SockAddr: %T" + "\r\n Flags: %T" + "\r\n", eSockAddr, eFlags) ); + + if ((xres = esock_decode_sockaddr(env, eSockAddr, &sa, &saLen)) != NULL) { + NDBG( ("NET", "nif_getnameinfo -> failed decode sockaddr: %s\r\n", xres) ); + return esock_make_error_str(env, xres); + } + + NDBG( ("NET", "nif_getnameinfo -> (try) decode flags\r\n") ); + + if (!decode_nameinfo_flags(env, eFlags, &flags)) + return enif_make_badarg(env); + + result = ngetnameinfo(env, &sa, saLen, flags); + + NDBG( ("NET", + "nif_getnameinfo -> done when result: " + "\r\n %T\r\n", result) ); + + return result; +#endif +} + + + +/* Given the provided sock(et) address (and flags), retreive the host and + * service info. + */ +#if !defined(__WIN32__) +static +ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, + const SocketAddress* 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); + + NDBG( ("NET", "ngetnameinfo -> res: %d\r\n", res) ); + + switch (res) { + case 0: + { + ERL_NIF_TERM keys[] = {atom_host, atom_service}; + ERL_NIF_TERM vals[] = {MKS(env, host), MKS(env, serv)}; + ERL_NIF_TERM info; + 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, &info)) + return enif_make_badarg(env); + + result = esock_make_ok2(env, info); + } + 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); + break; + } + + return result; +} +#endif + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + ERL_NIF_TERM result, eHostName, eServName; //, eHints; + char* hostName; + char* servName; + // struct addrinfo* hints; + + NDBG( ("NET", "nif_getaddrinfo -> entry (%d)\r\n", argc) ); + + if (argc != 3) { + return enif_make_badarg(env); + } + eHostName = argv[0]; + eServName = argv[1]; + // eHints = argv[2]; + + NDBG( ("NET", + "nif_getaddrinfo -> " + "\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)) + 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); + */ + + NDBG( ("NET", + "nif_getaddrinfo -> done when result: " + "\r\n %T\r\n", result) ); + + return result; +#endif +} + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, + char* host, + char* serv) +{ + ERL_NIF_TERM result; + struct addrinfo* addrInfoP; + int res; + + NDBG( ("NET", "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( ("NET", "ngetaddrinfo -> res: %d\r\n", res) ); + + switch (res) { + case 0: + { + ERL_NIF_TERM addrInfo = encode_address_infos(env, addrInfoP); + freeaddrinfo(addrInfoP); + result = esock_make_ok2(env, addrInfo); + } + 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); + break; + } + + return result; +} +#endif + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + ERL_NIF_TERM eifn, result; + char ifn[IF_NAMESIZE+1]; + + NDBG( ("NET", "nif_if_name2index -> entry (%d)\r\n", argc) ); + + if (argc != 1) { + return enif_make_badarg(env); + } + eifn = argv[0]; + + NDBG( ("NET", + "nif_if_name2index -> " + "\r\n Ifn: %T" + "\r\n", argv[0]) ); + + if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) + return esock_make_error(env, esock_atom_einval); + + result = nif_name2index(env, ifn); + + 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) +{ + unsigned int idx; + + NDBG( ("NET", "nif_name2index -> entry with ifn: %s\r\n", ifn) ); + + idx = if_nametoindex(ifn); + + NDBG( ("NET", "nif_name2index -> idx: %d\r\n", idx) ); + + 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 + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + ERL_NIF_TERM result; + unsigned int idx; + + 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( ("NET", "nif_index2name -> " + "\r\n Idx: %T" + "\r\n", argv[0]) ); + + result = nif_index2name(env, idx); + + 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) +{ + 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 = esock_make_ok2(env, MKS(env, ifn)); + } else { + result = esock_make_error(env, atom_enxio); + } + + FREE(ifn); + + return result; +} +#endif + + + +/* ---------------------------------------------------------------------- + * 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 defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + ERL_NIF_TERM result; + + NDBG( ("NET", "nif_if_names -> entry (%d)\r\n", argc) ); + + if (argc != 0) { + return enif_make_badarg(env); + } + + result = nif_names(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) +{ + ERL_NIF_TERM result; + struct if_nameindex* ifs = if_nameindex(); + + NDBG( ("NET", "nif_names -> ifs: 0x%lX\r\n", ifs) ); + + if (ifs == NULL) { + result = esock_make_error_errno(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); + + NDBG( ("NET", "nif_names -> len: %d\r\n", len) ); + + 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 = esock_make_ok2(env, MKLA(env, array, len)); + FREE(array); + } else { + result = esock_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; + BOOLEAN_T done = FALSE; + + while (!done) { + + 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) ); + + if ((p[len].if_index == 0) && (p[len].if_name == NULL)) + done = TRUE; + else + len++; + } + + return len; +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * U t i l i t y F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* 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. + */ +#if !defined(__WIN32__) +static +BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, + const ERL_NIF_TERM eflags, + int* flags) +{ + BOOLEAN_T result; + + if (IS_ATOM(env, eflags)) { + NDBG( ("NET", "decode_nameinfo_flags -> is atom (%T)\r\n", eflags) ); + if (COMPARE(eflags, esock_atom_undefined) == 0) { + *flags = 0; + result = TRUE; + } else { + result = FALSE; + } + } else if (IS_LIST(env, eflags)) { + NDBG( ("NET", "decode_nameinfo_flags -> is list\r\n") ); + result = decode_nameinfo_flags_list(env, eflags, flags); + } else { + result = FALSE; + } + + NDBG( ("NET", "decode_nameinfo_flags -> result: %s\r\n", B2S(result)) ); + + 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, esock_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, esock_atom_undefined) == 0) { + *stringP = NULL; + result = TRUE; + } else { + *stringP = NULL; + result = FALSE; + } + + } else { + + result = esock_decode_string(env, eString, stringP); + + } + + return result; + +} + + + +static +ERL_NIF_TERM decode_bool(ErlNifEnv* env, + ERL_NIF_TERM eBool, + BOOLEAN_T* bool) +{ + if (COMPARE(eBool, esock_atom_true) == 0) { + *bool = TRUE; + return esock_atom_ok; + } else if (COMPARE(eBool, esock_atom_false) == 0) { + *bool = FALSE; + return esock_atom_ok; + } else { + return esock_make_error(env, esock_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. + */ +static +ERL_NIF_TERM encode_address_infos(ErlNifEnv* env, + struct addrinfo* addrInfo) +{ + ERL_NIF_TERM result; + unsigned int len = address_info_length(addrInfo); + + NDBG( ("NET", "encode_address_infos -> 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] = encode_address_info(env, p); + p = p->ai_next; + i++; + } + + result = MKLA(env, array, len); + FREE(array); + } else { + result = MKEL(env); + } + + NDBG( ("NET", "encode_address_infos -> result: " + "\r\n %T\r\n", result) ); + + 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 = 1; + struct addrinfo* tmp; + BOOLEAN_T done = FALSE; + + tmp = addrInfoP; + + while (!done) { + if (tmp->ai_next != NULL) { + len++; + tmp = tmp->ai_next; + } else { + done = TRUE; + } + } + + 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 encode_address_info(ErlNifEnv* env, + struct addrinfo* addrInfoP) +{ + 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); + proto = encode_address_info_proto(env, addrInfoP->ai_protocol); + esock_encode_sockaddr(env, + (SocketAddress*) addrInfoP->ai_addr, + addrInfoP->ai_addrlen, + &addr); + + if (make_address_info(env, fam, type, proto, addr, &addrInfo) == NULL) + return addrInfo; + else + return esock_atom_undefined; // We should to better... + +} + + +/* 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. + */ +static +ERL_NIF_TERM encode_address_info_family(ErlNifEnv* env, + int family) +{ + ERL_NIF_TERM efam; + + if (NULL != esock_encode_domain(env, family, &efam)) + efam = MKI(env, family); + + 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 encode_address_info_type(ErlNifEnv* env, + int socktype) +{ + ERL_NIF_TERM etype; + + if (NULL != esock_encode_type(env, socktype, &etype)) + etype = MKI(env, socktype); + + 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 encode_address_info_proto(ErlNifEnv* env, + int proto) +{ + ERL_NIF_TERM eproto; + + if (NULL != esock_encode_protocol(env, proto, &eproto)) + eproto = MKI(env, proto); + + return eproto; +} + + + +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) +{ + 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; + } +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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_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}, + + /* 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} +}; + + +#if !defined(__WIN32__) +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); +} +#endif + + +/* ======================================================================= + * 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) +{ +#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 + +#define LOCAL_ATOM_DECL(A) atom_##A = MKA(env, #A) +LOCAL_ATOMS +LOCAL_ERROR_REASON_ATOMS +#undef LOCAL_ATOM_DECL + + // For storing "global" things... + // 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", + &netInit, + ERL_NIF_RT_CREATE, + NULL); + +#if !defined(__WIN32__) + NDBG( ("NET", "on_load -> done\r\n") ); +#endif + + return !net; +} + +ERL_NIF_INIT(net, net_funcs, on_load, NULL, NULL, NULL) diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c index a78ee62677..3df04e42e2 100644 --- a/erts/emulator/nifs/common/prim_file_nif.c +++ b/erts/emulator/nifs/common/prim_file_nif.c @@ -476,7 +476,8 @@ static ERL_NIF_TERM open_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[] ERL_NIF_TERM result; efile_path_t path; - if(argc != 2 || !enif_is_list(env, argv[1])) { + ASSERT(argc == 2); + if(!enif_is_list(env, argv[1])) { return enif_make_badarg(env); } @@ -551,7 +552,8 @@ static ERL_NIF_TERM read_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, con SysIOVec read_vec[1]; ErlNifBinary result; - if(argc != 1 || !enif_is_number(env, argv[0])) { + ASSERT(argc == 1); + if(!enif_is_number(env, argv[0])) { return enif_make_badarg(env); } @@ -589,7 +591,8 @@ static ERL_NIF_TERM write_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, co Sint64 bytes_written; ERL_NIF_TERM tail; - if(argc != 1 || !enif_inspect_iovec(env, 64, argv[0], &tail, &input)) { + ASSERT(argc == 1); + if(!enif_inspect_iovec(env, 64, argv[0], &tail, &input)) { return enif_make_badarg(env); } @@ -612,8 +615,8 @@ static ERL_NIF_TERM pread_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, co SysIOVec read_vec[1]; ErlNifBinary result; - if(argc != 2 || !enif_is_number(env, argv[0]) - || !enif_is_number(env, argv[1])) { + ASSERT(argc == 2); + if(!enif_is_number(env, argv[0]) || !enif_is_number(env, argv[1])) { return enif_make_badarg(env); } @@ -652,8 +655,9 @@ static ERL_NIF_TERM pwrite_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, c Sint64 bytes_written, offset; ERL_NIF_TERM tail; - if(argc != 2 || !enif_is_number(env, argv[0]) - || !enif_inspect_iovec(env, 64, argv[1], &tail, &input)) { + ASSERT(argc == 2); + if(!enif_is_number(env, argv[0]) + || !enif_inspect_iovec(env, 64, argv[1], &tail, &input)) { return enif_make_badarg(env); } @@ -680,7 +684,8 @@ static ERL_NIF_TERM seek_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, con Sint64 new_position, offset; enum efile_seek_t seek; - if(argc != 2 || !enif_get_int64(env, argv[1], &offset)) { + ASSERT(argc == 2); + if(!enif_get_int64(env, argv[1], &offset)) { return enif_make_badarg(env); } @@ -704,7 +709,8 @@ static ERL_NIF_TERM seek_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, con static ERL_NIF_TERM sync_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { int data_only; - if(argc != 1 || !enif_get_int(env, argv[0], &data_only)) { + ASSERT(argc == 1); + if(!enif_get_int(env, argv[0], &data_only)) { return enif_make_badarg(env); } @@ -716,9 +722,7 @@ static ERL_NIF_TERM sync_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, con } static ERL_NIF_TERM truncate_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - if(argc != 0) { - return enif_make_badarg(env); - } + ASSERT(argc == 0); if(!efile_truncate(d)) { return posix_error_to_tuple(env, d->posix_errno); @@ -730,8 +734,8 @@ static ERL_NIF_TERM truncate_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, static ERL_NIF_TERM allocate_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { Sint64 offset, length; - if(argc != 2 || !enif_is_number(env, argv[0]) - || !enif_is_number(env, argv[1])) { + ASSERT(argc == 2); + if(!enif_is_number(env, argv[0]) || !enif_is_number(env, argv[1])) { return enif_make_badarg(env); } @@ -752,8 +756,8 @@ static ERL_NIF_TERM advise_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, c enum efile_advise_t advise; Sint64 offset, length; - if(argc != 3 || !enif_is_number(env, argv[0]) - || !enif_is_number(env, argv[1])) { + ASSERT(argc == 3); + if(!enif_is_number(env, argv[0]) || !enif_is_number(env, argv[1])) { return enif_make_badarg(env); } @@ -816,8 +820,8 @@ static ERL_NIF_TERM ipread_s32bu_p32bu_nif_impl(efile_data_t *d, ErlNifEnv *env, ErlNifBinary payload; - if(argc != 2 || !enif_is_number(env, argv[0]) - || !enif_is_number(env, argv[1])) { + ASSERT(argc == 2); + if(!enif_is_number(env, argv[0]) || !enif_is_number(env, argv[1])) { return enif_make_badarg(env); } @@ -884,9 +888,7 @@ static ERL_NIF_TERM ipread_s32bu_p32bu_nif_impl(efile_data_t *d, ErlNifEnv *env, } static ERL_NIF_TERM get_handle_nif_impl(efile_data_t *d, ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { - if(argc != 0) { - return enif_make_badarg(env); - } + ASSERT(argc == 0); return efile_get_handle(env, d); } @@ -898,7 +900,8 @@ static ERL_NIF_TERM read_info_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a efile_path_t path; int follow_links; - if(argc != 2 || !enif_get_int(env, argv[1], &follow_links)) { + ASSERT(argc == 2); + if(!enif_get_int(env, argv[1], &follow_links)) { return enif_make_badarg(env); } @@ -933,7 +936,8 @@ static ERL_NIF_TERM set_permissions_nif(ErlNifEnv *env, int argc, const ERL_NIF_ efile_path_t path; unsigned int permissions; - if(argc != 2 || !enif_get_uint(env, argv[1], &permissions)) { + ASSERT(argc == 2); + if(!enif_get_uint(env, argv[1], &permissions)) { return enif_make_badarg(env); } @@ -952,8 +956,8 @@ static ERL_NIF_TERM set_owner_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a efile_path_t path; int uid, gid; - if(argc != 3 || !enif_get_int(env, argv[1], &uid) - || !enif_get_int(env, argv[2], &gid)) { + ASSERT(argc == 3); + if(!enif_get_int(env, argv[1], &uid) || !enif_get_int(env, argv[2], &gid)) { return enif_make_badarg(env); } @@ -972,9 +976,10 @@ static ERL_NIF_TERM set_time_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar Sint64 accessed, modified, created; efile_path_t path; - if(argc != 4 || !enif_get_int64(env, argv[1], &accessed) - || !enif_get_int64(env, argv[2], &modified) - || !enif_get_int64(env, argv[3], &created)) { + ASSERT(argc == 4); + if(!enif_get_int64(env, argv[1], &accessed) + || !enif_get_int64(env, argv[2], &modified) + || !enif_get_int64(env, argv[3], &created)) { return enif_make_badarg(env); } @@ -993,9 +998,7 @@ static ERL_NIF_TERM read_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a efile_path_t path; ERL_NIF_TERM result; - if(argc != 1) { - return enif_make_badarg(env); - } + ASSERT(argc == 1); if((posix_errno = efile_marshal_path(env, argv[0], &path))) { return posix_error_to_tuple(env, posix_errno); @@ -1012,9 +1015,7 @@ static ERL_NIF_TERM list_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar efile_path_t path; ERL_NIF_TERM result; - if(argc != 1) { - return enif_make_badarg(env); - } + ASSERT(argc == 1); if((posix_errno = efile_marshal_path(env, argv[0], &path))) { return posix_error_to_tuple(env, posix_errno); @@ -1030,9 +1031,7 @@ static ERL_NIF_TERM rename_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv efile_path_t existing_path, new_path; - if(argc != 2) { - return enif_make_badarg(env); - } + ASSERT(argc == 2); if((posix_errno = efile_marshal_path(env, argv[0], &existing_path))) { return posix_error_to_tuple(env, posix_errno); @@ -1050,9 +1049,7 @@ static ERL_NIF_TERM make_hard_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_T efile_path_t existing_path, new_path; - if(argc != 2) { - return enif_make_badarg(env); - } + ASSERT(argc == 2); if((posix_errno = efile_marshal_path(env, argv[0], &existing_path))) { return posix_error_to_tuple(env, posix_errno); @@ -1070,9 +1067,7 @@ static ERL_NIF_TERM make_soft_link_nif(ErlNifEnv *env, int argc, const ERL_NIF_T efile_path_t existing_path, new_path; - if(argc != 2) { - return enif_make_badarg(env); - } + ASSERT(argc == 2); if((posix_errno = efile_marshal_path(env, argv[0], &existing_path))) { return posix_error_to_tuple(env, posix_errno); @@ -1090,9 +1085,7 @@ static ERL_NIF_TERM make_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar efile_path_t path; - if(argc != 1) { - return enif_make_badarg(env); - } + ASSERT(argc == 1); if((posix_errno = efile_marshal_path(env, argv[0], &path))) { return posix_error_to_tuple(env, posix_errno); @@ -1108,9 +1101,7 @@ static ERL_NIF_TERM del_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM ar efile_path_t path; - if(argc != 1) { - return enif_make_badarg(env); - } + ASSERT(argc == 1); if((posix_errno = efile_marshal_path(env, argv[0], &path))) { return posix_error_to_tuple(env, posix_errno); @@ -1126,9 +1117,7 @@ static ERL_NIF_TERM del_dir_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg efile_path_t path; - if(argc != 1) { - return enif_make_badarg(env); - } + ASSERT(argc == 1); if((posix_errno = efile_marshal_path(env, argv[0], &path))) { return posix_error_to_tuple(env, posix_errno); @@ -1145,7 +1134,8 @@ static ERL_NIF_TERM get_device_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_T ERL_NIF_TERM result; int device_index; - if(argc != 1 || !enif_get_int(env, argv[0], &device_index)) { + ASSERT(argc == 1); + if(!enif_get_int(env, argv[0], &device_index)) { return enif_make_badarg(env); } @@ -1160,9 +1150,7 @@ static ERL_NIF_TERM get_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg posix_errno_t posix_errno; ERL_NIF_TERM result; - if(argc != 0) { - return enif_make_badarg(env); - } + ASSERT(argc == 0); if((posix_errno = efile_get_cwd(env, &result))) { return posix_error_to_tuple(env, posix_errno); @@ -1176,9 +1164,7 @@ static ERL_NIF_TERM set_cwd_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg efile_path_t path; - if(argc != 1) { - return enif_make_badarg(env); - } + ASSERT(argc == 1); if((posix_errno = efile_marshal_path(env, argv[0], &path))) { return posix_error_to_tuple(env, posix_errno); @@ -1254,9 +1240,7 @@ static ERL_NIF_TERM read_file_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a ErlNifBinary result; - if(argc != 1) { - return enif_make_badarg(env); - } + ASSERT(argc == 1); if((posix_errno = efile_marshal_path(env, argv[0], &path))) { return posix_error_to_tuple(env, posix_errno); @@ -1284,9 +1268,7 @@ static ERL_NIF_TERM altname_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM arg efile_path_t path; ERL_NIF_TERM result; - if(argc != 1) { - return enif_make_badarg(env); - } + ASSERT(argc == 1); if((posix_errno = efile_marshal_path(env, argv[0], &path))) { return posix_error_to_tuple(env, posix_errno); diff --git a/erts/emulator/nifs/common/socket_dbg.c b/erts/emulator/nifs/common/socket_dbg.c new file mode 100644 index 0000000000..96f75a328f --- /dev/null +++ b/erts/emulator/nifs/common/socket_dbg.c @@ -0,0 +1,148 @@ +/* + * %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 <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <time.h> + +#include <erl_nif.h> +#include "socket_dbg.h" + +#define TSELF() enif_thread_self() +#define TNAME(__T__) enif_thread_name( __T__ ) +#define TSNAME() TNAME(TSELF()) + +static FILE* dbgout = NULL; + +#if defined(CLOCK_REALTIME) +static int realtime(struct timespec* tsP); +static int timespec2str(char *buf, unsigned int len, struct timespec *ts); +#endif + + +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. + */ +extern +void esock_dbg_printf( const char* prefix, const char* format, ... ) +{ + va_list args; + char f[512 + sizeof(format)]; // This has to suffice... +#if defined(CLOCK_REALTIME) + char stamp[30]; + struct timespec ts; +#endif + 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 defined(CLOCK_REALTIME) + if (!realtime(&ts) && + (timespec2str(stamp, sizeof(stamp), &ts) == 0)) { + res = enif_snprintf(f, sizeof(f), "%s [%s] [%s] %s", + prefix, stamp, TSNAME(), format); + } else { + res = enif_snprintf(f, sizeof(f), "%s [%s] %s", + prefix, TSNAME(), format); + } +#else + res = enif_snprintf(f, sizeof(f), "%s [%s] %s", + prefix, TSNAME(), format); +#endif + + if (res > 0) { + va_start (args, format); + enif_vfprintf (dbgout, f, args); + va_end (args); + fflush(stdout); + } + + return; +} + + +#if defined(CLOCK_REALTIME) +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; +} +#endif diff --git a/erts/emulator/nifs/common/socket_dbg.h b/erts/emulator/nifs/common/socket_dbg.h new file mode 100644 index 0000000000..47739b46da --- /dev/null +++ b/erts/emulator/nifs/common/socket_dbg.h @@ -0,0 +1,55 @@ +/* + * %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__ + +/* Used when calling the init function */ +#define ESOCK_DBGOUT_DEFAULT "stdout" +#define ESOCK_DBGOUT_UNIQUE "unique" + + +/* 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; \ + fflush(stdout); \ + } + + +extern void esock_dbg_init(char* filename); +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..043303a60d --- /dev/null +++ b/erts/emulator/nifs/common/socket_int.h @@ -0,0 +1,392 @@ +/* + * %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 <winsock2.h> +#endif +#include <windows.h> +#include <Ws2tcpip.h> /* 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 <sdkddkver.h> +# ifdef NTDDI_VERSION +# undef NTDDI_VERSION +# endif +# define NTDDI_VERSION NTDDI_WINXP +#endif +#include <iphlpapi.h> + +#else /* !__WIN32__ */ + +#include <sys/socket.h> +#include <netinet/in.h> +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif + +#endif + +#include <erl_nif.h> + +/* The general purpose sockaddr */ +typedef union { + /* General sockaddr */ + struct sockaddr sa; + + /* 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; + + +/* *** Boolean *type* stuff... *** */ +typedef unsigned int BOOLEAN_T; +#define TRUE 1 +#define FALSE 0 + +#define BOOL2ATOM(__B__) ((__B__) ? esock_atom_true : esock_atom_false) + +#define B2S(__B__) ((__B__) ? "true" : "false") + +/* Misc error strings */ +#define ESOCK_STR_EAFNOSUPPORT "eafnosupport" +#define ESOCK_STR_EAGAIN "eagain" +#define ESOCK_STR_EINVAL "einval" + + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * "Global" atoms + */ + +#define GLOBAL_ATOM_DEFS \ + GLOBAL_ATOM_DEF(abort); \ + GLOBAL_ATOM_DEF(accept); \ + GLOBAL_ATOM_DEF(acceptconn); \ + GLOBAL_ATOM_DEF(acceptfilter); \ + GLOBAL_ATOM_DEF(adaption_layer); \ + GLOBAL_ATOM_DEF(addr); \ + GLOBAL_ATOM_DEF(addrform); \ + GLOBAL_ATOM_DEF(add_membership); \ + GLOBAL_ATOM_DEF(add_source_membership); \ + GLOBAL_ATOM_DEF(any); \ + GLOBAL_ATOM_DEF(associnfo); \ + GLOBAL_ATOM_DEF(authhdr); \ + GLOBAL_ATOM_DEF(auth_active_key); \ + GLOBAL_ATOM_DEF(auth_asconf); \ + GLOBAL_ATOM_DEF(auth_chunk); \ + GLOBAL_ATOM_DEF(auth_delete_key); \ + GLOBAL_ATOM_DEF(auth_key); \ + GLOBAL_ATOM_DEF(auth_level); \ + GLOBAL_ATOM_DEF(autoclose); \ + GLOBAL_ATOM_DEF(bindtodevice); \ + GLOBAL_ATOM_DEF(block_source); \ + GLOBAL_ATOM_DEF(broadcast); \ + GLOBAL_ATOM_DEF(busy_poll); \ + GLOBAL_ATOM_DEF(checksum); \ + GLOBAL_ATOM_DEF(close); \ + GLOBAL_ATOM_DEF(connect); \ + GLOBAL_ATOM_DEF(congestion); \ + GLOBAL_ATOM_DEF(context); \ + GLOBAL_ATOM_DEF(cork); \ + GLOBAL_ATOM_DEF(credentials); \ + GLOBAL_ATOM_DEF(ctrl); \ + GLOBAL_ATOM_DEF(ctrunc); \ + GLOBAL_ATOM_DEF(data); \ + GLOBAL_ATOM_DEF(debug); \ + GLOBAL_ATOM_DEF(default_send_params); \ + GLOBAL_ATOM_DEF(delayed_ack_time); \ + GLOBAL_ATOM_DEF(dgram); \ + GLOBAL_ATOM_DEF(disable_fragments); \ + GLOBAL_ATOM_DEF(domain); \ + GLOBAL_ATOM_DEF(dontfrag); \ + GLOBAL_ATOM_DEF(dontroute); \ + GLOBAL_ATOM_DEF(drop_membership); \ + GLOBAL_ATOM_DEF(drop_source_membership); \ + GLOBAL_ATOM_DEF(dstopts); \ + GLOBAL_ATOM_DEF(eor); \ + GLOBAL_ATOM_DEF(error); \ + GLOBAL_ATOM_DEF(errqueue); \ + GLOBAL_ATOM_DEF(esp_network_level); \ + GLOBAL_ATOM_DEF(esp_trans_level); \ + GLOBAL_ATOM_DEF(events); \ + GLOBAL_ATOM_DEF(explicit_eor); \ + GLOBAL_ATOM_DEF(faith); \ + GLOBAL_ATOM_DEF(false); \ + GLOBAL_ATOM_DEF(family); \ + GLOBAL_ATOM_DEF(flags); \ + GLOBAL_ATOM_DEF(flowinfo); \ + GLOBAL_ATOM_DEF(fragment_interleave); \ + GLOBAL_ATOM_DEF(freebind); \ + GLOBAL_ATOM_DEF(get_peer_addr_info); \ + GLOBAL_ATOM_DEF(hdrincl); \ + GLOBAL_ATOM_DEF(hmac_ident); \ + GLOBAL_ATOM_DEF(hoplimit); \ + GLOBAL_ATOM_DEF(hopopts); \ + GLOBAL_ATOM_DEF(ifindex); \ + GLOBAL_ATOM_DEF(inet); \ + GLOBAL_ATOM_DEF(inet6); \ + GLOBAL_ATOM_DEF(info); \ + GLOBAL_ATOM_DEF(initmsg); \ + GLOBAL_ATOM_DEF(iov); \ + GLOBAL_ATOM_DEF(ip); \ + GLOBAL_ATOM_DEF(ipcomp_level); \ + GLOBAL_ATOM_DEF(ipv6); \ + GLOBAL_ATOM_DEF(i_want_mapped_v4_addr); \ + GLOBAL_ATOM_DEF(join_group); \ + GLOBAL_ATOM_DEF(keepalive); \ + GLOBAL_ATOM_DEF(keepcnt); \ + GLOBAL_ATOM_DEF(keepidle); \ + GLOBAL_ATOM_DEF(keepintvl); \ + GLOBAL_ATOM_DEF(leave_group); \ + GLOBAL_ATOM_DEF(level); \ + GLOBAL_ATOM_DEF(linger); \ + GLOBAL_ATOM_DEF(local); \ + GLOBAL_ATOM_DEF(local_auth_chunks); \ + GLOBAL_ATOM_DEF(loopback); \ + GLOBAL_ATOM_DEF(lowdelay); \ + GLOBAL_ATOM_DEF(mark); \ + GLOBAL_ATOM_DEF(maxburst); \ + GLOBAL_ATOM_DEF(maxseg); \ + GLOBAL_ATOM_DEF(md5sig); \ + GLOBAL_ATOM_DEF(mincost); \ + GLOBAL_ATOM_DEF(minttl); \ + GLOBAL_ATOM_DEF(msfilter); \ + GLOBAL_ATOM_DEF(mtu); \ + GLOBAL_ATOM_DEF(mtu_discover); \ + GLOBAL_ATOM_DEF(multicast_all); \ + GLOBAL_ATOM_DEF(multicast_hops); \ + GLOBAL_ATOM_DEF(multicast_if); \ + GLOBAL_ATOM_DEF(multicast_loop); \ + GLOBAL_ATOM_DEF(multicast_ttl); \ + GLOBAL_ATOM_DEF(nodelay); \ + GLOBAL_ATOM_DEF(nodefrag); \ + GLOBAL_ATOM_DEF(noopt); \ + GLOBAL_ATOM_DEF(nopush); \ + GLOBAL_ATOM_DEF(not_found); \ + GLOBAL_ATOM_DEF(not_owner); \ + GLOBAL_ATOM_DEF(ok); \ + GLOBAL_ATOM_DEF(oob); \ + GLOBAL_ATOM_DEF(oobinline); \ + GLOBAL_ATOM_DEF(options); \ + GLOBAL_ATOM_DEF(origdstaddr); \ + GLOBAL_ATOM_DEF(partial_delivery_point); \ + GLOBAL_ATOM_DEF(passcred); \ + GLOBAL_ATOM_DEF(path); \ + GLOBAL_ATOM_DEF(peekcred); \ + GLOBAL_ATOM_DEF(peek_off); \ + GLOBAL_ATOM_DEF(peer_addr_params); \ + GLOBAL_ATOM_DEF(peer_auth_chunks); \ + GLOBAL_ATOM_DEF(pktinfo); \ + GLOBAL_ATOM_DEF(pktoptions); \ + GLOBAL_ATOM_DEF(port); \ + GLOBAL_ATOM_DEF(portrange); \ + GLOBAL_ATOM_DEF(primary_addr); \ + GLOBAL_ATOM_DEF(priority); \ + GLOBAL_ATOM_DEF(protocol); \ + GLOBAL_ATOM_DEF(raw); \ + GLOBAL_ATOM_DEF(rcvbuf); \ + GLOBAL_ATOM_DEF(rcvbufforce); \ + GLOBAL_ATOM_DEF(rcvlowat); \ + GLOBAL_ATOM_DEF(rcvtimeo); \ + GLOBAL_ATOM_DEF(rdm); \ + GLOBAL_ATOM_DEF(recv); \ + GLOBAL_ATOM_DEF(recvdstaddr); \ + GLOBAL_ATOM_DEF(recverr); \ + GLOBAL_ATOM_DEF(recvfrom); \ + GLOBAL_ATOM_DEF(recvif); \ + GLOBAL_ATOM_DEF(recvmsg); \ + GLOBAL_ATOM_DEF(recvopts); \ + GLOBAL_ATOM_DEF(recvorigdstaddr); \ + GLOBAL_ATOM_DEF(recvpktinfo); \ + GLOBAL_ATOM_DEF(recvtclass); \ + GLOBAL_ATOM_DEF(recvtos); \ + GLOBAL_ATOM_DEF(recvttl); \ + GLOBAL_ATOM_DEF(reliability); \ + GLOBAL_ATOM_DEF(reset_streams); \ + GLOBAL_ATOM_DEF(retopts); \ + GLOBAL_ATOM_DEF(reuseaddr); \ + GLOBAL_ATOM_DEF(reuseport); \ + GLOBAL_ATOM_DEF(rights); \ + GLOBAL_ATOM_DEF(router_alert); \ + GLOBAL_ATOM_DEF(rthdr); \ + GLOBAL_ATOM_DEF(rtoinfo); \ + GLOBAL_ATOM_DEF(rxq_ovfl); \ + GLOBAL_ATOM_DEF(scope_id); \ + GLOBAL_ATOM_DEF(sctp); \ + GLOBAL_ATOM_DEF(sec); \ + GLOBAL_ATOM_DEF(select_failed); \ + GLOBAL_ATOM_DEF(select_sent); \ + GLOBAL_ATOM_DEF(send); \ + GLOBAL_ATOM_DEF(sendmsg); \ + GLOBAL_ATOM_DEF(sendsrcaddr); \ + GLOBAL_ATOM_DEF(sendto); \ + GLOBAL_ATOM_DEF(seqpacket); \ + GLOBAL_ATOM_DEF(setfib); \ + GLOBAL_ATOM_DEF(set_peer_primary_addr); \ + GLOBAL_ATOM_DEF(sndbuf); \ + GLOBAL_ATOM_DEF(sndbufforce); \ + GLOBAL_ATOM_DEF(sndlowat); \ + GLOBAL_ATOM_DEF(sndtimeo); \ + GLOBAL_ATOM_DEF(socket); \ + GLOBAL_ATOM_DEF(socket_tag); \ + GLOBAL_ATOM_DEF(spec_dst); \ + GLOBAL_ATOM_DEF(status); \ + GLOBAL_ATOM_DEF(stream); \ + GLOBAL_ATOM_DEF(syncnt); \ + GLOBAL_ATOM_DEF(tclass); \ + GLOBAL_ATOM_DEF(tcp); \ + GLOBAL_ATOM_DEF(throughput); \ + GLOBAL_ATOM_DEF(timestamp); \ + GLOBAL_ATOM_DEF(tos); \ + GLOBAL_ATOM_DEF(transparent); \ + GLOBAL_ATOM_DEF(true); \ + GLOBAL_ATOM_DEF(trunc); \ + GLOBAL_ATOM_DEF(ttl); \ + GLOBAL_ATOM_DEF(type); \ + GLOBAL_ATOM_DEF(udp); \ + GLOBAL_ATOM_DEF(unblock_source); \ + GLOBAL_ATOM_DEF(undefined); \ + GLOBAL_ATOM_DEF(unicast_hops); \ + GLOBAL_ATOM_DEF(unknown); \ + GLOBAL_ATOM_DEF(usec); \ + GLOBAL_ATOM_DEF(user_timeout); \ + GLOBAL_ATOM_DEF(use_ext_recvinfo); \ + GLOBAL_ATOM_DEF(use_min_mtu); \ + GLOBAL_ATOM_DEF(v6only); + + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * Error reason atoms + */ + +#define GLOBAL_ERROR_REASON_ATOM_DEFS \ + GLOBAL_ATOM_DEF(eafnosupport); \ + GLOBAL_ATOM_DEF(eagain); \ + GLOBAL_ATOM_DEF(einval); + + +#define GLOBAL_ATOM_DEF(A) extern ERL_NIF_TERM esock_atom_##A +GLOBAL_ATOM_DEFS +GLOBAL_ERROR_REASON_ATOM_DEFS +#undef GLOBAL_ATOM_DEF + + +/* ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * 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)) +#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)) +#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) +#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 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)) +#define MLOCK(M) enif_mutex_lock((M)) +#define MUNLOCK(M) enif_mutex_unlock((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 MON2T(E, M) enif_make_monitor_term((E), (M)) + +#define COMPARE(A, B) enif_compare((A), (B)) +#define COMPARE_PIDS(P1, P2) enif_compare_pids((P1), (P2)) + +#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_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, 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)) +#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 new file mode 100644 index 0000000000..870ab63bdf --- /dev/null +++ b/erts/emulator/nifs/common/socket_nif.c @@ -0,0 +1,18287 @@ +/* + * %CopyrightBegin% + * + * 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. + * 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 + * + * All of the nif-functions which are part of the API has two parts. + * The first function is called 'nif_<something>', e.g. nif_open. + * This does the initial validation and argument processing and then + * calls the function that does the actual work. This is called + * 'n<something>'. + * ---------------------------------------------------------------------- + * + * + * This is just a code snippet in case there is need of extra debugging + * + * esock_dbg_printf("DEMONP", "[%d] %s: %T\r\n", + * descP->sock, slogan, + * MON2T(env, &monP->mon)); + * + */ + +#define STATIC_ERLANG_NIF 1 + + +#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 <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <ctype.h> +#include <sys/types.h> +#include <errno.h> +#include <time.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef HAVE_NET_IF_DL_H +#include <net/if_dl.h> +#endif + +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +#include <netpacket/packet.h> +#endif + +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#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 <winsock2.h> +#endif +#include <windows.h> +#include <Ws2tcpip.h> /* 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 <sdkddkver.h> +# ifdef NTDDI_VERSION +# undef NTDDI_VERSION +# endif +# define NTDDI_VERSION NTDDI_WINXP +#endif +#include <iphlpapi.h> + +#undef WANT_NONBLOCKING +#include "sys.h" + + + + +/* AND HERE WE MAY HAVE A BUNCH OF DEFINES....SEE INET DRIVER.... */ + + + + +#else /* ifdef __WIN32__ */ + +#include <sys/time.h> +#ifdef NETDB_H_NEEDS_IN_H +#include <netinet/in.h> +#endif +#include <netdb.h> + +#include <sys/socket.h> +#include <netinet/in.h> + +#ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H +#include <rpc/types.h> +#endif + +#include <netinet/ip.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <arpa/inet.h> + +#include <sys/param.h> +#ifdef HAVE_ARPA_NAMESER_H +#include <arpa/nameser.h> +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#include <net/if.h> + +#ifdef HAVE_SCHED_H +#include <sched.h> +#endif + +#ifdef HAVE_SETNS_H +#include <setns.h> +#endif + +#define HAVE_UDP + +/* SCTP support -- currently for UNIX platforms only: */ +#undef HAVE_SCTP +#if defined(HAVE_SCTP_H) + +#include <netinet/sctp.h> + +/* 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) *esock_sctp_bindx = NULL; +#else +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) *esock_sctp_peeloff = NULL; +#else +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) *esock_sctp_getladdrs = NULL; +#else +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) *esock_sctp_freeladdrs = NULL; +#else +static void (*esock_sctp_freeladdrs)(struct sockaddr *addrs) = NULL; +#endif + +#if defined(__GNUC__) && defined(HAVE_SCTP_GETPADDRS) +static typeof(sctp_getpaddrs) *esock_sctp_getpaddrs = NULL; +#else +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) *esock_sctp_freepaddrs = NULL; +#else +static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; +#endif + +*/ + +#endif /* #if defined(HAVE_SCTP_H) */ + + +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif +#include "sys.h" + +/* Socket stuff */ +#define INVALID_SOCKET -1 +// #define INVALID_EVENT -1 +#define SOCKET_ERROR -1 + +#endif /* ifdef __WIN32__ */ + +#include <erl_nif.h> + +#include "socket_dbg.h" +#include "socket_tarray.h" +#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 + + +/* Debug stuff... */ +#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 + + + +/* Socket stuff */ +#define INVALID_EVENT -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 *** */ + +/* 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 +/* This is really excessive, but just in case... */ +#define SOCKET_OPT_TCP_CONGESTION_NAME_MAX 256 +#endif + + +#if defined(TCP_CONGESTION) || defined(SO_BINDTODEVICE) +#define USE_GETOPT_STR_OPT +#define USE_SETOPT_STR_OPT +#endif + + + +/* *** 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_CLOSE 0x0080 + +#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_CLOSING (SOCKET_FLAG_CLOSE) + +#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) \ + (((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) +*/ + +#define SOCKET_SEND_FLAG_CONFIRM 0 +#define SOCKET_SEND_FLAG_DONTROUTE 1 +#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_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_TRUNC + +#define SOCKET_RECV_BUFFER_SIZE_DEFAULT 8192 +#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" : \ + ((__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 + +typedef union { + struct { + // 0 = not open, 1 = open + 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. + * + * 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 +#define SOCKET_PROTOCOL_ICMP 5 +#define SOCKET_PROTOCOL_IGMP 6 + +/* shutdown how */ +#define SOCKET_SHUTDOWN_HOW_RD 0 +#define SOCKET_SHUTDOWN_HOW_WR 1 +#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 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_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 + +#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_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_TIMESTAMP 31 +#define SOCKET_OPT_SOCK_TYPE 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_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 +#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_PKTINFO 19 +#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 +#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 +#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 +#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 +#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_RECVERR 24 +#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 + +#define SOCKET_OPT_TCP_CONGESTION 1 +#define SOCKET_OPT_TCP_CORK 2 +#define SOCKET_OPT_TCP_MAXSEG 7 +#define SOCKET_OPT_TCP_NODELAY 9 + +#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 +#define SOCKET_OPT_SCTP_INITMSG 18 +#define SOCKET_OPT_SCTP_MAXSEG 21 +#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 + + +#define SOCKET_SUPPORTS_OPTIONS 0x0001 +#define SOCKET_SUPPORTS_SCTP 0x0002 +#define SOCKET_SUPPORTS_IPV6 0x0003 + + + +/* =================================================================== * + * * + * Various enif macros * + * * + * =================================================================== */ + +#define SGDBG( proto ) ESOCK_DBG_PRINTF( data.dbg , proto ) +#define SSDBG( __D__ , proto ) ESOCK_DBG_PRINTF( (__D__)->dbg , proto ) + + + +/* =================================================================== * + * * + * Basic socket operations * + * * + * =================================================================== */ + +#ifdef __WIN32__ + +/* *** 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,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_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 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)) +#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_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)) +#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_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_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)) +#define sock_shutdown(s, how) shutdown((s), (how)) + +#endif /* !__WIN32__ */ + +#ifdef HAVE_SOCKLEN_T +# define SOCKLEN_T socklen_t +#else +# define SOCKLEN_T size_t +#endif + +#ifdef __WIN32__ +#define SOCKOPTLEN_T int +#else +#define SOCKOPTLEN_T SOCKLEN_T +#endif + +/* We can use the IPv4 def for this since the beginning + * is the same for INET and INET6 */ +#define which_address_port(sap) \ + ((((sap)->in4.sin_family == AF_INET) || \ + ((sap)->in4.sin_family == AF_INET6)) ? \ + ((sap)->in4.sin_port) : -1) + + +typedef struct { + int is_active; + ErlNifMonitor mon; +} ESockMonitor; + +typedef struct { + ErlNifPid pid; // PID of the requesting process + ESockMonitor 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* nextP; + SocketRequestor data; +} SocketRequestQueueElement; + +typedef struct { + SocketRequestQueueElement* first; + SocketRequestQueueElement* last; +} SocketRequestQueue; + + +typedef struct { + /* +++ The actual socket +++ */ + SOCKET sock; + HANDLE event; + + /* +++ Stuff "about" the socket +++ */ + int domain; + int type; + int protocol; + + unsigned int state; + SocketAddress remote; + unsigned int addrLen; + + ErlNifEnv* env; + + /* +++ Controller (owner) process +++ */ + ErlNifPid ctrlPid; + // ErlNifMonitor ctrlMon; + ESockMonitor ctrlMon; + + /* +++ Write stuff +++ */ + ErlNifMutex* writeMtx; + SocketRequestor currentWriter; + SocketRequestor* currentWriterP; // NULL or points to currentWriter + SocketRequestQueue writersQ; + BOOLEAN_T isWritable; + Uint32 writePkgCnt; + Uint32 writeByteCnt; + Uint32 writeTries; + Uint32 writeWaits; + Uint32 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 readCapacity; // DO WE NEED THIS + Uint32 readPkgCnt; + Uint32 readByteCnt; + Uint32 readTries; + Uint32 readWaits; + + /* +++ Accept stuff +++ */ + ErlNifMutex* accMtx; + SocketRequestor currentAcceptor; + SocketRequestor* currentAcceptorP; // NULL or points to currentAcceptor + SocketRequestQueue acceptorsQ; + + /* +++ Config & Misc stuff +++ */ + 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; + ErlNifPid closerPid; + ESockMonitor closerMon; + ErlNifEnv* closeEnv; + ERL_NIF_TERM closeRef; + BOOLEAN_T closeLocal; + +} SocketDescriptor; + + +/* Global stuff. + */ +typedef struct { + /* These are for debugging, testing and the like */ + // ERL_NIF_TERM version; + // ERL_NIF_TERM buildDate; + BOOLEAN_T dbg; + + BOOLEAN_T iow; // Where do we send this? Subscription? + ErlNifMutex* cntMtx; + Uint32 numSockets; + Uint32 numTypeStreams; + Uint32 numTypeDGrams; + Uint32 numTypeSeqPkgs; + Uint32 numDomainInet; + Uint32 numDomainInet6; + Uint32 numDomainLocal; + Uint32 numProtoIP; + Uint32 numProtoTCP; + Uint32 numProtoUDP; + Uint32 numProtoSCTP; +} SocketData; + + +/* ---------------------------------------------------------------------- + * F o r w a r d s + * ---------------------------------------------------------------------- + */ + + +extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */ + + +/* All the nif "callback" functions for the socket API has + * the exact same API: + * + * nif_<funcname>(ErlNifEnv* env, + * int argc, + * const ERL_NIF_TERM argv[]); + * + * So, to simplify, use some macro magic to define those. + * + * These are the functions making up the "official" API. + * Basically, these functions does some preliminary checks and argument + * extractions and then call the functions called 'n<funcname>', which + * does the actual work. Except for the info function. + * + * nif_info + * nif_supports + * nif_open + * nif_bind + * nif_connect + * nif_listen + * nif_accept + * nif_send + * nif_sendto + * nif_sendmsg + * nif_recv + * nif_recvfrom + * nif_recvmsg + * nif_close + * nif_shutdown + * nif_setopt + * nif_getopt + * nif_sockname + * nif_peername + * nif_finalize_connection + * nif_finalize_close + * nif_cancel + */ + +#define ESOCK_NIF_FUNCS \ + ESOCK_NIF_FUNC_DEF(info); \ + ESOCK_NIF_FUNC_DEF(supports); \ + ESOCK_NIF_FUNC_DEF(open); \ + ESOCK_NIF_FUNC_DEF(bind); \ + ESOCK_NIF_FUNC_DEF(connect); \ + ESOCK_NIF_FUNC_DEF(listen); \ + ESOCK_NIF_FUNC_DEF(accept); \ + ESOCK_NIF_FUNC_DEF(send); \ + ESOCK_NIF_FUNC_DEF(sendto); \ + ESOCK_NIF_FUNC_DEF(sendmsg); \ + ESOCK_NIF_FUNC_DEF(recv); \ + ESOCK_NIF_FUNC_DEF(recvfrom); \ + ESOCK_NIF_FUNC_DEF(recvmsg); \ + ESOCK_NIF_FUNC_DEF(close); \ + ESOCK_NIF_FUNC_DEF(shutdown); \ + ESOCK_NIF_FUNC_DEF(setopt); \ + ESOCK_NIF_FUNC_DEF(getopt); \ + ESOCK_NIF_FUNC_DEF(sockname); \ + ESOCK_NIF_FUNC_DEF(peername); \ + ESOCK_NIF_FUNC_DEF(finalize_connection); \ + ESOCK_NIF_FUNC_DEF(finalize_close); \ + ESOCK_NIF_FUNC_DEF(cancel); + +#define ESOCK_NIF_FUNC_DEF(F) \ + static ERL_NIF_TERM nif_##F(ErlNifEnv* env, \ + int argc, \ + const ERL_NIF_TERM argv[]); +ESOCK_NIF_FUNCS +#undef ESOCK_NIF_FUNC_DEF + + +#if !defined(__WIN32__) +/* And here comes the functions that does the actual work (for the most part) */ +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 nsupports_sctp(ErlNifEnv* env); +static ERL_NIF_TERM nsupports_ipv6(ErlNifEnv* env); + +static ERL_NIF_TERM nopen(ErlNifEnv* env, + int domain, + int type, + int protocol, + char* netns); +static ERL_NIF_TERM nbind(ErlNifEnv* env, + SocketDescriptor* descP, + SocketAddress* sockAddrP, + unsigned int addrLen); +static ERL_NIF_TERM nconnect(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM nlisten(ErlNifEnv* env, + SocketDescriptor* descP, + int backlog); +static ERL_NIF_TERM naccept(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept_listening(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ErlNifPid caller, + int save_errno); +static ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env, + SocketDescriptor* descP, + SOCKET accSock, + ErlNifPid caller, + SocketAddress* remote); +static ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref); +static ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + SOCKET accSock, + SocketAddress* remote); +static ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef, + int save_errno); +static ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ErlNifPid caller); +static ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ErlNifPid* pid, + unsigned int nextState); +static BOOLEAN_T naccept_accepted(ErlNifEnv* env, + SocketDescriptor* descP, + SOCKET accSock, + ErlNifPid pid, + SocketAddress* remote, + ERL_NIF_TERM* result); +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, + SocketAddress* toAddrP, + 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 bufSz, + int flags); +static ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + Uint16 bufLen, + Uint16 ctrlLen, + 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 nsetopt(ErlNifEnv* env, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt, + ERL_NIF_TERM eVal); + +/* Set OTP level options */ +static ERL_NIF_TERM nsetopt_otp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); +/* *** nsetopt_otp_debug *** + * *** nsetopt_otp_iow *** + * *** nsetopt_otp_ctrl_proc *** + * *** nsetopt_otp_rcvbuf *** + * *** nsetopt_otp_rcvctrlbuf *** + * *** nsetopt_otp_sndctrlbuf *** + */ +#define NSETOPT_OTP_FUNCS \ + NSETOPT_OTP_FUNC_DEF(debug); \ + NSETOPT_OTP_FUNC_DEF(iow); \ + NSETOPT_OTP_FUNC_DEF(ctrl_proc); \ + NSETOPT_OTP_FUNC_DEF(rcvbuf); \ + NSETOPT_OTP_FUNC_DEF(rcvctrlbuf); \ + NSETOPT_OTP_FUNC_DEF(sndctrlbuf); +#define NSETOPT_OTP_FUNC_DEF(F) \ + static ERL_NIF_TERM nsetopt_otp_##F(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + ERL_NIF_TERM eVal) +NSETOPT_OTP_FUNCS +#undef NSETOPT_OTP_FUNC_DEF + +/* Set native options */ +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); + + +/* *** 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, + 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, + 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_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, + 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_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, + 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_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, + 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, + 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, + 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_SOURCE_MEMBERSHIP) +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_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, + ERL_NIF_TERM eVal); +#endif +#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) +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* 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, + 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, + ERL_NIF_TERM eVal); +#endif +#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_MULTICAST_TTL) +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_PKTINFO) +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, + 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_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, + 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_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, + 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, + 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, + 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(HAVE_IPV6) +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, + 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, + 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_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, + 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, + 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_MULTICAST_HOPS) +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_MULTICAST_LOOP) +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, + 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, + 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, + 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(HAVE_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_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, + 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, + 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, + 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 +#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, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + ERL_NIF_TERM eOpt); + +static ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt); +/* *** ngetopt_otp_debug *** + * *** ngetopt_otp_iow *** + * *** ngetopt_otp_ctrl_proc *** + * *** ngetopt_otp_rcvbuf *** + * *** ngetopt_otp_rcvctrlbuf *** + * *** ngetopt_otp_sndctrlbuf *** + * *** ngetopt_otp_fd *** + * *** ngetopt_otp_domain *** + * *** ngetopt_otp_type *** + * *** ngetopt_otp_protocol *** + */ +#define NGETOPT_OTP_FUNCS \ + NGETOPT_OTP_FUNC_DEF(debug); \ + NGETOPT_OTP_FUNC_DEF(iow); \ + NGETOPT_OTP_FUNC_DEF(ctrl_proc); \ + NGETOPT_OTP_FUNC_DEF(rcvbuf); \ + NGETOPT_OTP_FUNC_DEF(rcvctrlbuf); \ + NGETOPT_OTP_FUNC_DEF(sndctrlbuf); \ + NGETOPT_OTP_FUNC_DEF(fd); \ + NGETOPT_OTP_FUNC_DEF(domain); \ + NGETOPT_OTP_FUNC_DEF(type); \ + NGETOPT_OTP_FUNC_DEF(protocol); +#define NGETOPT_OTP_FUNC_DEF(F) \ + static ERL_NIF_TERM ngetopt_otp_##F(ErlNifEnv* env, \ + SocketDescriptor* descP) +NGETOPT_OTP_FUNCS +#undef NGETOPT_OTP_FUNC_DEF + +static ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + ERL_NIF_TERM 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, + int eOpt); +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_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); +#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); +#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_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); +#endif +#if defined(SO_PRIORITY) +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); +#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); +#endif +#if defined(SO_REUSEADDR) +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); +#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); +#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); +#endif +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_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); +#endif +#if defined(IP_MTU) +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_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); +#endif +#if defined(IP_MULTICAST_LOOP) +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_NODEFRAG) +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_RECVDSTADDR) +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); +#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_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); +#endif +#if defined(IP_RECVTTL) +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); +#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); +#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); +#endif +#if defined(HAVE_IPV6) +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_DSTOPTS) +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); +#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); +#endif +#if defined(IPV6_MTU_DISCOVER) +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_MULTICAST_IF) +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_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); +#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); +#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); +#endif + +#endif // defined(HAVE_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_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); +#endif +#if defined(SCTP_DISABLE_FRAGMENTS) +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_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); +#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); +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 sockRef, + 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 sockRef, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_accept_current(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef); +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 sockRef, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_send_current(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef); +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 sockRef, + ERL_NIF_TERM opRef); +static ERL_NIF_TERM ncancel_recv_current(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef); +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); +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); + +#if defined(USE_SETOPT_STR_OPT) +static ERL_NIF_TERM nsetopt_str_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + int max, + ERL_NIF_TERM eVal); +#endif +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 nsetopt_timeval_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); + +#if defined(USE_GETOPT_STR_OPT) +static ERL_NIF_TERM ngetopt_str_opt(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + int max); +#endif +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 ngetopt_timeval_opt(ErlNifEnv* env, + SocketDescriptor* descP, + 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, + ssize_t dataSize, + int saveErrno, + ERL_NIF_TERM sockRef, + 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, + ERL_NIF_TERM sockRef); +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, + int read, + 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, + int read, + int saveErrno, + 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, + int read, + int saveErrno, + struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); + +static ERL_NIF_TERM nfinalize_connection(ErlNifEnv* env, + SocketDescriptor* descP); +static ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, + SocketDescriptor* descP); + +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); +extern char* decode_cmsghdrs(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + char* cmsgHdrBufP, + size_t cmsgHdrBufLen, + size_t* cmsgHdrBufUsed); +extern char* decode_cmsghdr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + char* bufP, + size_t rem, + size_t* used); +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, + int type, + unsigned char* dataP, + 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, + unsigned char* dataP, + size_t dataPos, + size_t dataLen, + ERL_NIF_TERM* eCMsgHdrData); +#if defined(HAVE_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, + ERL_NIF_TERM* flags); +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); +#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 +#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, + ERL_NIF_TERM eVal, + BOOLEAN_T* val); +*/ +static BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, + ERL_NIF_TERM eVal, + int* opt, + 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); + +static void inform_waiting_procs(ErlNifEnv* env, + char* role, + SocketDescriptor* descP, + SocketRequestQueue* q, + 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); + + +static BOOLEAN_T edomain2domain(int edomain, int* domain); +static BOOLEAN_T etype2type(int etype, int* type); +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); +static BOOLEAN_T elevel2level(BOOLEAN_T isEncoded, + int eLevel, + BOOLEAN_T* isOTP, + int* level); +#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* 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); + + + +/* *** activate_next_acceptor *** + * *** activate_next_writer *** + * *** activate_next_reader *** + * + * All the activate-next functions for acceptor, writer and reader + * have exactly the same API, so we apply some macro magic to simplify. + * They simply operates on dufferent data structures. + * + */ + +#define ACTIVATE_NEXT_FUNCS_DEFS \ + ACTIVATE_NEXT_FUNC_DEF(acceptor) \ + ACTIVATE_NEXT_FUNC_DEF(writer) \ + ACTIVATE_NEXT_FUNC_DEF(reader) + +#define ACTIVATE_NEXT_FUNC_DEF(F) \ + static BOOLEAN_T activate_next_##F(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + ERL_NIF_TERM sockRef); +ACTIVATE_NEXT_FUNCS_DEFS +#undef ACTIVATE_NEXT_FUNC_DEF + +/* +static BOOLEAN_T activate_next(ErlNifEnv* env, + SocketDescriptor* descP, + SocketRequestor* reqP, + SocketRequestQueue* q, + ERL_NIF_TERM sockRef); +*/ + +/* *** acceptor_search4pid | writer_search4pid | reader_search4pid *** + * *** acceptor_push | writer_push | reader_push *** + * *** acceptor_pop | writer_pop | reader_pop *** + * *** acceptor_unqueue | writer_unqueue | reader_unqueue *** + * + * All the queue operator functions (search4pid, push, pop + * and unqueue) for acceptor, writer and reader has exactly + * the same API, so we apply some macro magic to simplify. + */ + +#define ESOCK_OPERATOR_FUNCS_DEFS \ + ESOCK_OPERATOR_FUNCS_DEF(acceptor) \ + ESOCK_OPERATOR_FUNCS_DEF(writer) \ + ESOCK_OPERATOR_FUNCS_DEF(reader) + +#define ESOCK_OPERATOR_FUNCS_DEF(O) \ + static BOOLEAN_T O##_search4pid(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + ErlNifPid* pid); \ + static ERL_NIF_TERM O##_push(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + ErlNifPid pid, \ + ERL_NIF_TERM ref); \ + static BOOLEAN_T O##_pop(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + SocketRequestor* reqP); \ + static BOOLEAN_T O##_unqueue(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + const ErlNifPid* pid); +ESOCK_OPERATOR_FUNCS_DEFS +#undef ESOCK_OPERATOR_FUNCS_DEF + +static BOOLEAN_T requestor_pop(SocketRequestQueue* q, + SocketRequestor* reqP); + +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, + 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); + +#endif // if defined(__WIN32__) + +/* +#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, + int fd, + int is_direct_call); +static void socket_down(ErlNifEnv* env, + void* obj, + const ErlNifPid* pid, + const ErlNifMonitor* mon); + +#if !defined(__WIN32__) + +static void socket_down_acceptor(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid); +static void socket_down_writer(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid); +static void socket_down_reader(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid); + +static char* esock_send_close_msg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef); +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, + ErlNifEnv* msg_env); +static char* esock_send_msg(ErlNifEnv* env, + ERL_NIF_TERM msg, + ErlNifPid* pid, + ErlNifEnv* msg_env); + +static int esock_select_read(ErlNifEnv* env, + ErlNifEvent event, + void* obj, + const ErlNifPid* pid, + ERL_NIF_TERM ref); +static int esock_select_write(ErlNifEnv* env, + ErlNifEvent event, + void* obj, + const ErlNifPid* pid, + ERL_NIF_TERM ref); +static int esock_select_stop(ErlNifEnv* env, + ErlNifEvent event, + void* obj); +static int esock_select_cancel(ErlNifEnv* env, + ErlNifEvent event, + enum ErlNifSelectFlags mode, + void* obj); + +static BOOLEAN_T extract_debug(ErlNifEnv* env, + ERL_NIF_TERM map); +static BOOLEAN_T extract_iow(ErlNifEnv* env, + ERL_NIF_TERM map); + +#endif // if defined(__WIN32__) + +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 */ + + + +/* (special) error string constants */ +static char str_exmon[] = "exmonitor"; // failed monitor +static char str_exself[] = "exself"; // failed self +static char str_exsend[] = "exsend"; // failed send + + + +/* *** Global atoms *** */ +#define GLOBAL_ATOMS \ + GLOBAL_ATOM_DECL(abort); \ + GLOBAL_ATOM_DECL(accept); \ + GLOBAL_ATOM_DECL(acceptconn); \ + GLOBAL_ATOM_DECL(acceptfilter); \ + GLOBAL_ATOM_DECL(adaption_layer); \ + GLOBAL_ATOM_DECL(addr); \ + GLOBAL_ATOM_DECL(addrform); \ + GLOBAL_ATOM_DECL(add_membership); \ + GLOBAL_ATOM_DECL(add_source_membership); \ + GLOBAL_ATOM_DECL(any); \ + GLOBAL_ATOM_DECL(associnfo); \ + GLOBAL_ATOM_DECL(authhdr); \ + GLOBAL_ATOM_DECL(auth_active_key); \ + GLOBAL_ATOM_DECL(auth_asconf); \ + GLOBAL_ATOM_DECL(auth_chunk); \ + GLOBAL_ATOM_DECL(auth_delete_key); \ + GLOBAL_ATOM_DECL(auth_key); \ + GLOBAL_ATOM_DECL(auth_level); \ + GLOBAL_ATOM_DECL(autoclose); \ + GLOBAL_ATOM_DECL(bindtodevice); \ + GLOBAL_ATOM_DECL(block_source); \ + GLOBAL_ATOM_DECL(broadcast); \ + GLOBAL_ATOM_DECL(busy_poll); \ + GLOBAL_ATOM_DECL(checksum); \ + GLOBAL_ATOM_DECL(close); \ + GLOBAL_ATOM_DECL(connect); \ + GLOBAL_ATOM_DECL(congestion); \ + GLOBAL_ATOM_DECL(context); \ + GLOBAL_ATOM_DECL(cork); \ + GLOBAL_ATOM_DECL(credentials); \ + GLOBAL_ATOM_DECL(ctrl); \ + GLOBAL_ATOM_DECL(ctrunc); \ + GLOBAL_ATOM_DECL(data); \ + GLOBAL_ATOM_DECL(debug); \ + GLOBAL_ATOM_DECL(default_send_params); \ + GLOBAL_ATOM_DECL(delayed_ack_time); \ + GLOBAL_ATOM_DECL(dgram); \ + GLOBAL_ATOM_DECL(disable_fragments); \ + GLOBAL_ATOM_DECL(domain); \ + GLOBAL_ATOM_DECL(dontfrag); \ + GLOBAL_ATOM_DECL(dontroute); \ + GLOBAL_ATOM_DECL(drop_membership); \ + GLOBAL_ATOM_DECL(drop_source_membership); \ + GLOBAL_ATOM_DECL(dstopts); \ + GLOBAL_ATOM_DECL(eor); \ + GLOBAL_ATOM_DECL(error); \ + GLOBAL_ATOM_DECL(errqueue); \ + GLOBAL_ATOM_DECL(esp_network_level); \ + GLOBAL_ATOM_DECL(esp_trans_level); \ + GLOBAL_ATOM_DECL(events); \ + GLOBAL_ATOM_DECL(explicit_eor); \ + GLOBAL_ATOM_DECL(faith); \ + GLOBAL_ATOM_DECL(false); \ + GLOBAL_ATOM_DECL(family); \ + GLOBAL_ATOM_DECL(flags); \ + GLOBAL_ATOM_DECL(flowinfo); \ + GLOBAL_ATOM_DECL(fragment_interleave); \ + GLOBAL_ATOM_DECL(freebind); \ + GLOBAL_ATOM_DECL(get_peer_addr_info); \ + GLOBAL_ATOM_DECL(hdrincl); \ + GLOBAL_ATOM_DECL(hmac_ident); \ + GLOBAL_ATOM_DECL(hoplimit); \ + GLOBAL_ATOM_DECL(hopopts); \ + GLOBAL_ATOM_DECL(ifindex); \ + GLOBAL_ATOM_DECL(inet); \ + GLOBAL_ATOM_DECL(inet6); \ + GLOBAL_ATOM_DECL(info); \ + GLOBAL_ATOM_DECL(initmsg); \ + GLOBAL_ATOM_DECL(iov); \ + GLOBAL_ATOM_DECL(ip); \ + GLOBAL_ATOM_DECL(ipcomp_level); \ + GLOBAL_ATOM_DECL(ipv6); \ + GLOBAL_ATOM_DECL(i_want_mapped_v4_addr); \ + GLOBAL_ATOM_DECL(join_group); \ + GLOBAL_ATOM_DECL(keepalive); \ + GLOBAL_ATOM_DECL(keepcnt); \ + GLOBAL_ATOM_DECL(keepidle); \ + GLOBAL_ATOM_DECL(keepintvl); \ + GLOBAL_ATOM_DECL(leave_group); \ + GLOBAL_ATOM_DECL(level); \ + GLOBAL_ATOM_DECL(linger); \ + GLOBAL_ATOM_DECL(local); \ + GLOBAL_ATOM_DECL(local_auth_chunks); \ + GLOBAL_ATOM_DECL(loopback); \ + GLOBAL_ATOM_DECL(lowdelay); \ + GLOBAL_ATOM_DECL(mark); \ + GLOBAL_ATOM_DECL(maxburst); \ + GLOBAL_ATOM_DECL(maxseg); \ + GLOBAL_ATOM_DECL(md5sig); \ + GLOBAL_ATOM_DECL(mincost); \ + GLOBAL_ATOM_DECL(minttl); \ + GLOBAL_ATOM_DECL(msfilter); \ + GLOBAL_ATOM_DECL(mtu); \ + GLOBAL_ATOM_DECL(mtu_discover); \ + GLOBAL_ATOM_DECL(multicast_all); \ + GLOBAL_ATOM_DECL(multicast_hops); \ + GLOBAL_ATOM_DECL(multicast_if); \ + GLOBAL_ATOM_DECL(multicast_loop); \ + GLOBAL_ATOM_DECL(multicast_ttl); \ + GLOBAL_ATOM_DECL(nodelay); \ + GLOBAL_ATOM_DECL(nodefrag); \ + GLOBAL_ATOM_DECL(noopt); \ + GLOBAL_ATOM_DECL(nopush); \ + GLOBAL_ATOM_DECL(not_found); \ + GLOBAL_ATOM_DECL(not_owner); \ + GLOBAL_ATOM_DECL(ok); \ + GLOBAL_ATOM_DECL(oob); \ + GLOBAL_ATOM_DECL(oobinline); \ + GLOBAL_ATOM_DECL(options); \ + GLOBAL_ATOM_DECL(origdstaddr); \ + GLOBAL_ATOM_DECL(partial_delivery_point); \ + GLOBAL_ATOM_DECL(passcred); \ + GLOBAL_ATOM_DECL(path); \ + GLOBAL_ATOM_DECL(peekcred); \ + GLOBAL_ATOM_DECL(peek_off); \ + GLOBAL_ATOM_DECL(peer_addr_params); \ + GLOBAL_ATOM_DECL(peer_auth_chunks); \ + GLOBAL_ATOM_DECL(pktinfo); \ + GLOBAL_ATOM_DECL(pktoptions); \ + GLOBAL_ATOM_DECL(port); \ + GLOBAL_ATOM_DECL(portrange); \ + GLOBAL_ATOM_DECL(primary_addr); \ + GLOBAL_ATOM_DECL(priority); \ + GLOBAL_ATOM_DECL(protocol); \ + GLOBAL_ATOM_DECL(raw); \ + GLOBAL_ATOM_DECL(rcvbuf); \ + GLOBAL_ATOM_DECL(rcvbufforce); \ + GLOBAL_ATOM_DECL(rcvlowat); \ + GLOBAL_ATOM_DECL(rcvtimeo); \ + GLOBAL_ATOM_DECL(rdm); \ + GLOBAL_ATOM_DECL(recv); \ + GLOBAL_ATOM_DECL(recvdstaddr); \ + GLOBAL_ATOM_DECL(recverr); \ + GLOBAL_ATOM_DECL(recvfrom); \ + GLOBAL_ATOM_DECL(recvif); \ + GLOBAL_ATOM_DECL(recvmsg); \ + GLOBAL_ATOM_DECL(recvopts); \ + GLOBAL_ATOM_DECL(recvorigdstaddr); \ + GLOBAL_ATOM_DECL(recvpktinfo); \ + GLOBAL_ATOM_DECL(recvtclass); \ + GLOBAL_ATOM_DECL(recvtos); \ + GLOBAL_ATOM_DECL(recvttl); \ + GLOBAL_ATOM_DECL(reliability); \ + GLOBAL_ATOM_DECL(reset_streams); \ + GLOBAL_ATOM_DECL(retopts); \ + GLOBAL_ATOM_DECL(reuseaddr); \ + GLOBAL_ATOM_DECL(reuseport); \ + GLOBAL_ATOM_DECL(rights); \ + GLOBAL_ATOM_DECL(router_alert); \ + GLOBAL_ATOM_DECL(rthdr); \ + GLOBAL_ATOM_DECL(rtoinfo); \ + GLOBAL_ATOM_DECL(rxq_ovfl); \ + GLOBAL_ATOM_DECL(scope_id); \ + GLOBAL_ATOM_DECL(sctp); \ + GLOBAL_ATOM_DECL(sec); \ + GLOBAL_ATOM_DECL(select_failed); \ + GLOBAL_ATOM_DECL(select_sent); \ + GLOBAL_ATOM_DECL(send); \ + GLOBAL_ATOM_DECL(sendmsg); \ + GLOBAL_ATOM_DECL(sendsrcaddr); \ + GLOBAL_ATOM_DECL(sendto); \ + GLOBAL_ATOM_DECL(seqpacket); \ + GLOBAL_ATOM_DECL(setfib); \ + GLOBAL_ATOM_DECL(set_peer_primary_addr); \ + GLOBAL_ATOM_DECL(socket); \ + GLOBAL_ATOM_DECL(sndbuf); \ + GLOBAL_ATOM_DECL(sndbufforce); \ + GLOBAL_ATOM_DECL(sndlowat); \ + GLOBAL_ATOM_DECL(sndtimeo); \ + GLOBAL_ATOM_DECL(spec_dst); \ + GLOBAL_ATOM_DECL(status); \ + GLOBAL_ATOM_DECL(stream); \ + GLOBAL_ATOM_DECL(syncnt); \ + GLOBAL_ATOM_DECL(tclass); \ + GLOBAL_ATOM_DECL(tcp); \ + GLOBAL_ATOM_DECL(throughput); \ + GLOBAL_ATOM_DECL(timestamp); \ + GLOBAL_ATOM_DECL(tos); \ + GLOBAL_ATOM_DECL(transparent); \ + GLOBAL_ATOM_DECL(true); \ + GLOBAL_ATOM_DECL(trunc); \ + GLOBAL_ATOM_DECL(ttl); \ + GLOBAL_ATOM_DECL(type); \ + GLOBAL_ATOM_DECL(udp); \ + GLOBAL_ATOM_DECL(unblock_source); \ + GLOBAL_ATOM_DECL(undefined); \ + GLOBAL_ATOM_DECL(unicast_hops); \ + GLOBAL_ATOM_DECL(unknown); \ + GLOBAL_ATOM_DECL(usec); \ + GLOBAL_ATOM_DECL(user_timeout); \ + GLOBAL_ATOM_DECL(use_ext_recvinfo); \ + GLOBAL_ATOM_DECL(use_min_mtu); \ + GLOBAL_ATOM_DECL(v6only); + + +/* *** Global error reason atoms *** */ +#define GLOBAL_ERROR_REASON_ATOMS \ + GLOBAL_ATOM_DECL(eagain); \ + GLOBAL_ATOM_DECL(eafnosupport); \ + GLOBAL_ATOM_DECL(einval); + + +#define GLOBAL_ATOM_DECL(A) ERL_NIF_TERM esock_atom_##A +GLOBAL_ATOMS +GLOBAL_ERROR_REASON_ATOMS +#undef GLOBAL_ATOM_DECL +ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') + +/* *** Local atoms *** */ +#define LOCAL_ATOMS \ + LOCAL_ATOM_DECL(adaptation_layer); \ + LOCAL_ATOM_DECL(address); \ + LOCAL_ATOM_DECL(association); \ + LOCAL_ATOM_DECL(assoc_id); \ + LOCAL_ATOM_DECL(authentication); \ + LOCAL_ATOM_DECL(bool); \ + LOCAL_ATOM_DECL(close); \ + LOCAL_ATOM_DECL(closed); \ + LOCAL_ATOM_DECL(closing); \ + LOCAL_ATOM_DECL(cookie_life); \ + LOCAL_ATOM_DECL(data_in); \ + LOCAL_ATOM_DECL(do); \ + LOCAL_ATOM_DECL(dont); \ + LOCAL_ATOM_DECL(exclude); \ + LOCAL_ATOM_DECL(false); \ + LOCAL_ATOM_DECL(global_counters); \ + LOCAL_ATOM_DECL(in4_sockaddr); \ + LOCAL_ATOM_DECL(in6_sockaddr); \ + LOCAL_ATOM_DECL(include); \ + LOCAL_ATOM_DECL(initial); \ + LOCAL_ATOM_DECL(int); \ + LOCAL_ATOM_DECL(interface); \ + LOCAL_ATOM_DECL(iow); \ + LOCAL_ATOM_DECL(local_rwnd); \ + LOCAL_ATOM_DECL(max); \ + LOCAL_ATOM_DECL(max_attempts); \ + LOCAL_ATOM_DECL(max_init_timeo); \ + LOCAL_ATOM_DECL(max_instreams); \ + LOCAL_ATOM_DECL(max_rxt); \ + LOCAL_ATOM_DECL(min); \ + LOCAL_ATOM_DECL(mode); \ + LOCAL_ATOM_DECL(multiaddr); \ + LOCAL_ATOM_DECL(null); \ + LOCAL_ATOM_DECL(num_dinet); \ + LOCAL_ATOM_DECL(num_dinet6); \ + LOCAL_ATOM_DECL(num_dlocal); \ + LOCAL_ATOM_DECL(num_outstreams); \ + LOCAL_ATOM_DECL(num_peer_dests); \ + LOCAL_ATOM_DECL(num_pip); \ + LOCAL_ATOM_DECL(num_psctp); \ + LOCAL_ATOM_DECL(num_ptcp); \ + LOCAL_ATOM_DECL(num_pudp); \ + LOCAL_ATOM_DECL(num_sockets); \ + LOCAL_ATOM_DECL(num_tdgrams); \ + LOCAL_ATOM_DECL(num_tseqpkgs); \ + LOCAL_ATOM_DECL(num_tstreams); \ + LOCAL_ATOM_DECL(partial_delivery); \ + LOCAL_ATOM_DECL(peer_error); \ + LOCAL_ATOM_DECL(peer_rwnd); \ + LOCAL_ATOM_DECL(probe); \ + LOCAL_ATOM_DECL(select); \ + LOCAL_ATOM_DECL(sender_dry); \ + LOCAL_ATOM_DECL(send_failure); \ + LOCAL_ATOM_DECL(shutdown); \ + LOCAL_ATOM_DECL(slist); \ + LOCAL_ATOM_DECL(sourceaddr); \ + LOCAL_ATOM_DECL(timeout); \ + LOCAL_ATOM_DECL(true); \ + LOCAL_ATOM_DECL(want); + +/* Local error reason atoms */ +#define LOCAL_ERROR_REASON_ATOMS \ + LOCAL_ATOM_DECL(eisconn); \ + LOCAL_ATOM_DECL(enotclosing); \ + LOCAL_ATOM_DECL(enotconn); \ + LOCAL_ATOM_DECL(exalloc); \ + LOCAL_ATOM_DECL(exbadstate); \ + LOCAL_ATOM_DECL(exbusy); \ + LOCAL_ATOM_DECL(exmon); \ + LOCAL_ATOM_DECL(exself); \ + LOCAL_ATOM_DECL(exsend); + +#define LOCAL_ATOM_DECL(LA) static ERL_NIF_TERM atom_##LA +LOCAL_ATOMS +LOCAL_ERROR_REASON_ATOMS +#undef LOCAL_ATOM_DECL + + +/* *** Sockets *** */ +static ErlNifResourceType* sockets; +static ErlNifResourceTypeInit socketInit = { + socket_dtor, + socket_stop, + (ErlNifResourceDown*) socket_down +}; + +// Initiated when the nif is loaded +static SocketData data; + + +/* ---------------------------------------------------------------------- + * N I F F u n c t i o n s + * ---------------------------------------------------------------------- + * + * Utility and admin functions: + * ---------------------------- + * nif_info/0 + * nif_supports/1 + * (nif_debug/1) + * + * The "proper" socket functions: + * ------------------------------ + * nif_open(Domain, Type, Protocol, Extra) + * nif_bind(Sock, LocalAddr) + * nif_connect(Sock, SockAddr) + * nif_listen(Sock, Backlog) + * 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) + * nif_close(Sock) + * nif_shutdown(Sock, How) + * nif_sockname(Sock) + * nif_peername(Sock) + * + * And some functions to manipulate and retrieve socket options: + * ------------------------------------------------------------- + * nif_setopt/5 + * nif_getopt/4 + * + * And some utility functions: + * ------------------------------------------------------------- + * + * And some socket admin functions: + * ------------------------------------------------------------- + * nif_cancel(Sock, Ref) + */ + + +/* ---------------------------------------------------------------------- + * nif_info + * + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + 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[] = {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); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &info)) + return enif_make_badarg(env); + + return info; + } +#endif +} + + + +/* ---------------------------------------------------------------------- + * nif_supports + * + * Description: + * This function is intended to answer the question: "Is X supported?" + * Currently three keys are "supported": options | sctp | ipv6 + * 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_supports(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + 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); + } + + return nsupports(env, key); +#endif +} + + + +/* 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. + */ +#if !defined(__WIN32__) +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; + + case SOCKET_SUPPORTS_SCTP: + result = nsupports_sctp(env); + break; + + case SOCKET_SUPPORTS_IPV6: + result = nsupports_ipv6(env); + break; + + default: + result = esock_atom_false; + break; + } + + return result; +} +#endif + + +#if !defined(__WIN32__) +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; +} +#endif + + + +#if !defined(__WIN32__) +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_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); +#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); + + + /* *** 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; +} +#endif + + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsupports_options_ip(ErlNifEnv* env) +{ + SocketTArray opts = TARRAY_CREATE(128); + ERL_NIF_TERM tmp, optsL; + + + /* *** 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; +} +#endif + + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsupports_options_ipv6(ErlNifEnv* env) +{ + SocketTArray opts = TARRAY_CREATE(128); + ERL_NIF_TERM tmp, optsL; + + + /* *** 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; +} +#endif + + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsupports_options_tcp(ErlNifEnv* env) +{ + SocketTArray opts = TARRAY_CREATE(32); + ERL_NIF_TERM tmp, optsL; + + + /* *** 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; +} +#endif + + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsupports_options_udp(ErlNifEnv* env) +{ + SocketTArray opts = TARRAY_CREATE(8); + ERL_NIF_TERM tmp, optsL; + + + /* *** 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; +} +#endif + + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsupports_options_sctp(ErlNifEnv* env) +{ + SocketTArray opts = TARRAY_CREATE(64); + ERL_NIF_TERM tmp, optsL; + + + /* *** 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; +} +#endif + + + +#if !defined(__WIN32__) +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; +} +#endif + + + +#if !defined(__WIN32__) +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; +} +#endif + + + +/* ---------------------------------------------------------------------- + * nif_open + * + * Description: + * Create an endpoint for communication. + * + * Arguments: + * 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! + * We sould also use this for the fd value, in case we should use + * an already existing (file) descriptor. + */ +static +ERL_NIF_TERM nif_open(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + int edomain, etype, eproto; + int domain, type, proto; + char* netns; + ERL_NIF_TERM emap; + ERL_NIF_TERM result; + + SGDBG( ("SOCKET", "nif_open -> entry with %d args\r\n", argc) ); + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 4) || + !GET_INT(env, argv[0], &edomain) || + !GET_INT(env, argv[1], &etype) || + !IS_MAP(env, argv[3])) { + return enif_make_badarg(env); + } + 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], eproto, emap) ); + + if (!edomain2domain(edomain, &domain)) { + SGDBG( ("SOCKET", "nif_open -> invalid domain: %d\r\n", edomain) ); + return esock_make_error(env, esock_atom_einval); + } + + if (!etype2type(etype, &type)) { + SGDBG( ("SOCKET", "nif_open -> invalid type: %d\r\n", etype) ); + return esock_make_error(env, esock_atom_einval); + } + + if (!eproto2proto(env, eproto, &proto)) { + SGDBG( ("SOCKET", "nif_open -> invalid protocol: %d\r\n", eproto) ); + return esock_make_error(env, esock_atom_einval); + } + +#ifdef HAVE_SETNS + /* We *currently* only support one extra option: netns */ + if (!emap2netns(env, emap, &netns)) { + SGDBG( ("SOCKET", "nif_open -> namespace: %s\r\n", netns) ); + return enif_make_badarg(env); + } +#else + netns = NULL; +#endif + + + result = nopen(env, domain, type, proto, netns); + + SGDBG( ("SOCKET", "nif_open -> done with result: " + "\r\n %T" + "\r\n", result) ); + + return result; + +#endif // if defined(__WIN32__) +} + + +/* 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. + */ +#if !defined(__WIN32__) +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 = 0; +#endif + + SGDBG( ("SOCKET", "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 ((netns != NULL) && + !change_network_namespace(netns, ¤t_ns, &save_errno)) + return esock_make_error_errno(env, save_errno); +#endif + + 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)) + return esock_make_error_errno(env, save_errno); + + if (netns != NULL) + FREE(netns); +#endif + + + if ((event = sock_create_event(sock)) == INVALID_EVENT) { + save_errno = sock_errno(); + while ((sock_close(sock) == INVALID_SOCKET) && (sock_errno() == EINTR)); + return esock_make_error_errno(env, save_errno); + } + + SGDBG( ("SOCKET", "nopen -> event success: %d\r\n", event) ); + + SET_NONBLOCKING(sock); + + + /* Create and initiate the socket "descriptor" */ + 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->state = SOCKET_STATE_OPEN; + descP->domain = domain; + descP->type = type; + descP->protocol = protocol; + + /* Does this apply to other types? Such as RAW? + * Also, is this really correct? Should we not wait for bind? + */ + 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, + * which will be costly. Send it somewhere? + */ + res = enif_make_resource(env, descP); + enif_release_resource(descP); + + /* 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 esock_make_error(env, atom_exself); + + if (MONP("nopen -> ctrl", + env, descP, + &descP->ctrlPid, + &descP->ctrlMon) != 0) + return esock_make_error(env, atom_exmon); + + + inc_socket(domain, type, protocol); + + return esock_make_ok2(env, res); +} +#endif // if !defined(__WIN32__) + + + +#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. + */ +#if !defined(__WIN32__) +static +BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err) +{ + int save_errno; + int current_ns = 0; + int new_ns = 0; + + SGDBG( ("SOCKET", "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) { + *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; + } +} +#endif // if !defined(__WIN32__) + + +/* *** restore network namespace *** + * Restore the previous namespace (see above). + */ +#if !defined(__WIN32__) +static +BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err) +{ + int save_errno; + + SGDBG( ("SOCKET", "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. + * 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 // if !defined(__WIN32__) +#endif // ifdef HAVE_SETNS + + + +/* ---------------------------------------------------------------------- + * nif_bind + * + * Description: + * Bind a name to a socket. + * + * Arguments: + * [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, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + 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 */ + + if ((argc != 2) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + return enif_make_badarg(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" + "\r\n SockAddr: %T" + "\r\n", descP->sock, descP->state, argv[0], eSockAddr) ); + + /* Make sure we are ready + * Not sure how this would even happen, but... + */ + if (descP->state != SOCKET_STATE_OPEN) + return esock_make_error(env, atom_exbadstate); + + if ((xres = esock_decode_sockaddr(env, eSockAddr, &sockAddr, &addrLen)) != NULL) + return esock_make_error_str(env, xres); + + return nbind(env, descP, &sockAddr, addrLen); + +#endif // if defined(__WIN32__) +} + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nbind(ErlNifEnv* env, + SocketDescriptor* descP, + SocketAddress* sockAddrP, + unsigned int addrLen) +{ + int port, ntohs_port; + + SSDBG( descP, ("SOCKET", "nbind -> try bind\r\n") ); + + if (IS_SOCKET_ERROR(sock_bind(descP->sock, + (struct sockaddr*) sockAddrP, addrLen))) { + 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) { + 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; + } + + ntohs_port = sock_ntohs(port); + + SSDBG( descP, ("SOCKET", "nbind -> done with port = %d\r\n", ntohs_port) ); + + return esock_make_ok2(env, MKI(env, ntohs_port)); + +} +#endif // if !defined(__WIN32__) + + + + +/* ---------------------------------------------------------------------- + * nif_connect + * + * Description: + * Initiate a connection on a socket + * + * Arguments: + * Socket (ref) - Points to the socket descriptor. + * SockAddr - Socket Address of "remote" host. + * This is sockaddr(), which is either + * sockaddr_in4 or sockaddr_in6. + */ +static +ERL_NIF_TERM nif_connect(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + ERL_NIF_TERM res, eSockAddr; + char* xres; + + SGDBG( ("SOCKET", "nif_connect -> entry with argc: %d\r\n", argc) ); + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 2) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + return enif_make_badarg(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); + } + + + MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); + + res = nconnect(env, descP); + + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); + + return res; + +#endif // if !defined(__WIN32__) +} + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nconnect(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM res, ref; + int code, sres, save_errno = 0; + + /* + * Verify that we are where in the proper state + */ + + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + + if (!IS_OPEN(descP)) { + SSDBG( descP, ("SOCKET", "nif_connect -> not open\r\n") ); + return esock_make_error(env, atom_exbadstate); + } + + if (IS_CONNECTED(descP)) { + SSDBG( descP, ("SOCKET", "nif_connect -> already connected\r\n") ); + return esock_make_error(env, atom_eisconn); + } + + if (IS_CONNECTING(descP)) { + SSDBG( descP, ("SOCKET", "nif_connect -> already connecting\r\n") ); + return esock_make_error(env, esock_atom_einval); + } + + + /* + * And attempt to connect + */ + + code = sock_connect(descP->sock, + (struct sockaddr*) &descP->remote, + descP->addrLen); + save_errno = sock_errno(); + + SSDBG( descP, ("SOCKET", "nif_connect -> connect result: %d, %d\r\n", + code, save_errno) ); + + if (IS_SOCKET_ERROR(code) && + ((save_errno == ERRNO_BLOCK) || /* Winsock2 */ + (save_errno == EINPROGRESS))) { /* Unix & OSE!! */ + ref = MKREF(env); + descP->state = SOCKET_STATE_CONNECTING; + if ((sres = esock_select_write(env, descP->sock, descP, NULL, ref)) < 0) { + res = esock_make_error(env, + MKT2(env, + esock_atom_select_failed, + MKI(env, sres))); + } else { + res = esock_make_ok2(env, ref); + } + } else if (code == 0) { /* ok we are connected */ + + descP->state = SOCKET_STATE_CONNECTED; + descP->isReadable = TRUE; + descP->isWritable = TRUE; + + res = esock_atom_ok; + } else { + res = esock_make_error_errno(env, save_errno); + } + + return res; + +} +#endif // if !defined(__WIN32__) + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + 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(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) +{ + int error; + + if (descP->state != SOCKET_STATE_CONNECTING) + return esock_make_error(env, atom_enotconn); + + if (!verify_is_connected(descP, &error)) { + descP->state = SOCKET_STATE_OPEN; /* restore state */ + return esock_make_error_errno(env, error); + } + + descP->state = SOCKET_STATE_CONNECTED; + descP->isReadable = TRUE; + descP->isWritable = TRUE; + + 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) +{ + /* + * *** 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->remote); + sys_memzero((char *) &descP->remote, sz); + code = sock_peer(desc->sock, + (struct sockaddr*) &descP->remote, &sz); + + if (IS_SOCKET_ERROR(code)) { + *err = sock_errno(); + return FALSE; + } + +#else + + 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); + + if ((code < 0) || error) { + *err = error; + return FALSE; + } + +#endif /* SO_ERROR */ + + *err = 0; + + return TRUE; +} +#endif + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + int backlog; + + SGDBG( ("SOCKET", "nif_listen -> entry with argc: %d\r\n", argc) ); + + /* 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); + } + + 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); + +#endif // if defined(__WIN32__) +} + + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nlisten(ErlNifEnv* env, + SocketDescriptor* descP, + int backlog) +{ + + /* + * Verify that we are where in the proper state + */ + + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + + if (descP->state == SOCKET_STATE_CLOSED) + return esock_make_error(env, atom_exbadstate); + + if (!IS_OPEN(descP)) + return esock_make_error(env, atom_exbadstate); + + + /* + * And attempt to make socket listening + */ + + if (IS_SOCKET_ERROR(sock_listen(descP->sock, backlog))) + return esock_make_error_errno(env, sock_errno()); + + descP->state = SOCKET_STATE_LISTENING; + + return esock_atom_ok; + +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + ERL_NIF_TERM sockRef, ref, res; + + SGDBG( ("SOCKET", "nif_accept -> entry with argc: %d\r\n", argc) ); + + /* Extract arguments and perform preliminary validation */ + + sockRef = argv[0]; + if ((argc != 2) || + !enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + 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) ); + + MLOCK(descP->accMtx); + + res = naccept(env, descP, sockRef, ref); + + MUNLOCK(descP->accMtx); + + return res; + +#endif // if defined(__WIN32__) +} + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM naccept(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref) +{ + ERL_NIF_TERM res; + + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + + switch (descP->state) { + case SOCKET_STATE_LISTENING: + res = naccept_listening(env, descP, ref); + break; + + case SOCKET_STATE_ACCEPTING: + res = naccept_accepting(env, descP, sockRef, ref); + break; + + default: + res = esock_make_error(env, esock_atom_einval); + break; + } + + return res; +} +#endif // if !defined(__WIN32__) + + +/* *** 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, + ERL_NIF_TERM ref) +{ + SocketAddress remote; + unsigned int n; + SOCKET accSock; + int save_errno; + ErlNifPid caller; + ERL_NIF_TERM res; + + 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) ); + + res = naccept_listening_error(env, descP, ref, caller, save_errno); + + } else { + + /* + * We got one + */ + + SSDBG( descP, ("SOCKET", "naccept_listening -> success\r\n") ); + + res = naccept_listening_accept(env, descP, accSock, caller, &remote); + + } + + return res; +} + + +/* *** naccept_listening_error *** + * The accept call resultet in an error - handle it. + * There are only two cases: + * 1) BLOCK => Attempt a "retry" + * 2) Other => Return the value (converted to an atom) + */ +static +ERL_NIF_TERM naccept_listening_error(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ErlNifPid caller, + int save_errno) +{ + ERL_NIF_TERM res; + + if (save_errno == ERRNO_BLOCK) { + + /* *** Try again later *** */ + SSDBG( descP, ("SOCKET", "naccept_listening_error -> would block\r\n") ); + + descP->currentAcceptor.pid = caller; + if (MONP("naccept_listening -> current acceptor", + env, descP, + &descP->currentAcceptor.pid, + &descP->currentAcceptor.mon) != 0) + return esock_make_error(env, atom_exmon); + + descP->currentAcceptor.ref = enif_make_copy(descP->env, ref); + descP->currentAcceptorP = &descP->currentAcceptor; + + res = naccept_busy_retry(env, descP, ref, NULL, SOCKET_STATE_ACCEPTING); + + + } else { + SSDBG( descP, + ("SOCKET", + "naccept_listening -> errno: %d\r\n", save_errno) ); + res = esock_make_error_errno(env, save_errno); + } + + return res; +} + + +/* *** naccept_listening_accept *** + * The accept call was successful (accepted) - handle the new connection. + */ +static +ERL_NIF_TERM naccept_listening_accept(ErlNifEnv* env, + SocketDescriptor* descP, + SOCKET accSock, + ErlNifPid caller, + SocketAddress* remote) +{ + ERL_NIF_TERM res; + + naccept_accepted(env, descP, accSock, caller, remote, &res); + + return res; +} +#endif // if !defined(__WIN32__) + + + +/* *** naccept_accepting *** + * We have an active acceptor and possibly acceptors waiting in queue. + * If the pid of the calling process is not the pid of the "current process", + * push the requester onto the (acceptor) queue. + */ +#if !defined(__WIN32__) +static +ERL_NIF_TERM naccept_accepting(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM ref) +{ + ErlNifPid caller; + ERL_NIF_TERM res; + + SSDBG( descP, ("SOCKET", "naccept_accepting -> get caller\r\n") ); + + if (enif_self(env, &caller) == NULL) + return esock_make_error(env, atom_exself); + + 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(&descP->currentAcceptor.pid, &caller) == 0) { + + SSDBG( descP, + ("SOCKET", "naccept_accepting -> current acceptor\r\n") ); + + res = naccept_accepting_current(env, descP, sockRef, ref); + + } else { + + /* Not the "current acceptor", so (maybe) push onto queue */ + + SSDBG( descP, + ("SOCKET", "naccept_accepting -> *not* current acceptor\r\n") ); + + res = naccept_accepting_other(env, descP, ref, caller); + + } + + return res; + +} + + + +/* *** naccept_accepting_current *** + * Handles when the current acceptor makes another attempt. + */ +static +ERL_NIF_TERM naccept_accepting_current(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef) +{ + SocketAddress remote; + unsigned int n; + SOCKET accSock; + int save_errno; + ERL_NIF_TERM res; + + SSDBG( descP, ("SOCKET", "naccept_accepting_current -> try accept\r\n") ); + n = sizeof(descP->remote); + sys_memzero((char *) &remote, n); + accSock = sock_accept(descP->sock, (struct sockaddr*) &remote, &n); + if (accSock == INVALID_SOCKET) { + + save_errno = sock_errno(); + + SSDBG( descP, + ("SOCKET", + "naccept_accepting_current -> accept failed: %d\r\n", + save_errno) ); + + res = naccept_accepting_current_error(env, descP, sockRef, + accRef, save_errno); + + } else { + + SSDBG( descP, ("SOCKET", "naccept_accepting_current -> accepted\r\n") ); + + res = naccept_accepting_current_accept(env, descP, sockRef, + accSock, &remote); + + } + + return res; +} + + +/* *** naccept_accepting_current_accept *** + * Handles when the current acceptor succeeded in its accept call - + * handle the new connection. + */ +static +ERL_NIF_TERM naccept_accepting_current_accept(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + SOCKET accSock, + SocketAddress* remote) +{ + ERL_NIF_TERM res; + + if (naccept_accepted(env, descP, accSock, + descP->currentAcceptor.pid, remote, &res)) { + + /* We should really go through the queue until we succeed to activate + * a waiting acceptor. For now we just pop once and hope for the best... + * This will leave any remaining acceptors *hanging*... + * + * We need a "activate-next" function. + * + */ + + if (!activate_next_acceptor(env, descP, sockRef)) { + + SSDBG( descP, + ("SOCKET", + "naccept_accepting_current_accept -> " + "no more writers\r\n") ); + + descP->state = SOCKET_STATE_LISTENING; + + descP->currentAcceptorP = NULL; + descP->currentAcceptor.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentAcceptor.pid); + esock_monitor_init(&descP->currentAcceptor.mon); + } + + } + + return res; +} + + +/* *** naccept_accepting_current_error *** + * The accept call of current acceptor resultet in an error - handle it. + * There are only two cases: + * 1) BLOCK => Attempt a "retry" + * 2) Other => Return the value (converted to an atom) + */ +static +ERL_NIF_TERM naccept_accepting_current_error(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef, + int save_errno) +{ + SocketRequestor req; + ERL_NIF_TERM res, reason; + + if (save_errno == ERRNO_BLOCK) { + + /* + * Just try again, no real error, just a ghost trigger from poll, + */ + + SSDBG( descP, + ("SOCKET", + "naccept_accepting_current_error -> " + "would block: try again\r\n") ); + + res = naccept_busy_retry(env, descP, opRef, &descP->currentAcceptor.pid, + /* No state change */ + descP->state); + + } else { + + reason = MKA(env, erl_errno_id(save_errno)); + res = esock_make_error(env, reason); + + while (acceptor_pop(env, descP, &req)) { + SSDBG( descP, + ("SOCKET", "naccept_accepting_current_error -> abort %T\r\n", + req.pid) ); + esock_send_abort_msg(env, sockRef, req.ref, reason, &req.pid); + DEMONP("naccept_accepting_current_error -> pop'ed writer", + env, descP, &req.mon); + } + + } + + return res; +} + + +/* *** naccept_accepting_other *** + * Handles when the another acceptor makes an attempt, which + * results (maybe) in the request beeing pushed onto the + * acceptor queue. + */ +static +ERL_NIF_TERM naccept_accepting_other(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ErlNifPid caller) +{ + ERL_NIF_TERM result; + + if (!acceptor_search4pid(env, descP, &caller)) // Ugh! (&caller) + result = acceptor_push(env, descP, caller, ref); + else + result = esock_make_error(env, esock_atom_eagain); + + return result; +} +#endif // if !defined(__WIN32__) + + + +/* *** naccept_busy_retry *** + * Perform a retry select. If successful, set nextState. + */ +#if !defined(__WIN32__) +static +ERL_NIF_TERM naccept_busy_retry(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM ref, + ErlNifPid* pid, + unsigned int nextState) +{ + int sres; + ERL_NIF_TERM res, reason; + + if ((sres = esock_select_read(env, descP->sock, descP, pid, ref)) < 0) { + reason = MKT2(env, esock_atom_select_failed, MKI(env, sres)); + res = esock_make_error(env, reason); + } else { + descP->state = nextState; + res = esock_make_error(env, esock_atom_eagain); + } + + return res; +} + + + +/* *** naccept_accepted *** + * Generic function handling a successful accept. + */ +static +BOOLEAN_T naccept_accepted(ErlNifEnv* env, + SocketDescriptor* descP, + SOCKET accSock, + ErlNifPid pid, + SocketAddress* remote, + ERL_NIF_TERM* result) +{ + SocketDescriptor* accDescP; + HANDLE accEvent; + ERL_NIF_TERM accRef; + int save_errno; + + /* + * We got one + */ + + if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) { + save_errno = sock_errno(); + while ((sock_close(accSock) == INVALID_SOCKET) && + (sock_errno() == EINTR)); + *result = esock_make_error_errno(env, save_errno); + return FALSE; + } + + if ((accDescP = alloc_descriptor(accSock, accEvent)) == NULL) { + sock_close(accSock); + *result = enif_make_badarg(env); + return FALSE; + } + + accDescP->domain = descP->domain; + 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 + + accRef = enif_make_resource(env, accDescP); + enif_release_resource(accDescP); + + accDescP->ctrlPid = pid; + if (MONP("naccept_accepted -> ctrl", + env, accDescP, + &accDescP->ctrlPid, + &accDescP->ctrlMon) != 0) { + sock_close(accSock); + *result = esock_make_error(env, atom_exmon); + return FALSE; + } + + accDescP->remote = *remote; + SET_NONBLOCKING(accDescP->sock); + + accDescP->state = SOCKET_STATE_CONNECTED; + accDescP->isReadable = TRUE; + accDescP->isWritable = TRUE; + + *result = esock_make_ok2(env, accRef); + + return TRUE; + +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + ERL_NIF_TERM sockRef, sendRef; + 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) || + !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); + } + + 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, sockRef, sendRef, sndData.size, eflags) ); + + if (!esendflags2sendflags(eflags, &flags)) + return enif_make_badarg(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, sockRef, sendRef, &sndData, flags); + + MUNLOCK(descP->writeMtx); + + return res; +#endif // if defined(__WIN32__) +} + + + +/* *** nsend *** + * + * 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). + */ +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsend(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* sndDataP, + int flags) +{ + 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... + */ + cnt_inc(&descP->writeTries, 1); + + 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, sndDataP->size, save_errno, + sockRef, sendRef); + +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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. + * Dest - Destination (socket) address. + * Flags - Send flags. + */ + +static +ERL_NIF_TERM nif_sendto(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + ERL_NIF_TERM sockRef, sendRef; + 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 != 5) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP) || + !GET_BIN(env, argv[2], &sndData) || + !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); + } + + 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, sockRef, sendRef, sndData.size, eSockAddr, eflags) ); + + 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) { + SSDBG( descP, ("SOCKET", "nif_sendto -> sockaddr decode: %s\r\n", xres) ); + return esock_make_error_str(env, xres); + } + + MLOCK(descP->writeMtx); + + res = nsendto(env, descP, sockRef, sendRef, &sndData, flags, + &remoteAddr, remoteAddrLen); + + MUNLOCK(descP->writeMtx); + + SGDBG( ("SOCKET", "nif_sendto -> done with result: " + "\r\n %T" + "\r\n", res) ); + + return res; +#endif // if defined(__WIN32__) +} + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsendto(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* dataP, + int flags, + SocketAddress* toAddrP, + unsigned int toAddrLen) +{ + 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... + */ + cnt_inc(&descP->writeTries, 1); + + if (toAddrP != NULL) { + written = sock_sendto(descP->sock, + dataP->data, dataP->size, flags, + &toAddrP->sa, toAddrLen); + } else { + written = sock_sendto(descP->sock, + 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, save_errno, + sockRef, sendRef); +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + ERL_NIF_TERM res, sockRef, 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) || + !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); + } + + 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) ); + + if (!esendflags2sendflags(eflags, &flags)) + return esock_make_error(env, esock_atom_einval); + + MLOCK(descP->writeMtx); + + res = nsendmsg(env, descP, sockRef, sendRef, eMsgHdr, flags); + + MUNLOCK(descP->writeMtx); + + SSDBG( descP, + ("SOCKET", "nif_sendmsg -> done with result: " + "\r\n %T" + "\r\n", res) ); + + return res; +#endif // if defined(__WIN32__) +} + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsendmsg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + 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; + char* ctrlBuf; + 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. + */ + if (IS_CONNECTED(descP)) { + + /* We don't need the address */ + + SSDBG( descP, ("SOCKET", "nsendmsg -> connected: no address\r\n") ); + + 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); + + SSDBG( descP, ("SOCKET", "nsendmsg -> not connected: " + "\r\n address: %T" + "\r\n", eAddr) ); + + 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); + + SSDBG( descP, ("SOCKET", "nsendmsg -> iov length: %d\r\n", iovLen) ); + + 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 = (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) { + FREE(iovBins); + FREE(iov); + if (ctrlBuf != NULL) FREE(ctrlBuf); + return esock_make_error_str(env, xres); + } + msgHdr.msg_iov = iov; + 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, + eCtrl, + ctrlBuf, ctrlBufLen, &ctrlBufUsed)) != NULL) { + FREE(iovBins); + FREE(iov); + if (ctrlBuf != NULL) FREE(ctrlBuf); + return esock_make_error_str(env, xres); + } + } else { + ctrlBufUsed = 0; + } + msgHdr.msg_control = ctrlBuf; + msgHdr.msg_controllen = ctrlBufUsed; + + + /* 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; // OK or not complete: this value should not matter in this case + + res = send_check_result(env, descP, written, dataSize, save_errno, + sockRef, sendRef); + + FREE(iovBins); + FREE(iov); + if (ctrlBuf != NULL) FREE(ctrlBuf); + + return res; +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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, + 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 + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + ERL_NIF_TERM sockRef, recvRef; + int len; + unsigned int eflags; + int flags; + ERL_NIF_TERM res; + + if ((argc != 4) || + !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 case we send abort + recvRef = argv[1]; + + if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + + 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, sockRef, recvRef, len, flags); + + MUNLOCK(descP->readMtx); + + return res; +#endif // if defined(__WIN32__) +} + + +/* 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... + */ +#if !defined(__WIN32__) +static +ERL_NIF_TERM nrecv(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + int len, + int flags) +{ + ssize_t read; + ErlNifBinary buf; + ERL_NIF_TERM readerCheck; + int save_errno; + int bufSz = (len ? len : descP->rBufSz); + + SSDBG( descP, ("SOCKET", "nrecv -> entry with" + "\r\n len: %d (%d:%d)" + "\r\n flags: %d" + "\r\n", len, descP->rNumCnt, bufSz, flags) ); + + 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). + */ + if (!ALLOC_BIN(bufSz, &buf)) + 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); + + // 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, + sockRef, + recvRef); +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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. + * + * <KOLLA> + * + * How do we handle if the peek flag is set? We need to basically keep + * track of if we expect any data from the read. Regardless of the + * number of bytes we try to read. + * + * </KOLLA> + */ + +static +ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + ERL_NIF_TERM sockRef, recvRef; + unsigned int bufSz; + unsigned int eflags; + 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) || + !GET_UINT(env, argv[2], &bufSz) || + !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); + } + + 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); */ + + if (!erecvflags2recvflags(eflags, &flags)) { + SSDBG( descP, ("SOCKET", "nif_recvfrom -> recvflags decode failed\r\n") ); + return enif_make_badarg(env); + } + + MLOCK(descP->readMtx); + + /* <KOLLA> + * 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. + * </KOLLA> + */ + + res = nrecvfrom(env, descP, sockRef, recvRef, bufSz, flags); + + MUNLOCK(descP->readMtx); + + return res; +#endif // if defined(__WIN32__) +} + + +/* 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... + */ +#if !defined(__WIN32__) +static +ERL_NIF_TERM nrecvfrom(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + Uint16 len, + int flags) +{ + SocketAddress fromAddr; + unsigned int addrLen; + ssize_t read; + int save_errno; + ErlNifBinary buf; + ERL_NIF_TERM readerCheck; + 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); + + /* 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). + */ + if (!ALLOC_BIN(bufSz, &buf)) + 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(fromAddr); + sys_memzero((char*) &fromAddr, addrLen); + + 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, + sockRef, + recvRef); +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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. + * + * <KOLLA> + * + * How do we handle if the peek flag is set? We need to basically keep + * track of if we expect any data from the read. Regardless of the + * number of bytes we try to read. + * + * </KOLLA> + */ + +static +ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + ERL_NIF_TERM sockRef, 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 != 5) || + !GET_UINT(env, argv[2], &bufSz) || + !GET_UINT(env, argv[3], &ctrlSz) || + !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 + recvRef = argv[1]; + + if (!enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + + 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); + + /* <KOLLA> + * + * 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. + * + * </KOLLA> + */ + + res = nrecvmsg(env, descP, sockRef, recvRef, bufSz, ctrlSz, flags); + + MUNLOCK(descP->readMtx); + + return res; +#endif // if defined(__WIN32__) +} + + +/* 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... + */ +#if !defined(__WIN32__) +static +ERL_NIF_TERM nrecvmsg(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + Uint16 bufLen, + Uint16 ctrlLen, + int flags) +{ + unsigned int addrLen; + ssize_t read; + int save_errno; + int bufSz = (bufLen ? bufLen : descP->rBufSz); + int ctrlSz = (ctrlLen ? ctrlLen : descP->rCtrlSz); + struct msghdr msgHdr; + 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" + "\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); + + /* 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])) + 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, &data[0])) + 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 = data[0].data; + iov[0].iov_len = data[0].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, + data, // Needed for iov encode + &ctrl, // Needed for ctrl header encode + sockRef, + recvRef); +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + + if ((argc != 1) || + !enif_get_resource(env, argv[0], sockets, (void**) &descP)) { + return enif_make_badarg(env); + } + + if (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + + return nclose(env, descP); +#endif // if defined(__WIN32__) +} + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nclose(ErlNifEnv* env, + SocketDescriptor* descP) +{ + 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 (0x%lX, 0x%lX, 0x%lX, 0x%lX)\r\n", + descP->sock, + descP->state, + descP->currentWriterP, + descP->currentReaderP, + descP->currentAcceptorP) ); + + 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 esock_make_error(env, atom_exself); + } + + /* Monitor the caller, since we should complete this operation even if + * the caller dies (for whatever reason). + * + * <KOLLA> + * + * Can we actiually use this for anything? + * + * </KOLLA> + */ + + if (MONP("nclose -> closer", + env, descP, + &descP->closerPid, + &descP->closerMon) != 0) { + MUNLOCK(descP->closeMtx); + return esock_make_error(env, atom_exmon); + } + + descP->closeLocal = TRUE; + descP->state = SOCKET_STATE_CLOSING; + descP->isReadable = FALSE; + descP->isWritable = FALSE; + doClose = TRUE; + } + + if (doClose) { + descP->closeEnv = enif_alloc_env(); + descP->closeRef = MKREF(descP->closeEnv); + selectRes = esock_select_stop(env, descP->sock, descP); + if (selectRes & ERL_NIF_SELECT_STOP_CALLED) { + /* Prep done - inform the caller it can finalize (close) directly */ + SSDBG( descP, + ("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 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) ); + + /* <KOLLA> + * + * 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? + * + * </KOLLA> + */ + + // 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); + } + + } else { + reply = esock_make_error(env, reason); + } + + MUNLOCK(descP->closeMtx); + + SSDBG( descP, + ("SOCKET", "nclose -> [%d] done when: " + "\r\n state: 0x%lX" + "\r\n reply: %T" + "\r\n", descP->sock, descP->state, reply) ); + + return reply; +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * nif_finalize_close + * + * Description: + * Perform the actual socket close! + * Note that this function is executed in a dirty 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + 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); +#endif // if defined(__WIN32__) +} + + +/* *** nfinalize_close *** + * Perform the final step in the socket close. + */ +#if !defined(__WIN32__) +static +ERL_NIF_TERM nfinalize_close(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM reply; + + if (IS_CLOSED(descP)) + return esock_atom_ok; + + if (!IS_CLOSING(descP)) + 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 + * 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 = esock_make_error(env, atom_timeout); + } else { + reply = esock_make_error_errno(env, save_errno); + } + } else { + reply = esock_atom_ok; + } + sock_close_event(descP->event); + + descP->sock = INVALID_SOCKET; + descP->event = INVALID_EVENT; + + descP->state = SOCKET_STATE_CLOSED; + + return reply; +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * nif_shutdown + * + * Description: + * Disable sends and/or receives on a socket. + * + * Arguments: + * [0] Socket (ref) - Points to the socket descriptor. + * [1] How - What will be shutdown. + */ + +static +ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + 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 (IS_CLOSED(descP) || IS_CLOSING(descP)) + return esock_make_error(env, atom_closed); + + if (!ehow2how(ehow, &how)) + return enif_make_badarg(env); + + return nshutdown(env, descP, how); +#endif // if defined(__WIN32__) +} + + + +#if !defined(__WIN32__) +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 = esock_atom_ok; + } else { + reply = esock_make_error_errno(env, sock_errno()); + } + + return reply; +} +#endif // if !defined(__WIN32__) + + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + 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) ); + + /* Extract arguments and perform preliminary validation */ + + if ((argc != 5) || + !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]; + 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", */ + /* 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: %d (%T)" + "\r\n Level: %d (%d)" + "\r\n Opt: %d" + "\r\n Value: %T" + "\r\n", + descP->sock, argv[0], + isEncoded, eIsEncoded, + level, eLevel, + 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; +#endif // if defined(__WIN32__) +} + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM nsetopt(ErlNifEnv* env, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + int eOpt, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + + 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 { + 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 eOpt, + ERL_NIF_TERM eVal) +{ + 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); + break; + + case SOCKET_OPT_OTP_IOW: + result = nsetopt_otp_iow(env, descP, eVal); + break; + + case SOCKET_OPT_OTP_CTRL_PROC: + 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; + } + + return result; +} + + +/* nsetopt_otp_debug - Handle the OTP (level) debug options + */ +static +ERL_NIF_TERM nsetopt_otp_debug(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + descP->dbg = esock_decode_bool(eVal); + + return esock_atom_ok; +} + + +/* nsetopt_otp_iow - Handle the OTP (level) iow options + */ +static +ERL_NIF_TERM nsetopt_otp_iow(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + descP->iow = esock_decode_bool(eVal); + + return esock_atom_ok; +} + + + +/* 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 caller, newCtrlPid; + // ErlNifMonitor newCtrlMon; + ESockMonitor newCtrlMon; + int xres; + + SSDBG( descP, + ("SOCKET", "nsetopt_otp_ctrl_proc -> entry with" + "\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(&descP->ctrlPid, &caller) != 0) { + 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); + } + + 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("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; + + SSDBG( descP, ("SOCKET", "nsetopt_otp_ctrl_proc -> done\r\n") ); + + return esock_atom_ok; +} + + + +/* 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) +{ + 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 (IS_NUM(env, eVal)) { + + /* This will have the effect that the buffer size will be + * reported as an integer (getopt). + */ + n = 0; + + 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; +} + + + +/* 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). + */ +static +ERL_NIF_TERM nsetopt_native(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal) +{ + ErlNifBinary val; + ERL_NIF_TERM result; + + SSDBG( descP, + ("SOCKET", "nsetopt_native -> entry with" + "\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, + val.data, val.size); + 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); + } + + SSDBG( descP, + ("SOCKET", "nsetopt_native -> done when" + "\r\n result: %T" + "\r\n", result) ); + + return result; +} + + + +/* nsetopt_level - A "proper" level (option) has been specified + */ +static +ERL_NIF_TERM nsetopt_level(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt, + ERL_NIF_TERM eVal) +{ + 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); + break; + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + 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 + + case IPPROTO_TCP: + result = nsetopt_lvl_tcp(env, descP, eOpt, eVal); + break; + + case IPPROTO_UDP: + result = nsetopt_lvl_udp(env, descP, eOpt, eVal); + break; + +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + result = nsetopt_lvl_sctp(env, descP, eOpt, eVal); + break; +#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; +} + + + +/* nsetopt_lvl_socket - Level *SOCKET* option + */ +static +ERL_NIF_TERM nsetopt_lvl_socket(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_socket -> entry with" + "\r\n opt: %d" + "\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); + 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); + break; +#endif + +#if defined(SO_KEEPALIVE) + case SOCKET_OPT_SOCK_KEEPALIVE: + result = nsetopt_lvl_sock_keepalive(env, descP, eVal); + break; +#endif + +#if defined(SO_LINGER) + case SOCKET_OPT_SOCK_LINGER: + result = nsetopt_lvl_sock_linger(env, descP, eVal); + 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_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); + break; +#endif + +#if defined(SO_RCVBUF) + case SOCKET_OPT_SOCK_RCVBUF: + result = nsetopt_lvl_sock_rcvbuf(env, descP, eVal); + 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); + break; +#endif + +#if defined(SO_REUSEADDR) + case SOCKET_OPT_SOCK_REUSEADDR: + result = nsetopt_lvl_sock_reuseaddr(env, descP, eVal); + 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); + 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); + break; +#endif + +#if defined(SO_TIMESTAMP) + case SOCKET_OPT_SOCK_TIMESTAMP: + result = nsetopt_lvl_sock_timestamp(env, descP, eVal); + break; +#endif + + default: + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_socket -> unknown opt (%d)\r\n", eOpt) ); + 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; +} + + +#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, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST, eVal); +} +#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, + 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 = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + } else { + result = esock_make_error(env, esock_atom_einval); + } + + return result; +} +#endif + + +#if defined(SO_OOBINLINE) +static +ERL_NIF_TERM nsetopt_lvl_sock_oobinline(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_OOBINLINE, eVal); +} +#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_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_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, + 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, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR, eVal); +} +#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, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF, eVal); +} +#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, + 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 + + +#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) + */ +static +ERL_NIF_TERM nsetopt_lvl_ip(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) +{ + 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: + result = nsetopt_lvl_ip_add_membership(env, descP, eVal); + 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_FREEBIND) + case SOCKET_OPT_IP_FREEBIND: + result = nsetopt_lvl_ip_freebind(env, descP, eVal); + 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); + break; +#endif + +#if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) + 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); + 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); + break; +#endif + +#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_MULTICAST_TTL) + case SOCKET_OPT_IP_MULTICAST_TTL: + result = nsetopt_lvl_ip_multicast_ttl(env, descP, eVal); + break; +#endif + +#if defined(IP_NODEFRAG) + case SOCKET_OPT_IP_NODEFRAG: + result = nsetopt_lvl_ip_nodefrag(env, descP, eVal); + break; +#endif + +#if defined(IP_PKTINFO) + case SOCKET_OPT_IP_PKTINFO: + result = nsetopt_lvl_ip_pktinfo(env, descP, eVal); + 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); + break; +#endif + +#if defined(IP_RECVIF) + case SOCKET_OPT_IP_RECVIF: + result = nsetopt_lvl_ip_recvif(env, descP, eVal); + break; +#endif + +#if defined(IP_RECVOPTS) + case SOCKET_OPT_IP_RECVOPTS: + result = nsetopt_lvl_ip_recvopts(env, descP, eVal); + 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); + break; +#endif + +#if defined(IP_RECVTTL) + case SOCKET_OPT_IP_RECVTTL: + result = nsetopt_lvl_ip_recvttl(env, descP, eVal); + 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); + 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); + 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); + 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: + 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; +} + + +/* 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_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. + * 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 + + + +/* 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_drop_source_membership(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ip_update_source(env, descP, eVal, + IP_DROP_SOURCE_MEMBERSHIP); +} +#endif + + + +/* 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_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) +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_msfilter - Level IP MSFILTER option + * + * The value can be *either* the atom 'null' or a map of type 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) +{ + 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 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* 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(). + */ +#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_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. + */ +#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) +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_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_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_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_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) +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) +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_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_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) +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_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_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) +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_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) +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 = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + } else { + result = esock_make_error(env, esock_atom_einval); + } + + return result; +} +#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) +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_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(HAVE_IPV6) +static +ERL_NIF_TERM nsetopt_lvl_ipv6(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_ipv6 -> entry with" + "\r\n opt: %d" + "\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); + 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); + break; +#endif + +#if defined(IPV6_DSTOPTS) + case SOCKET_OPT_IPV6_DSTOPTS: + result = nsetopt_lvl_ipv6_dstopts(env, descP, eVal); + 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); + 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); + 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_MULTICAST_HOPS) + case SOCKET_OPT_IPV6_MULTICAST_HOPS: + result = nsetopt_lvl_ipv6_multicast_hops(env, descP, eVal); + 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_MULTICAST_LOOP) + case SOCKET_OPT_IPV6_MULTICAST_LOOP: + result = nsetopt_lvl_ipv6_multicast_loop(env, descP, eVal); + 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); + 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); + 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); + break; +#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; +} + + +#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, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ipv6_update_membership(env, descP, eVal, + IPV6_ADD_MEMBERSHIP); +} +#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, + SocketDescriptor* descP, + ERL_NIF_TERM eVal) +{ + return nsetopt_lvl_ipv6_update_membership(env, descP, eVal, + IPV6_DROP_MEMBERSHIP); +} +#endif + + +#if defined(IPV6_DSTOPTS) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + +#if defined(IPV6_FLOWINFO) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + +#if defined(IPV6_HOPLIMIT) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + +#if defined(IPV6_HOPOPTS) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + +#if defined(IPV6_MTU) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_mtu(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + +/* 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 { +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + + res = socket_setopt(descP->sock, level, 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_MULTICAST_HOPS) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + + +#if defined(IPV6_MULTICAST_IF) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + + +#if defined(IPV6_MULTICAST_LOOP) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + +#if defined(IPV6_RECVERR) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_recverr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + +#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(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, level, opt, eVal); +} +#endif + + +#if defined(IPV6_ROUTER_ALERT) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + + +#if defined(IPV6_RTHDR) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + +#if defined(IPV6_UNICAST_HOPS) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + + +#if defined(IPV6_V6ONLY) +static +ERL_NIF_TERM nsetopt_lvl_ipv6_v6only(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM 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 + + +#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; +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#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_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(HAVE_IPV6) + + + +/* nsetopt_lvl_tcp - Level *TCP* option(s) + */ +static +ERL_NIF_TERM nsetopt_lvl_tcp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) +{ + 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: + result = nsetopt_lvl_tcp_congestion(env, descP, eVal); + break; +#endif + +#if defined(TCP_MAXSEG) + case SOCKET_OPT_TCP_MAXSEG: + result = nsetopt_lvl_tcp_maxseg(env, descP, eVal); + break; +#endif + +#if defined(TCP_NODELAY) + case SOCKET_OPT_TCP_NODELAY: + result = nsetopt_lvl_tcp_nodelay(env, descP, eVal); + break; +#endif + + default: + result = esock_make_error(env, esock_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 + + +/* 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 +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; + + 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: + result = nsetopt_lvl_udp_cork(env, descP, eVal); + break; +#endif + + default: + result = esock_make_error(env, esock_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 + + + + +/* nsetopt_lvl_sctp - Level *SCTP* option(s) + */ +#if defined(HAVE_SCTP) +static +ERL_NIF_TERM nsetopt_lvl_sctp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal) +{ + ERL_NIF_TERM result; + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp -> entry with" + "\r\n opt: %d" + "\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); + 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); + 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); + break; +#endif + +#if defined(SCTP_NODELAY) + case SOCKET_OPT_SCTP_NODELAY: + result = nsetopt_lvl_sctp_nodelay(env, descP, eVal); + 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; + } + + return result; +} + + +/* 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_associnfo -> decode attributes\r\n") ); + + /* On some platforms the assoc id is typed as an unsigned integer (uint32) + * So, to avoid warnings there, we always make an explicit cast... + * Also, size of types matter, so adjust for that... + */ + +#if (SIZEOF_INT == 4) + { + int tmpAssocId; + if (!GET_INT(env, eAssocId, &tmpAssocId)) + return esock_make_error(env, esock_atom_einval); + assocParams.sasoc_assoc_id = + (typeof(assocParams.sasoc_assoc_id)) tmpAssocId; + } +#elif (SIZEOF_LONG == 4) + { + long tmpAssocId; + if (!GET_LONG(env, eAssocId, &tmpAssocId)) + return esock_make_error(env, esock_atom_einval); + assocParams.sasoc_assoc_id = + (typeof(assocParams.sasoc_assoc_id)) tmpAssocId; + } +#else + SIZE CHECK FOR ASSOC ID FAILED +#endif + + + /* + * We should really make sure this is ok in erlang (to ensure that + * 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, eMaxRxt, &tmp)) + return esock_make_error(env, esock_atom_einval); + 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) tmp; + + if (!GET_UINT(env, ePeerRWND, &assocParams.sasoc_peer_rwnd)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_UINT(env, eLocalRWND, &assocParams.sasoc_local_rwnd)) + return esock_make_error(env, esock_atom_einval); + + if (!GET_UINT(env, eCookieLife, &assocParams.sasoc_cookie_life)) + return esock_make_error(env, esock_atom_einval); + + SSDBG( descP, + ("SOCKET", "nsetopt_lvl_sctp_associnfo -> 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) +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_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) +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; +#if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_AUTHENTICATION_EVENT) + ERL_NIF_TERM eAuth; +#endif +#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; + + 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 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)) + return esock_make_error(env, esock_atom_einval); +#endif + + 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); +#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 + + 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_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) tmp; + + if (!GET_UINT(env, eMaxIn, &tmp)) + return esock_make_error(env, esock_atom_einval); + 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) tmp; + + if (!GET_UINT(env, eMaxInitTO, &tmp)) + return esock_make_error(env, esock_atom_einval); + initMsg.sinit_max_init_timeo = (Uint16) 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) +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) +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 + + +/* 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") ); + + /* On some platforms the assoc id is typed as an unsigned integer (uint32) + * So, to avoid warnings there, we always make an explicit cast... + * Also, size of types matter, so adjust for that... + */ + +#if (SIZEOF_INT == 4) + { + int tmpAssocId; + if (!GET_INT(env, eAssocId, &tmpAssocId)) + return esock_make_error(env, esock_atom_einval); + rtoInfo.srto_assoc_id = (typeof(rtoInfo.srto_assoc_id)) tmpAssocId; + } +#elif (SIZEOF_LONG == 4) + { + long tmpAssocId; + if (!GET_LONG(env, eAssocId, &tmpAssocId)) + return esock_make_error(env, esock_atom_einval); + rtoInfo.srto_assoc_id = (typeof(rtoInfo.srto_assoc_id)) tmpAssocId; + } +#else + SIZE CHECK FOR ASSOC ID FAILED +#endif + /* + if (!GET_INT(env, eAssocId, &tmpAssocId)) + return esock_make_error(env, esock_atom_einval); + rtoInfo.srto_assoc_id = (typeof(rtoInfo.srto_assoc_id)) tmpAssocId; + */ + + 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) + + + + +/* 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; + int ival, res; + + val = esock_decode_bool(eVal); + + ival = (val) ? 1 : 0; + res = socket_setopt(descP->sock, level, opt, &ival, sizeof(ival)); + + if (res != 0) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + + 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; + + /* + 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()); + else + result = esock_atom_ok; + + } else { + result = esock_make_error(env, esock_atom_einval); + } + + return result; +} + + +/* nsetopt_str_opt - set an option that has an string value + */ +#if defined(USE_SETOPT_STR_OPT) +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; +} +#endif + + +/* 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; + struct timeval timeVal; + int res; + char* xres; + + SSDBG( descP, + ("SOCKET", "nsetopt_timeval_opt -> entry with" + "\r\n eVal: %T" + "\r\n", eVal) ); + + if ((xres = esock_decode_timeval(env, eVal, &timeVal)) != NULL) + return esock_make_error_str(env, xres); + + 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, + 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 = IPPROTO_IP; +#endif + result = TRUE; + break; + +#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 + + 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 +++ + * + * <Per H @ Tail-f> + * 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. + * </Per H @ Tail-f> + * + * <PaN> + * 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. + * </PaN> + */ +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; + SOCKOPTLEN_T tmpArgSzPRIO = sizeof(tmpIValPRIO); + SOCKOPTLEN_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; +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * nif_getopt + * + * Description: + * 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: + * 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_getopt(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + int eLevel, level = -1; + 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)) { + 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} + + 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" + "\r\n eIsEncoded: %T" + "\r\n eLevel: %d" + "\r\n eOpt: %T" + "\r\n", descP->sock, argv[0], eIsEncoded, eLevel, eOpt) ); + + isEncoded = esock_decode_bool(eIsEncoded); + + if (!elevel2level(isEncoded, eLevel, &isOTP, &level)) + return esock_make_error(env, esock_atom_einval); + + return ngetopt(env, descP, isEncoded, isOTP, level, eOpt); +#endif // if defined(__WIN32__) +} + + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM ngetopt(ErlNifEnv* env, + SocketDescriptor* descP, + BOOLEAN_T isEncoded, + BOOLEAN_T isOTP, + int level, + ERL_NIF_TERM eOpt) +{ + ERL_NIF_TERM result; + int opt; + + SSDBG( descP, + ("SOCKET", "ngetopt -> entry with" + "\r\n isEncoded: %s" + "\r\n isOTP: %s" + "\r\n level: %d" + "\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. + */ + 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 { + if (GET_INT(env, eOpt, &opt)) + result = ngetopt_level(env, descP, level, opt); + else + result = esock_make_error(env, esock_atom_einval); + } + + SSDBG( descP, + ("SOCKET", "ngetopt -> done when" + "\r\n result: %T" + "\r\n", result) ); + + return result; +} + + + +/* ngetopt_otp - Handle OTP (level) options + */ +static +ERL_NIF_TERM ngetopt_otp(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + SSDBG( descP, + ("SOCKET", "ngetopt_otp -> entry with" + "\r\n eOpt: %d" + "\r\n", eOpt) ); + + 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; + + case SOCKET_OPT_OTP_CTRL_PROC: + 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; + + case SOCKET_OPT_OTP_FD: + result = ngetopt_otp_fd(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; + } + + SSDBG( descP, + ("SOCKET", "ngetopt_otp -> done when" + "\r\n result: %T" + "\r\n", result) ); + + return result; +} + + +/* ngetopt_otp_debug - Handle the OTP (level) debug option + */ +static +ERL_NIF_TERM ngetopt_otp_debug(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = esock_encode_bool(descP->dbg); + + return esock_make_ok2(env, eVal); +} + + +/* ngetopt_otp_iow - Handle the OTP (level) iow option + */ +static +ERL_NIF_TERM ngetopt_otp_iow(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM eVal = esock_encode_bool(descP->iow); + + return esock_make_ok2(env, eVal); +} + + +/* ngetopt_otp_ctrl_proc - Handle the OTP (level) controlling_process option + */ +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); +} + + + +/* ngetopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option + */ +static +ERL_NIF_TERM ngetopt_otp_rcvbuf(ErlNifEnv* env, + SocketDescriptor* descP) +{ + 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); +} + + +/* ngetopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option + */ +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 option + */ +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); +} + + +/* 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, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result, reason; + 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: + reason = MKT2(env, esock_atom_unknown, MKI(env, val)); + result = esock_make_error(env, reason); + 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, reason; + 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: + reason = MKT2(env, esock_atom_unknown, MKI(env, val)); + result = esock_make_error(env, reason); + 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, reason; + 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: + reason = MKT2(env, esock_atom_unknown, MKI(env, val)); + result = esock_make_error(env, reason); + 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()} + */ +static +ERL_NIF_TERM ngetopt_native(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + ERL_NIF_TERM eOpt) +{ + ERL_NIF_TERM result = enif_make_badarg(env); + int opt; + Uint16 valueType; + SOCKOPTLEN_T valueSz; + + SSDBG( descP, + ("SOCKET", "ngetopt_native -> entry with" + "\r\n level: %d" + "\r\n eOpt: %T" + "\r\n", level, eOpt) ); + + /* <KOLLA> + * We should really make it possible to specify more common specific types, + * such as integer or boolean (instead of the size)... + * </KOLLA> + */ + + 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); + break; + case SOCKET_OPT_VALUE_TYPE_INT: + result = ngetopt_int_opt(env, descP, level, opt); + break; + case SOCKET_OPT_VALUE_TYPE_BOOL: + result = ngetopt_bool_opt(env, descP, level, opt); + break; + default: + result = esock_make_error(env, esock_atom_einval); + break; + } + } else { + result = esock_make_error(env, esock_atom_einval); + } + + SSDBG( descP, + ("SOCKET", "ngetopt_native -> done when" + "\r\n result: %T" + "\r\n", result) ); + + return result; +} + + +static +ERL_NIF_TERM ngetopt_native_unspec(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int opt, + SOCKOPTLEN_T valueSz) +{ + ERL_NIF_TERM result = esock_make_error(env, esock_atom_einval); + 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) + result = esock_make_error_errno(env, sock_errno()); + else + result = esock_atom_ok; + } else { + SOCKOPTLEN_T vsz = valueSz; + ErlNifBinary val; + + 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) { + saveErrno = sock_errno(); + + result = esock_make_error_errno(env, saveErrno); + } else { + + /* 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 { + result = enif_make_badarg(env); + } + } + + SSDBG( descP, + ("SOCKET", "ngetopt_native_unspec -> done when" + "\r\n result: %T" + "\r\n", result) ); + + return result; +} + + + +/* ngetopt_level - A "proper" level (option) has been specified + */ +static +ERL_NIF_TERM ngetopt_level(ErlNifEnv* env, + SocketDescriptor* descP, + int level, + int eOpt) +{ + 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); + break; + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + 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 + + 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 = esock_make_error(env, esock_atom_einval); + break; + } + + SSDBG( descP, + ("SOCKET", "ngetopt_level -> done when" + "\r\n result: %T" + "\r\n", result) ); + + 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; + + 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: + result = ngetopt_lvl_sock_acceptconn(env, descP); + 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); + 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); + 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_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); + break; +#endif + +#if defined(SO_PRIORITY) + case SOCKET_OPT_SOCK_PRIORITY: + result = ngetopt_lvl_sock_priority(env, descP); + 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); + 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); + break; +#endif + +#if defined(SO_REUSEADDR) + case SOCKET_OPT_SOCK_REUSEADDR: + result = ngetopt_lvl_sock_reuseaddr(env, descP); + 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); + 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); + 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); + break; +#endif + + default: + result = esock_make_error(env, esock_atom_einval); + break; + } + + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_socket -> done when" + "\r\n result: %T" + "\r\n", result) ); + + return result; +} + + +#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_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, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_BROADCAST); +} +#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, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result, reason; + 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, sock_errno()); + } 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: + reason = MKT2(env, esock_atom_unknown, MKI(env, val)); + result = esock_make_error(env, reason); + break; + } + } + + return result; +} +#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 = 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); + ERL_NIF_TERM linger = MKT2(env, lOnOff, lSecs); + + result = esock_make_ok2(env, linger); + } + + return result; +} +#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, + 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, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_PRIORITY); +} +#endif + + +#if defined(SO_PROTOCOL) +static +ERL_NIF_TERM ngetopt_lvl_sock_protocol(ErlNifEnv* env, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result, reason; + 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, sock_errno()); + } 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: + reason = MKT2(env, esock_atom_unknown, MKI(env, val)); + result = esock_make_error(env, reason); + break; + } + } + + return result; +} +#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_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, + 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, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, SOL_SOCKET, SO_REUSEADDR); +} +#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, + SocketDescriptor* descP) +{ + return ngetopt_int_opt(env, descP, SOL_SOCKET, SO_SNDBUF); +} +#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, + SocketDescriptor* descP) +{ + return ngetopt_timeval_opt(env, descP, SOL_SOCKET, SO_SNDTIMEO); +} +#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, + SocketDescriptor* descP) +{ + ERL_NIF_TERM result, reason; + 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, sock_errno()); + } 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: + reason = MKT2(env, esock_atom_unknown, MKI(env, val)); + result = esock_make_error(env, reason); + break; + } + } + + return result; +} +#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; + + 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: + result = ngetopt_lvl_ip_freebind(env, descP); + 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); + break; +#endif + +#if defined(IP_MTU) + case SOCKET_OPT_IP_MTU: + result = ngetopt_lvl_ip_mtu(env, descP); + 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_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); + break; +#endif + +#if defined(IP_MULTICAST_LOOP) + case SOCKET_OPT_IP_MULTICAST_LOOP: + result = ngetopt_lvl_ip_multicast_loop(env, descP); + 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_NODEFRAG) + case SOCKET_OPT_IP_NODEFRAG: + result = ngetopt_lvl_ip_nodefrag(env, descP); + break; +#endif + +#if defined(IP_PKTINFO) + case SOCKET_OPT_IP_PKTINFO: + result = ngetopt_lvl_ip_pktinfo(env, descP); + 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); + break; +#endif + +#if defined(IP_RECVIF) + case SOCKET_OPT_IP_RECVIF: + result = ngetopt_lvl_ip_recvif(env, descP); + break; +#endif + +#if defined(IP_RECVOPTS) + case SOCKET_OPT_IP_RECVOPTS: + result = ngetopt_lvl_ip_recvopts(env, descP); + 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); + break; +#endif + +#if defined(IP_RECVTTL) + case SOCKET_OPT_IP_RECVTTL: + result = ngetopt_lvl_ip_recvttl(env, descP); + 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); + 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); + 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); + break; +#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; +} + + +/* 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_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_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) +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_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_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) +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) +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_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_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_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) +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_recvdstaddr - Level IP RECVDSTADDR option + */ +#if defined(IP_RECVDSTADDR) +static +ERL_NIF_TERM ngetopt_lvl_ip_recvdstaddr(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_RECVDSTADDR); +} +#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_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) +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_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) +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_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) +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_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) +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 = esock_make_error_errno(env, sock_errno()); + } else { + result = encode_ip_tos(env, val); + } + + return result; +} +#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) +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(HAVE_IPV6) +static +ERL_NIF_TERM ngetopt_lvl_ipv6(ErlNifEnv* env, + SocketDescriptor* descP, + int eOpt) +{ + ERL_NIF_TERM result; + + SSDBG( descP, + ("SOCKET", "ngetopt_lvl_ipv6 -> entry with" + "\r\n eOpt: %d" + "\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_DSTOPTS) + case SOCKET_OPT_IPV6_DSTOPTS: + result = ngetopt_lvl_ipv6_dstopts(env, descP); + 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); + 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); + 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_MULTICAST_HOPS) + case SOCKET_OPT_IPV6_MULTICAST_HOPS: + result = ngetopt_lvl_ipv6_multicast_hops(env, descP); + 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_MULTICAST_LOOP) + case SOCKET_OPT_IPV6_MULTICAST_LOOP: + result = ngetopt_lvl_ipv6_multicast_loop(env, descP); + 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); + 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); + 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); + 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; +} + + +#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_DSTOPTS) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_dstopts(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + return ngetopt_bool_opt(env, descP, level, IPV6_DSTOPTS); +} +#endif + + +#if defined(IPV6_FLOWINFO) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_flowinfo(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_FLOWINFO); +} +#endif + + +#if defined(IPV6_HOPLIMIT) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_hoplimit(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_HOPLIMIT); +} +#endif + + +#if defined(IPV6_HOPOPTS) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_hopopts(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_HOPOPTS); +} +#endif + + +#if defined(IPV6_MTU) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_mtu(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_int_opt(env, descP, level, IPV6_MTU); +} +#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; +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + res = sock_getopt(descP->sock, level, 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_MULTICAST_HOPS) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_hops(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#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 + + +#if defined(IPV6_MULTICAST_IF) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_if(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#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 + + +#if defined(IPV6_MULTICAST_LOOP) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_multicast_loop(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#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 + + +#if defined(IPV6_RECVERR) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_recverr(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_RECVERR); +} +#endif + + +#if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) +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; +#else + int opt = IPV6_PKTINFO; +#endif + + return ngetopt_bool_opt(env, descP, level, opt); +} +#endif + + +#if defined(IPV6_ROUTER_ALERT) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_router_alert(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#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 + + +#if defined(IPV6_RTHDR) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_rthdr(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_RTHDR); +} +#endif + + +#if defined(IPV6_UNICAST_HOPS) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_unicast_hops(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#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 + + +#if defined(IPV6_V6ONLY) +static +ERL_NIF_TERM ngetopt_lvl_ipv6_v6only(ErlNifEnv* env, + SocketDescriptor* descP) +{ +#if defined(SOL_IPV6) + int level = SOL_IPV6; +#else + int level = IPPROTO_IPV6; +#endif + + return ngetopt_bool_opt(env, descP, level, IPV6_V6ONLY); +} +#endif + + +#endif // defined(HAVE_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 = esock_make_error(env, esock_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 = esock_make_error(env, esock_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; + + 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); + 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_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); + break; +#endif + +#if defined(SCTP_NODELAY) + case SOCKET_OPT_SCTP_NODELAY: + result = ngetopt_lvl_sctp_nodelay(env, descP); + 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; + } + + 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 + * + * <KOLLA> + * + * 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? + * + * </KOLLA> + */ +#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) +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_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_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) +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) +static +ERL_NIF_TERM ngetopt_lvl_sctp_nodelay(ErlNifEnv* env, + SocketDescriptor* descP) +{ + return ngetopt_bool_opt(env, descP, IPPROTO_SCTP, SCTP_NODELAY); +} +#endif + + +/* ngetopt_lvl_sctp_associnfo - Level SCTP ASSOCINFO option + * + * <KOLLA> + * + * 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? + * + * </KOLLA> + */ +#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) + + + +/* ngetopt_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; + + /* + 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) { + result = esock_make_error_errno(env, sock_errno()); + } else { + ERL_NIF_TERM bval = ((val) ? atom_true : atom_false); + + 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; +} + + +/* ngetopt_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 = esock_make_error_errno(env, sock_errno()); + } else { + result = esock_make_ok2(env, MKI(env, val)); + } + + return result; +} + + + +/* 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; + 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, + ("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 + * size of the buffer we allocate for the value. + * The actual size of the (read) value will be communicated + * in the optSz variable. + */ +#if defined(USE_GETOPT_STR_OPT) +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; + + 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 { + 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; +} +#endif // if defined(USE_GETOPT_STR_OPT) +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + 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); + } + + 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" + "\r\n", descP->sock, argv[0]) ); + + res = nsockname(env, descP); + + SSDBG( descP, ("SOCKET", "nif_sockname -> done with res = %T\r\n", res) ); + + return res; +#endif // if defined(__WIN32__) +} + + + +#if !defined(__WIN32__) +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); + } +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + 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); + } + + 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" + "\r\n", descP->sock, argv[0]) ); + + res = npeername(env, descP); + + SSDBG( descP, ("SOCKET", "nif_peername -> done with res = %T\r\n", res) ); + + return res; +#endif // if defined(__WIN32__) +} + + + +#if !defined(__WIN32__) +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); + } +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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[]) +{ +#if defined(__WIN32__) + return enif_raise_exception(env, MKA(env, "notsup")); +#else + SocketDescriptor* descP; + ERL_NIF_TERM op, sockRef, opRef, result; + + SGDBG( ("SOCKET", "nif_cancel -> entry with argc: %d\r\n", argc) ); + + /* Extract arguments and perform preliminary validation */ + + sockRef = argv[0]; + if ((argc != 3) || + !enif_get_resource(env, sockRef, sockets, (void**) &descP)) { + return enif_make_badarg(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" + "\r\n opRef: %T" + "\r\n", descP->sock, op, opRef) ); + + result = ncancel(env, descP, op, sockRef, opRef); + + SSDBG( descP, + ("SOCKET", "nif_cancel -> done with result: " + "\r\n %T" + "\r\n", result) ); + + return result; +#endif // if !defined(__WIN32__) +} + + +#if !defined(__WIN32__) +static +ERL_NIF_TERM ncancel(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM op, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) +{ + /* <KOLLA> + * + * Do we really need all these variants? Should it not be enough with: + * + * connect | accept | send | recv + * + * </KOLLA> + */ + 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, sockRef, opRef); + } else if (COMPARE(op, esock_atom_send) == 0) { + return ncancel_send(env, descP, sockRef, opRef); + } else if (COMPARE(op, esock_atom_sendto) == 0) { + return ncancel_send(env, descP, sockRef, opRef); + } else if (COMPARE(op, esock_atom_sendmsg) == 0) { + return ncancel_send(env, descP, sockRef, opRef); + } else if (COMPARE(op, esock_atom_recv) == 0) { + return ncancel_recv(env, descP, sockRef, opRef); + } else if (COMPARE(op, esock_atom_recvfrom) == 0) { + return ncancel_recv(env, descP, sockRef, opRef); + } else if (COMPARE(op, esock_atom_recvmsg) == 0) { + return ncancel_recv(env, descP, sockRef, 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 sockRef, + 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, sockRef); + } 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 acceptor 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 sockRef) +{ + ERL_NIF_TERM res; + + 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) ); + + if (!activate_next_acceptor(env, descP, sockRef)) { + + SSDBG( descP, + ("SOCKET", "ncancel_accept_current -> no more writers\r\n") ); + + descP->state = SOCKET_STATE_LISTENING; + + descP->currentAcceptorP = NULL; + descP->currentAcceptor.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentAcceptor.pid); + esock_monitor_init(&descP->currentAcceptor.mon); + } + + 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 *** + * + * 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 sockRef, + ERL_NIF_TERM opRef) +{ + ERL_NIF_TERM res; + + MLOCK(descP->writeMtx); + + SSDBG( descP, + ("SOCKET", "ncancel_send -> entry with" + "\r\n opRef: %T" + "\r\n %s" + "\r\n", opRef, + ((descP->currentWriterP == NULL) ? "without writer" : "with writer")) ); + + if (descP->currentWriterP != NULL) { + if (COMPARE(opRef, descP->currentWriter.ref) == 0) { + res = ncancel_send_current(env, descP, sockRef); + } 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 sockRef) +{ + ERL_NIF_TERM res; + + SSDBG( descP, ("SOCKET", "ncancel_send_current -> entry\r\n") ); + + DEMONP("ncancel_send_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) ); + + if (!activate_next_writer(env, descP, sockRef)) { + SSDBG( descP, + ("SOCKET", "ncancel_send_current -> no more writers\r\n") ); + descP->currentWriterP = NULL; + descP->currentWriter.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentWriter.pid); + esock_monitor_init(&descP->currentWriter.mon); + } + + 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); + } +} + + + +/* *** 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 sockRef, + ERL_NIF_TERM opRef) +{ + 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, sockRef); + } 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 sockRef) +{ + ERL_NIF_TERM res; + + 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) ); + + if (!activate_next_reader(env, descP, sockRef)) { + SSDBG( descP, + ("SOCKET", "ncancel_recv_current -> no more readers\r\n") ); + descP->currentReaderP = NULL; + descP->currentReader.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentReader.pid); + esock_monitor_init(&descP->currentReader.mon); + } + + 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); + } +} + + + +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 = esock_select_cancel(env, descP->sock, smode, descP); + + 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); + } + +} +#endif // if !defined(__WIN32__) + + + + +/* ---------------------------------------------------------------------- + * U t i l i t y F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* *** 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. + */ +#if !defined(__WIN32__) +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(&descP->currentWriter.pid, &caller) != 0) { + /* 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", + "send_check_writer -> 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, + ssize_t written, + ssize_t dataSize, + int saveErrno, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef) +{ + int sres; + + 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); + if (descP->currentWriterP != NULL) + DEMONP("send_check_result -> current writer", + 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 (!activate_next_writer(env, descP, sockRef)) { + descP->currentWriterP = NULL; + descP->currentWriter.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentWriter.pid); + esock_monitor_init(&descP->currentWriter.mon); + } + + return esock_atom_ok; + + } else if (written < 0) { + + /* Some kind of send failure - check what kind */ + + if ((saveErrno != EAGAIN) && (saveErrno != EINTR)) { + SocketRequestor req; + ERL_NIF_TERM res, reason; + + /* + * 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) ); + + reason = MKA(env, erl_errno_id(saveErrno)); + res = esock_make_error(env, reason); + + if (descP->currentWriterP != NULL) { + + DEMONP("send_check_result -> current writer", + env, descP, &descP->currentWriter.mon); + + while (writer_pop(env, descP, &req)) { + SSDBG( descP, + ("SOCKET", "send_check_result -> abort %T\r\n", + req.pid) ); + esock_send_abort_msg(env, sockRef, req.ref, + reason, &req.pid); + DEMONP("send_check_result -> pop'ed writer", + env, descP, &req.mon); + } + } + + return res; + + } else { + /* Ok, try again later */ + + SSDBG( descP, ("SOCKET", "send_check_result -> try again\r\n") ); + } + + } + else { + SSDBG( descP, + ("SOCKET", "send_check_result -> " + "not entire package written (%d of %d)\r\n", written, dataSize) ); + } + + /* 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. + */ + + 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("send_check_result -> current writer", + 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); + + sres = esock_select_write(env, descP->sock, descP, NULL, sendRef); + + if (written >= 0) { + if (sres < 0) { + /* Returned: {error, Reason} + * Reason: {select_failed, sres, written} + */ + return esock_make_error(env, + MKT3(env, + esock_atom_select_failed, + MKI(env, sres), + MKI(env, written))); + } else { + return esock_make_ok2(env, MKI(env, written)); + } + } else { + if (sres < 0) { + /* Returned: {error, Reason} + * Reason: {select_failed, sres} + */ + return esock_make_error(env, + MKT2(env, + esock_atom_select_failed, + MKI(env, sres))); + } else { + return esock_make_error(env, esock_atom_eagain); + } + } +} + + + +/* *** 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(&descP->currentReader.pid, &caller) != 0) { + 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)) + tmp = reader_push(env, descP, caller, ref); + else + tmp = esock_make_error(env, esock_atom_eagain); + + SSDBG( descP, + ("SOCKET", + "recv_check_reader -> queue (push) result: %T\r\n", tmp) ); + + *checkResult = tmp; + + 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("recv_init_current_reader -> current reader", + 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, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM res = esock_atom_ok; + + if (descP->currentReaderP != NULL) { + + DEMONP("recv_update_current_reader -> current reader", + env, descP, &descP->currentReader.mon); + + if (!activate_next_reader(env, descP, sockRef)) { + + SSDBG( descP, + ("SOCKET", + "recv_update_current_reader -> no more readers\r\n") ); + + descP->currentReaderP = NULL; + descP->currentReader.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentReader.pid); + esock_monitor_init(&descP->currentReader.mon); + } + + } + + return res; +} + + + +/* *** 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 sockRef, + ERL_NIF_TERM reason) +{ + SocketRequestor req; + + if (descP->currentReaderP != NULL) { + + DEMONP("recv_error_current_reader -> current reader", + env, descP, &descP->currentReader.mon); + + while (reader_pop(env, descP, &req)) { + SSDBG( descP, + ("SOCKET", "recv_error_current_reader -> abort %T\r\n", + req.pid) ); + esock_send_abort_msg(env, sockRef, req.ref, reason, &req.pid); + DEMONP("recv_error_current_reader -> pop'ed reader", + env, descP, &req.mon); + } + } +} + + + +/* *** recv_check_result *** + * + * Process the result of a call to recv. + */ +static +ERL_NIF_TERM recv_check_result(ErlNifEnv* env, + SocketDescriptor* descP, + int read, + int toRead, + int saveErrno, + ErlNifBinary* bufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + char* xres; + int sres; + ERL_NIF_TERM res, 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) ); + + + /* <KOLLA> + * + * We need to handle read = 0 for other type(s) (DGRAM) when + * its actually valid to read 0 bytes. + * + * </KOLLA> + */ + + if ((read == 0) && (descP->type == SOCK_STREAM)) { + + 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! + */ + + recv_error_current_reader(env, descP, sockRef, res); + + FREE_BIN(bufP); + + return res; + + } + + /* There is a special case: If the provided 'to read' value is + * 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) { + + /* +++ 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 => * + * (maybe) 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. + * + * 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); + + 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) ); + + if (descP->rNum > 0) { + + descP->rNumCnt++; + if (descP->rNumCnt >= descP->rNum) { + + descP->rNumCnt = 0; + + cnt_inc(&descP->readPkgCnt, 1); + + recv_update_current_reader(env, descP, sockRef); + + /* 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); + + } + } + + /* Yes, we *do* need to continue reading */ + + if ((xres = recv_init_current_reader(env, + descP, recvRef)) != NULL) { + descP->rNumCnt = 0; + FREE_BIN(bufP); + 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); + + SSDBG( descP, + ("SOCKET", + "recv_check_result -> [%d] " + "we got exactly what we could fit\r\n", toRead) ); + + recv_update_current_reader(env, descP, sockRef); + + /* 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); + + } + + } else if (read < 0) { + + /* +++ Error handling +++ */ + + FREE_BIN(bufP); + + if (saveErrno == ECONNRESET) { + + res = esock_make_error(env, atom_closed); + + /* +++ Oups - closed +++ */ + + SSDBG( descP, ("SOCKET", + "recv_check_result -> [%d] closed\r\n", toRead) ); + + /* <KOLLA> + * + * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING + * PROCESS, WE NEED TO INFORM IT!!! + * + * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! + * HANDLED BY THE STOP (CALLBACK) FUNCTION? + * + * 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??? + * + * </KOLLA> + */ + + descP->closeLocal = FALSE; + descP->state = SOCKET_STATE_CLOSING; + + recv_error_current_reader(env, descP, sockRef, res); + + if ((sres = esock_select_stop(env, descP->sock, descP)) < 0) { + esock_warning_msg("Failed stop select (closed) " + "for current reader (%T): %d\r\n", + recvRef, sres); + } + + return res; + + } else if ((saveErrno == ERRNO_BLOCK) || + (saveErrno == EAGAIN)) { + + 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); + + SSDBG( descP, ("SOCKET", "recv_check_result -> SELECT for more\r\n") ); + + if ((sres = esock_select_read(env, descP->sock, descP, + NULL, recvRef)) < 0) { + res = esock_make_error(env, + MKT2(env, + esock_atom_select_failed, + MKI(env, sres))); + } else { + res = esock_make_error(env, esock_atom_eagain); + } + + return res; + } else { + ERL_NIF_TERM res = esock_make_error_errno(env, saveErrno); + + SSDBG( descP, ("SOCKET", "recv_check_result -> [%d] errno: %d\r\n", + toRead, saveErrno) ); + + recv_error_current_reader(env, descP, sockRef, res); + + return res; + } + + } 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 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) ); + + descP->rNumCnt = 0; + cnt_inc(&descP->readPkgCnt, 1); + cnt_inc(&descP->readByteCnt, read); + + recv_update_current_reader(env, descP, sockRef); + + /* 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); + + SSDBG( descP, + ("SOCKET", "recv_check_result -> [%d] done\r\n", toRead) ); + + return esock_make_ok3(env, atom_true, data); + + } else { + + /* +++ We got only a part of what was expected +++ + * +++ => 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) ); + + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) { + FREE_BIN(bufP); + return esock_make_error_str(env, xres); + } + + data = MKBIN(env, bufP); + data = MKSBIN(env, data, 0, read); + + cnt_inc(&descP->readByteCnt, read); + + /* SELECT for more data */ + + if ((sres = esock_select_read(env, descP->sock, descP, + NULL, recvRef)) < 0) { + /* Result: {error, Reason} + * Reason: {select_failed, sres, data} + */ + res = esock_make_error(env, + MKT3(env, + esock_atom_select_failed, + MKI(env, sres), + data)); + } else { + res = esock_make_ok3(env, atom_false, data); + } + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + return res; + } + } +} + + +/* 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, + int saveErrno, + ErlNifBinary* bufP, + SocketAddress* fromAddrP, + unsigned int fromAddrLen, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + char* xres; + int sres; + ERL_NIF_TERM data, res; + + 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. + */ + + if (read < 0) { + + /* +++ Error handling +++ */ + + if (saveErrno == ECONNRESET) { + res = esock_make_error(env, atom_closed); + + /* +++ Oups - closed +++ */ + + SSDBG( descP, ("SOCKET", "recvfrom_check_result -> closed\r\n") ); + + /* <KOLLA> + * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING + * PROCESS, WE NEED TO INFORM IT!!! + * + * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! + * + * </KOLLA> + */ + + descP->closeLocal = FALSE; + descP->state = SOCKET_STATE_CLOSING; + + recv_error_current_reader(env, descP, sockRef, res); + + if ((sres = esock_select_stop(env, descP->sock, descP)) < 0) { + esock_warning_msg("Failed stop select (closed) " + "for current reader (%T): %d\r\n", + recvRef, sres); + } + + FREE_BIN(bufP); + + return res; + + } else if ((saveErrno == ERRNO_BLOCK) || + (saveErrno == EAGAIN)) { + + SSDBG( descP, ("SOCKET", "recvfrom_check_result -> eagain\r\n") ); + + FREE_BIN(bufP); + + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) + return esock_make_error_str(env, xres); + + if ((sres = esock_select_read(env, descP->sock, descP, + NULL, recvRef)) < 0) { + res = esock_make_error(env, + MKT2(env, + esock_atom_select_failed, + MKI(env, sres))); + } else { + res = esock_make_error(env, esock_atom_eagain); + } + + return res; + } else { + + res = esock_make_error_errno(env, saveErrno); + + SSDBG( descP, + ("SOCKET", + "recvfrom_check_result -> errno: %d\r\n", saveErrno) ); + + recv_error_current_reader(env, descP, sockRef, res); + + FREE_BIN(bufP); + + return res; + } + + } else { + + /* +++ We sucessfully got a message - time to encode the address +++ */ + + ERL_NIF_TERM eSockAddr; + + esock_encode_sockaddr(env, + fromAddrP, fromAddrLen, + &eSockAddr); + + 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); + } + + recv_update_current_reader(env, descP, sockRef); + + return esock_make_ok2(env, MKT2(env, eSockAddr, data)); + + } +} + + + +/* 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* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + int sres; + ERL_NIF_TERM res; + + 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) ); + + + /* <KOLLA> + * + * We need to handle read = 0 for other type(s) (DGRAM) when + * its actually valid to read 0 bytes. + * + * </KOLLA> + */ + + 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! + */ + + + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + + 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. + */ + + if (read < 0) { + + /* +++ Error handling +++ */ + + if (saveErrno == ECONNRESET) { + + /* +++ Oups - closed +++ */ + + SSDBG( descP, ("SOCKET", "recvmsg_check_result -> closed\r\n") ); + + /* <KOLLA> + * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING + * PROCESS, WE NEED TO INFORM IT!!! + * + * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!! + * + * </KOLLA> + */ + + res = esock_make_error(env, atom_closed); + descP->closeLocal = FALSE; + descP->state = SOCKET_STATE_CLOSING; + + recv_error_current_reader(env, descP, sockRef, res); + + if ((sres = esock_select_stop(env, descP->sock, descP)) < 0) { + esock_warning_msg("Failed stop select (closed) " + "for current reader (%T): %d\r\n", + recvRef, sres); + } + + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + + return res;; + + } else if ((saveErrno == ERRNO_BLOCK) || + (saveErrno == EAGAIN)) { + char* xres; + + SSDBG( descP, ("SOCKET", "recvmsg_check_result -> eagain\r\n") ); + + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + + if ((xres = recv_init_current_reader(env, descP, recvRef)) != NULL) + return esock_make_error_str(env, xres); + + if ((sres = esock_select_read(env, descP->sock, descP, + NULL, recvRef)) < 0) { + res = esock_make_error(env, + MKT2(env, + esock_atom_select_failed, + MKI(env, sres))); + } else { + res = esock_make_error(env, esock_atom_eagain); + } + + return res; + + } else { + + res = esock_make_error_errno(env, saveErrno); + + SSDBG( descP, + ("SOCKET", + "recvmsg_check_result -> errno: %d\r\n", saveErrno) ); + + recv_error_current_reader(env, descP, sockRef, res); + + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + + return res; + } + + } else { + + /* +++ We sucessfully got a message - time to encode it +++ */ + + ERL_NIF_TERM eMsgHdr; + char* xres; + + /* + * <KOLLA> + * + * The return value of recvmsg is the *total* number of bytes + * that where successfully read. This data has been put into + * the *IO vector*. + * + * </KOLLA> + */ + + if ((xres = encode_msghdr(env, descP, + read, msgHdrP, dataBufP, ctrlBufP, + &eMsgHdr)) != NULL) { + + SSDBG( descP, + ("SOCKET", + "recvmsg_check_result -> " + "(msghdr) encode failed: %s\r\n", xres) ); + + recv_update_current_reader(env, descP, sockRef); + + FREE_BIN(dataBufP); FREE_BIN(ctrlBufP); + + return esock_make_error_str(env, xres); + } else { + + SSDBG( descP, + ("SOCKET", + "recvmsg_check_result -> " + "(msghdr) encode ok: %T\r\n", eMsgHdr) ); + + recv_update_current_reader(env, descP, sockRef); + + return esock_make_ok2(env, eMsgHdr); + } + + } +} + + + + +/* +++ 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) ); + + /* 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, + 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 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 + * 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 (encode_cmsghdr_level(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; +} + + + +/* +++ decode_cmsghdrs +++ + * + * Decode a list of cmsghdr(). There can be 0 or more cmsghdr "blocks". + * + * 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. + * + * 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, + char* cmsgHdrBufP, + size_t cmsgHdrBufLen, + size_t* cmsgHdrBufUsed) +{ + ERL_NIF_TERM elem, tail, list; + 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)) { + + 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; + + used = 0; // Just in case... + if ((xres = decode_cmsghdr(env, descP, elem, bufP, rem, &used)) != NULL) + return xres; + + 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; +} + + +/* +++ 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 is already coded. + */ +extern +char* decode_cmsghdr(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM eCMsgHdr, + 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; + char* xres; + + /* First extract all three attributes (as terms) */ + + 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, descP, bufP, rem, level, type, eData, used); + + } else { + *used = 0; + 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 and ip_tos() respectively. + */ +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) +{ + 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)) { + 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 { + + /* 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)) { + 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), + used); + } else { + *used = 0; + xres = ESOCK_STR_EINVAL; + } + } + break; +#endif + +#if defined(IP_TTL) + case IP_TTL: + { + int data; + if (GET_INT(env, eData, &data)) { + 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), + used); + } else { + *used = 0; + xres = ESOCK_STR_EINVAL; + } + } + break; +#endif + + } + break; + + default: + *used = 0; + xres = ESOCK_STR_EINVAL; + break; + } + + } + + return xres; +} + + +/* *** decode_cmsghdr_final *** + * + * This does the final create of the cmsghdr (including the data copy). + */ +static +char* decode_cmsghdr_final(SocketDescriptor* descP, + char* bufP, + size_t rem, + int level, + int type, + char* data, + int sz, + size_t* used) +{ + int len = CMSG_LEN(sz); + int space = CMSG_SPACE(sz); + + 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 */ + cmsgP->cmsg_len = len; + cmsgP->cmsg_level = level; + cmsgP->cmsg_type = type; + + 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; +} + + +/* +++ encode_cmsghdr_level +++ + * + * Encode the level 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; + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + *eLevel = esock_atom_ip; + 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; +#endif + + case IPPROTO_UDP: + *eLevel = esock_atom_udp; + xres = NULL; + break; + + default: + *eLevel = MKI(env, level); + xres = NULL; + break; + } + + return xres; +} + + + +/* +++ 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 if (COMPARE(eLevel, esock_atom_ip) == 0) { +#if defined(SOL_IP) + *level = SOL_IP; +#else + *level = IPPROTO_IP; +#endif + xres = NULL; +#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) { + *level = IPPROTO_UDP; + xres = NULL; + } else { + *level = -1; + xres = ESOCK_STR_EINVAL; + } + } else if (IS_NUM(env, eLevel)) { + if (!GET_INT(env, eLevel, level)) + xres = ESOCK_STR_EINVAL; + } else { + *level = -1; + xres = ESOCK_STR_EINVAL; + } + + return xres; +} + + + +/* +++ 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(SO_TIMESTAMP) + case SO_TIMESTAMP: + *eType = esock_atom_timestamp; + break; +#endif + +#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(HAVE_IPV6) +#if defined(SOL_IPV6) + case SOL_IPV6: +#else + case IPPROTO_IPV6: +#endif + 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: + 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; +} + + + +/* +++ 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 (IS_NUM(env, eType)) { + if (!GET_INT(env, eType, type)) { + *type = -1; + xres = ESOCK_STR_EINVAL; + } + } else { + *type = -1; + xres = ESOCK_STR_EINVAL; + } + break; + + +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + if (IS_ATOM(env, eType)) { + 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 { + xres = ESOCK_STR_EINVAL; + } + } else 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; + +#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; + xres = ESOCK_STR_EINVAL; + } + } else { + *type = -1; + xres = ESOCK_STR_EINVAL; + } + break; +#endif + + case IPPROTO_UDP: + 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; + + default: + *type = -1; + 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, ctrlBuf, type, + dataP, dataPos, dataLen, + 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(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); + 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_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(). + * + */ + +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 = NULL; + + switch (type) { +#if defined(IP_TOS) + case IP_TOS: + { + unsigned char tos = *dataP; + switch (IPTOS_TOS(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; +#if defined(IPTOS_MINCOST) + case IPTOS_MINCOST: + *eCMsgHdrData = esock_atom_mincost; + break; +#endif + 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 xres; +} + + + +/* +++ encode_cmsghdr_data_ipv6 +++ + * + * Encode the data part when protocol = IPv6 of the cmsghdr(). + * + */ +#if defined(HAVE_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(). + * + * 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 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" + "\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: + * + * {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. */ + + onOff = esock_decode_bool(lt[0]); + + if (!GET_INT(env, lt[1], &secs)) + return FALSE; + + valP->l_onoff = (onOff) ? 1 : 0; + valP->l_linger = secs; + + return TRUE; +} + + + +/* +++ decode 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 + * + */ +#if defined(IP_TOS) +static +BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) +{ + BOOLEAN_T result = FALSE; + + if (IS_ATOM(env, eVal)) { + + if (COMPARE(eVal, esock_atom_lowdelay) == 0) { + *val = IPTOS_LOWDELAY; + result = TRUE; + } else if (COMPARE(eVal, esock_atom_throughput) == 0) { + *val = IPTOS_THROUGHPUT; + result = TRUE; + } 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; + } + + } 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; +} +#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; +#if defined(IP_PMTUDISC_PROBE) + } else if (COMPARE(eVal, atom_probe) == 0) { + *val = IP_PMTUDISC_PROBE; +#endif + } 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 + + + +/* +++ 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; +#if defined(IPV6_PMTUDISC_PROBE) + } else if (COMPARE(eVal, atom_probe) == 0) { + *val = IPV6_PMTUDISC_PROBE; +#endif + } 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; + +#if defined(IP_PMTUDISC_PROBE) + case IP_PMTUDISC_PROBE: + *eVal = atom_probe; + break; +#endif + + default: + *eVal = MKI(env, val); + break; + } + + return; +} +#endif + + + +/* +++ 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; + +#if defined(IPV6_PMTUDISC_PROBE) + case IPV6_PMTUDISC_PROBE: + *eVal = atom_probe; + break; +#endif + + 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: + * + * {NativeOpt, ValueSize} + * + * NativeOpt :: integer() + * ValueSize :: int | bool | non_neg_integer() + * + */ +static +BOOLEAN_T decode_native_get_opt(ErlNifEnv* env, ERL_NIF_TERM eVal, + int* opt, Uint16* valueType, 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. + * 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 (IS_ATOM(env, nativeOptT[1])) { + + 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; + } + } else { + return FALSE; + } + + SGDBG( ("SOCKET", "decode_native_get_opt -> done\r\n") ); + + return TRUE; +} + + + +/* +++ 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 (IPTOS_TOS(val)) { + case IPTOS_LOWDELAY: + result = esock_make_ok2(env, esock_atom_lowdelay); + break; + + case IPTOS_THROUGHPUT: + result = esock_make_ok2(env, esock_atom_throughput); + break; + + case IPTOS_RELIABILITY: + 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)); + break; + } + + return result; +} + + + + + +/* *** 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 */ + + // This needs to be released when the socket is closed! + descP->env = enif_alloc_env(); + + sprintf(buf, "socket[w,%d]", sock); + descP->writeMtx = MCREATE(buf); + descP->currentWriterP = NULL; // currentWriter not used + descP->writersQ.first = NULL; + descP->writersQ.last = NULL; + descP->isWritable = FALSE; // 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 = FALSE; // TRUE; + descP->readPkgCnt = 0; + descP->readByteCnt = 0; + descP->readTries = 0; + descP->readWaits = 0; + + sprintf(buf, "socket[acc,%d]", sock); + descP->accMtx = MCREATE(buf); + descP->currentAcceptorP = NULL; // currentAcceptor not used + descP->acceptorsQ.first = NULL; + descP->acceptorsQ.last = NULL; + + sprintf(buf, "socket[close,%d]", sock); + descP->closeMtx = MCREATE(buf); + descP->closeEnv = NULL; + descP->closeRef = esock_atom_undefined; + + 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; + descP->dbg = SOCKET_DEBUG_DEFAULT; + + 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; +} + + + +/* 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); + + /* *** Domain counter *** */ + 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 + + /* *** Type counter *** */ + if (type == SOCK_STREAM) + 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 + + /* *** Protocol counter *** */ + 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); + + /* *** Domain counter *** */ + 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 + + /* *** Type counter *** */ + if (type == SOCK_STREAM) + 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 + + /* *** Protocol counter *** */ + 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); +} + + +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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. + */ +#if !defined(__WIN32__) +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: + *domain = -1; + 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; + +#ifdef HAVE_SCTP + case SOCKET_TYPE_SEQPACKET: + *type = SOCK_SEQPACKET; + break; +#endif + + default: + *type = -1; + return FALSE; + } + + return TRUE; +} + + +/* eproto2proto - convert internal (erlang) protocol to (proper) protocol + * + * Note that only a subset is supported. + */ +static +BOOLEAN_T eproto2proto(ErlNifEnv* env, + ERL_NIF_TERM eproto, + int* proto) +{ + if (IS_NUM(env, eproto)) { + int ep; + + 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; +#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; + + 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; +} + + +#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 (!GET_MAP_VAL(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 + + +/* esendflags2sendflags - convert internal (erlang) send flags to (proper) + * send flags. + */ +static +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) { +#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; + } + + } + + *flags = tmp; + + return TRUE; +} + + + +/* erecvflags2recvflags - convert internal (erlang) send flags to (proper) + * send flags. + */ +static +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) ); + + if (eflags == 0) { + *flags = 0; + return TRUE; + } + + 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) { +#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 + + /* + * <KOLLA> + * + * We need to handle this, because it may effect the read algorithm + * + * </KOLLA> + */ +#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; + } + + } + + *flags = tmp; + + return TRUE; +} + + + +/* 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 +size_t my_strnlen(const char *s, size_t maxlen) +{ + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; +} +*/ +#endif + + +/* 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 an close message to the specified process: + * A message in the form: + * + * {'$socket', SockRef, close, CloseRef} + * + * 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, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM sr = ((descP->closeEnv != NULL) ? + enif_make_copy(descP->closeEnv, sockRef) : + sockRef); + char* res = esock_send_socket_msg(env, + sr, + esock_atom_close, + descP->closeRef, + &descP->closerPid, + descP->closeEnv); + + descP->closeEnv = NULL; + + return res; +} + + +/* 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* esock_send_abort_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + ERL_NIF_TERM reason, + ErlNifPid* pid) +{ + ErlNifEnv* msg_env = enif_alloc_env(); + ERL_NIF_TERM info = MKT2(msg_env, + enif_make_copy(msg_env, recvRef), + enif_make_copy(msg_env, reason)); + + return esock_send_socket_msg(env, sockRef, esock_atom_abort, info, pid, + msg_env); +} + + +/* *** esock_send_socket_msg *** + * + * This function sends a general purpose socket message to an erlang + * process. A general 'socket' message has the ("erlang") form: + * + * {'$socket', SockRef, Tag, Info} + * + * Where + * + * SockRef: reference() + * Tag: atom() + * Info: term() + * + */ + +static +char* esock_send_socket_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM tag, + ERL_NIF_TERM info, + ErlNifPid* pid, + ErlNifEnv* msg_env) +{ + ERL_NIF_TERM msg; + if (!msg_env) { + msg_env = enif_alloc_env(); + sockRef = enif_make_copy(msg_env, sockRef); + tag = enif_make_copy(msg_env, tag); + info = enif_make_copy(msg_env, info); + } + msg = MKT4(msg_env, esock_atom_socket_tag, sockRef, tag, info); + + return esock_send_msg(env, msg, pid, msg_env); +} + + +/* Send a message to the specified process. + */ +static +char* esock_send_msg(ErlNifEnv* env, + ERL_NIF_TERM msg, + ErlNifPid* pid, + ErlNifEnv* msg_env) +{ + int res = enif_send(env, pid, msg_env, msg); + if (msg_env) + enif_free_env(msg_env); + + if (!res) + return str_exsend; + else + return NULL; +} +#endif // #if defined(__WIN32__) + + +/* ---------------------------------------------------------------------- + * S e l e c t W r a p p e r F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +static +int esock_select_read(ErlNifEnv* env, + ErlNifEvent event, + void* obj, + const ErlNifPid* pid, + ERL_NIF_TERM ref) +{ + return enif_select(env, event, (ERL_NIF_SELECT_READ), obj, pid, ref); +} + + +static +int esock_select_write(ErlNifEnv* env, + ErlNifEvent event, + void* obj, + const ErlNifPid* pid, + ERL_NIF_TERM ref) +{ + return enif_select(env, event, (ERL_NIF_SELECT_WRITE), obj, pid, ref); +} + + +static +int esock_select_stop(ErlNifEnv* env, + ErlNifEvent event, + void* obj) +{ + return enif_select(env, event, (ERL_NIF_SELECT_STOP), obj, NULL, + esock_atom_undefined); +} + +static +int esock_select_cancel(ErlNifEnv* env, + ErlNifEvent event, + enum ErlNifSelectFlags mode, + void* obj) +{ + return enif_select(env, event, (ERL_NIF_SELECT_CANCEL | mode), obj, NULL, + esock_atom_undefined); +} + + +/* ---------------------------------------------------------------------- + * A c t i v a t e N e x t ( o p e r a t o r ) F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* *** activate_next_acceptor *** + * *** activate_next_writer *** + * *** activate_next_reader *** + * + * This functions pops the requestors queue and then selects until it + * manages to successfully activate a requestor or the queue is empty. + * Return value indicates if a new requestor was activated or not. + */ + +#define ACTIVATE_NEXT_FUNCS \ + ACTIVATE_NEXT_FUNC_DECL(acceptor, read, currentAcceptor, acceptorsQ) \ + ACTIVATE_NEXT_FUNC_DECL(writer, write, currentWriter, writersQ) \ + ACTIVATE_NEXT_FUNC_DECL(reader, read, currentReader, readersQ) + +#define ACTIVATE_NEXT_FUNC_DECL(F, S, R, Q) \ + static \ + BOOLEAN_T activate_next_##F(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + ERL_NIF_TERM sockRef) \ + { \ + BOOLEAN_T popped, activated; \ + int sres; \ + SocketRequestor* reqP = &descP->R; \ + SocketRequestQueue* q = &descP->Q; \ + \ + popped = FALSE; \ + do { \ + \ + if (requestor_pop(q, reqP)) { \ + \ + /* There was another one */ \ + \ + SSDBG( descP, \ + ("SOCKET", \ + "activate_next_" #F " -> new (active) requestor: " \ + "\r\n pid: %T" \ + "\r\n ref: %T" \ + "\r\n", reqP->pid, reqP->ref) ); \ + \ + if ((sres = esock_select_##S(env, descP->sock, descP, \ + &reqP->pid, reqP->ref)) < 0) { \ + /* We need to inform this process, reqP->pid, */ \ + /* that we failed to select, so we don't leave */ \ + /* it hanging. */ \ + /* => send abort */ \ + \ + esock_send_abort_msg(env, sockRef, reqP->ref, \ + sres, &reqP->pid); \ + \ + } else { \ + \ + /* Success: New requestor selected */ \ + popped = TRUE; \ + activated = FALSE; \ + \ + } \ + \ + } else { \ + \ + SSDBG( descP, \ + ("SOCKET", \ + "activate_next_" #F " -> no more requestors\r\n") ); \ + \ + popped = TRUE; \ + activated = FALSE; \ + } \ + \ + } while (!popped); \ + \ + SSDBG( descP, \ + ("SOCKET", "activate_next_" #F " -> " \ + "done with %s\r\n", B2S(activated)) ); \ + \ + return activated; \ + } +ACTIVATE_NEXT_FUNCS +#undef ACTIVATE_NEXT_FUNC_DECL + + + + +/* ---------------------------------------------------------------------- + * R e q u e s t o r Q u e u e F u n c t i o n s + * ---------------------------------------------------------------------- + * + * Since each of these functions (search4pid, push, pop and unqueue + * are virtually identical for acceptors, writers and readers, + * we make use of set of declaration macros. + */ + +#if !defined(__WIN32__) + +/* *** acceptor_search4pid *** + * *** writer_search4pid *** + * *** reader_search4pid *** + * + * Search for a pid in the requestor (acceptor, writer, or reader) queue. + * + */ + +#define REQ_SEARCH4PID_FUNCS \ + REQ_SEARCH4PID_FUNC_DECL(acceptor, acceptorsQ) \ + REQ_SEARCH4PID_FUNC_DECL(writer, writersQ) \ + REQ_SEARCH4PID_FUNC_DECL(reader, readersQ) + +#define REQ_SEARCH4PID_FUNC_DECL(F, Q) \ + static \ + BOOLEAN_T F##_search4pid(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + ErlNifPid* pid) \ + { \ + return qsearch4pid(env, &descP->Q, pid); \ + } +REQ_SEARCH4PID_FUNCS +#undef REQ_SEARCH4PID_FUNC_DECL + + + +/* *** acceptor_push *** + * *** writer_push *** + * *** reader_push *** + * + * Push a requestor (acceptor, writer, or reader) onto its queue. + * This happens when we already have a current request (of its type). + * + */ + +#define REQ_PUSH_FUNCS \ + REQ_PUSH_FUNC_DECL(acceptor, acceptorsQ) \ + REQ_PUSH_FUNC_DECL(writer, writersQ) \ + REQ_PUSH_FUNC_DECL(reader, readersQ) + +#define REQ_PUSH_FUNC_DECL(F, Q) \ + static \ + ERL_NIF_TERM F##_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("reader_push -> " #F " request", \ + env, descP, &pid, &reqP->mon) != 0) { \ + FREE(reqP); \ + return esock_make_error(env, atom_exmon); \ + } \ + \ + qpush(&descP->Q, e); \ + \ + return esock_make_error(env, esock_atom_eagain); \ + } +REQ_PUSH_FUNCS +#undef REQ_PUSH_FUNC_DECL + + + +/* *** acceptor_pop *** + * *** writer_pop *** + * *** reader_pop *** + * + * Pop a requestor (acceptor, writer, or reader) from its queue. + * + */ +#define REQ_POP_FUNCS \ + REQ_POP_FUNC_DECL(acceptor, acceptorsQ) \ + REQ_POP_FUNC_DECL(writer, writersQ) \ + REQ_POP_FUNC_DECL(reader, readersQ) + +#define REQ_POP_FUNC_DECL(F, Q) \ + static \ + BOOLEAN_T F##_pop(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + SocketRequestor* reqP) \ + { \ + return requestor_pop(&descP->Q, reqP); \ + } +REQ_POP_FUNCS +#undef REQ_POP_FUNC_DECL + + + +/* *** acceptor_unqueue *** + * *** writer_unqueue *** + * *** reader_unqueue *** + * + * Remove a requestor (acceptor, writer, or reader) from its queue. + * + */ + +#define REQ_UNQUEUE_FUNCS \ + REQ_UNQUEUE_FUNC_DECL(acceptor, acceptorsQ) \ + REQ_UNQUEUE_FUNC_DECL(writer, writersQ) \ + REQ_UNQUEUE_FUNC_DECL(reader, readersQ) + +#define REQ_UNQUEUE_FUNC_DECL(F, Q) \ + static \ + BOOLEAN_T F##_unqueue(ErlNifEnv* env, \ + SocketDescriptor* descP, \ + const ErlNifPid* pid) \ + { \ + return qunqueue(env, descP, "qunqueue -> waiting " #F, \ + &descP->Q, pid); \ + } +REQ_UNQUEUE_FUNCS +#undef REQ_UNQUEUE_FUNC_DECL + + + +/* *** requestor pop *** + * + * Pop an requestor from its queue. + */ +static +BOOLEAN_T requestor_pop(SocketRequestQueue* q, + SocketRequestor* reqP) +{ + SocketRequestQueueElement* e = qpop(q); + + if (e != NULL) { + reqP->pid = e->data.pid; + reqP->mon = e->data.mon; + reqP->ref = e->data.ref; + FREE(e); + return TRUE; + } else { + /* (writers) Queue was empty */ + enif_set_pid_undefined(&reqP->pid); + // *reqP->mon = NULL; we have no null value for monitors + reqP->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(&tmp->data.pid, pid) == 0) + 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, + SocketDescriptor* descP, + const char* slogan, + 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(&e->data.pid, pid) == 0) { + + /* 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) { + 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; +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * C o u n t e r F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +#if !defined(__WIN32__) +static +BOOLEAN_T cnt_inc(Uint32* cnt, Uint32 inc) +{ + BOOLEAN_T wrap; + Uint32 max = 0xFFFFFFFF; + Uint32 current = *cnt; + + if ((max - inc) >= current) { + *cnt += inc; + wrap = FALSE; + } else { + *cnt = inc - (max - current) - 1; + wrap = TRUE; + } + + return (wrap); +} + + +static +void cnt_dec(Uint32* cnt, Uint32 dec) +{ + Uint32 current = *cnt; + + if (dec > current) + *cnt = 0; // The counter cannot be < 0 so this is the best we can do... + else + *cnt -= dec; + + return; +} +#endif // if !defined(__WIN32__) + + + + +/* ---------------------------------------------------------------------- + * M o n i t o r W r a p p e r F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +#if !defined(__WIN32__) + +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\r\n", descP->sock, slogan) ); + res = enif_monitor_process(env, descP, pid, &monP->mon); + + if (res != 0) { + monP->is_active = 0; + SSDBG( descP, ("SOCKET", "[%d] monitor failed: %d\r\n", descP->sock, res) ); + } else { + monP->is_active = 1; + } + + return res; +} + + +static +int esock_demonitor(const char* slogan, + ErlNifEnv* env, + SocketDescriptor* descP, + ESockMonitor* monP) +{ + int res; + + if (!monP->is_active) + return 1; + + SSDBG( descP, ("SOCKET", "[%d] %s: try demonitor\r\n", descP->sock, slogan) ); + + 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) ); + } + + return res; +} + + +static +void esock_monitor_init(ESockMonitor* monP) +{ + monP->is_active = 0; +} + +#endif // if !defined(__WIN32__) + + +/* +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 + * ---------------------------------------------------------------------- + */ + +/* ========================================================================= + * socket_dtor - Callback function for resource destructor + * + */ +static +void socket_dtor(ErlNifEnv* env, void* obj) +{ +#if !defined(__WIN32__) + 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); +#endif +} + + +/* ========================================================================= + * 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). + * + */ +static +void socket_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) +{ +#if !defined(__WIN32__) + SocketDescriptor* descP = (SocketDescriptor*) obj; + ERL_NIF_TERM sockRef; + + SSDBG( descP, + ("SOCKET", "socket_stop -> entry when %s" + "\r\n sock: %d (%d)" + "\r\n", + ((is_direct_call) ? "called" : "scheduled"), descP->sock, fd) ); + + /* +++ Lock it down +++ */ + + MLOCK(descP->writeMtx); + MLOCK(descP->readMtx); + MLOCK(descP->accMtx); + if (!is_direct_call) MLOCK(descP->closeMtx); + + 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) ); + + sockRef = enif_make_resource(env, descP); + 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... + * If we got here because the controlling process died, + * there is no point to demonitor. Also, we do not actually + * have a monitor in that case... + */ + DEMONP("socket_stop -> ctrl", env, descP, &descP->ctrlMon); + + + + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Check current and waiting Writers + * + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + + if (descP->currentWriterP != NULL) { + /* We have a (current) writer and *may* therefor also have + * writers waiting. + */ + + DEMONP("socket_stop -> current writer", + env, descP, &descP->currentWriter.mon); + + SSDBG( descP, ("SOCKET", "socket_stop -> handle current writer\r\n") ); + if (COMPARE_PIDS(&descP->closerPid, &descP->currentWriter.pid) != 0) { + SSDBG( descP, ("SOCKET", "socket_stop -> " + "send abort message to current writer %T\r\n", + descP->currentWriter.pid) ); + if (esock_send_abort_msg(env, + sockRef, + 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) */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting writer(s)\r\n") ); + inform_waiting_procs(env, "writer", + descP, &descP->writersQ, TRUE, atom_closed); + } + + + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Check current and waiting Readers + * + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + + if (descP->currentReaderP != NULL) { + + /* We have a (current) reader and *may* therefor also have + * readers waiting. + */ + + DEMONP("socket_stop -> current reader", + env, descP, &descP->currentReader.mon); + + SSDBG( descP, ("SOCKET", "socket_stop -> handle current reader\r\n") ); + if (COMPARE_PIDS(&descP->closerPid, &descP->currentReader.pid) != 0) { + SSDBG( descP, ("SOCKET", "socket_stop -> " + "send abort message to current reader %T\r\n", + descP->currentReader.pid) ); + /* + esock_dbg_printf("SOCKET", "socket_stop -> " + "send abort message to current reader %T\r\n", + descP->currentReader.pid); + */ + if (esock_send_abort_msg(env, + sockRef, + 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) */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting reader(s)\r\n") ); + inform_waiting_procs(env, "reader", + descP, &descP->readersQ, TRUE, atom_closed); + } + + + + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Check current and waiting Acceptors + * + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + + if (descP->currentAcceptorP != NULL) { + /* We have a (current) acceptor and *may* therefor also have + * acceptors waiting. + */ + + DEMONP("socket_stop -> current acceptor", + env, descP, &descP->currentAcceptor.mon); + + SSDBG( descP, ("SOCKET", "socket_stop -> handle current acceptor\r\n") ); + if (COMPARE_PIDS(&descP->closerPid, &descP->currentAcceptor.pid) != 0) { + SSDBG( descP, ("SOCKET", "socket_stop -> " + "send abort message to current acceptor %T\r\n", + descP->currentAcceptor.pid) ); + if (esock_send_abort_msg(env, + sockRef, + 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) */ + SSDBG( descP, ("SOCKET", "socket_stop -> handle waiting acceptor(s)\r\n") ); + inform_waiting_procs(env, "acceptor", + descP, &descP->acceptorsQ, TRUE, atom_closed); + } + + + + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Maybe inform waiting closer + * + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + + if (descP->sock != INVALID_SOCKET) { + + if (descP->closeLocal) { + + if (!is_direct_call) { + + /* +++ send close message to the waiting process +++ */ + + esock_send_close_msg(env, descP, sockRef); + + DEMONP("socket_stop -> closer", env, descP, &descP->closerMon); + + } else { + + /* We only need to explicitly free the environment here + * since the message send takes care of it if scheduled. + */ + + if (descP->closeEnv != NULL) { + enif_clear_env(descP->closeEnv); + enif_free_env(descP->closeEnv); + descP->closeEnv = NULL; + } + + } + } + } + + + SSDBG( descP, ("SOCKET", "socket_stop -> unlock all mutex(s)\r\n") ); + + if (!is_direct_call) 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) ); + +#endif // if !defined(__WIN32__) +} + + + +/* This function traverse the queue and sends the specified + * 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, + char* role, + SocketDescriptor* descP, + SocketRequestQueue* q, + BOOLEAN_T free, + ERL_NIF_TERM reason) +{ + SocketRequestQueueElement* currentP = q->first; + SocketRequestQueueElement* nextP; + ERL_NIF_TERM sockRef = enif_make_resource(env, descP); + + /* + esock_dbg_printf("SOCKET", "inform_waiting_procs -> entry with: " + "\r\n role: %s" + "\r\n free: %s" + "\r\n reason: %T" + "\r\n", role, B2S(free), reason); + */ + + while (currentP != NULL) { + + /* <KOLLA> + * + * 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... + * + * </KOLLA> + */ + + SSDBG( descP, + ("SOCKET", "inform_waiting_procs -> abort request %T (from %T)\r\n", + currentP->data.ref, currentP->data.pid) ); + + /* + esock_dbg_printf("SOCKET", "inform_waiting_procs -> " + "try sending abort to %s %T " + "\r\n", role, currentP->data.pid); + */ + + if (esock_send_abort_msg(env, + sockRef, + currentP->data.ref, + reason, + ¤tP->data.pid) != NULL) { + + esock_warning_msg("Failed sending abort (%T) message to " + "current %s %T\r\n", + currentP->data.ref, + role, + currentP->data.pid); + + } + + DEMONP("inform_waiting_procs -> current 'request'", + env, descP, ¤tP->data.mon); + nextP = currentP->nextP; + if (free) FREE(currentP); + currentP = nextP; + } + + if (free) { + q->first = NULL; + q->last = NULL; + } +} +#endif // if !defined(__WIN32__) + + +/* ========================================================================= + * socket_down - Callback function for resource down (monitored processes) + * + */ +static +void socket_down(ErlNifEnv* env, + void* obj, + const ErlNifPid* pid, + const ErlNifMonitor* mon) +{ +#if !defined(__WIN32__) + SocketDescriptor* descP = (SocketDescriptor*) obj; + int sres; + ERL_NIF_TERM sockRef; + + SSDBG( descP, ("SOCKET", "socket_down -> entry with" + "\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))) ); + + if (!IS_CLOSED(descP)) { + + if (COMPARE_PIDS(&descP->ctrlPid, pid) == 0) { + + /* 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; + MON_INIT(&descP->closerMon); + + sres = esock_select_stop(env, descP->sock, descP); + + if (sres & 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); + + descP->sock = INVALID_SOCKET; + descP->event = INVALID_EVENT; + + descP->state = SOCKET_STATE_CLOSED; + + } else if (sres & 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 { + + 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: %T" + "\r\n", sres, pid, descP->sock, + MON2T(env, mon)); + } + + } else { + + /* check all operation queue(s): acceptor, writer and reader. + * + * Is it really any point in doing this if the socket is closed? + * + */ + + SSDBG( descP, ("SOCKET", "socket_down -> other process term\r\n") ); + + sockRef = enif_make_resource(env, descP); + + MLOCK(descP->accMtx); + if (descP->currentAcceptorP != NULL) + socket_down_acceptor(env, descP, sockRef, pid); + MUNLOCK(descP->accMtx); + + MLOCK(descP->writeMtx); + if (descP->currentWriterP != NULL) + socket_down_writer(env, descP, sockRef, pid); + MUNLOCK(descP->writeMtx); + + MLOCK(descP->readMtx); + if (descP->currentReaderP != NULL) + socket_down_reader(env, descP, sockRef, pid); + MUNLOCK(descP->readMtx); + + } + } + + SSDBG( descP, ("SOCKET", "socket_down -> done\r\n") ); + +#endif // if !defined(__WIN32__) +} + + + +/* *** socket_down_acceptor *** + * + * Check and then handle a downed acceptor process. + * + */ +#if !defined(__WIN32__) +static +void socket_down_acceptor(ErlNifEnv* env, + SocketDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid) +{ + if (COMPARE_PIDS(&descP->currentAcceptor.pid, pid) == 0) { + + SSDBG( descP, ("SOCKET", + "socket_down_acceptor -> " + "current acceptor - try activate next\r\n") ); + + if (!activate_next_acceptor(env, descP, sockRef)) { + + SSDBG( descP, + ("SOCKET", "socket_down_acceptor -> no more writers\r\n") ); + + descP->state = SOCKET_STATE_LISTENING; + + descP->currentAcceptorP = NULL; + descP->currentAcceptor.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentAcceptor.pid); + esock_monitor_init(&descP->currentAcceptor.mon); + } + + } 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, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid) +{ + if (COMPARE_PIDS(&descP->currentWriter.pid, pid) == 0) { + + SSDBG( descP, ("SOCKET", + "socket_down_writer -> " + "current writer - try activate next\r\n") ); + + if (!activate_next_writer(env, descP, sockRef)) { + SSDBG( descP, ("SOCKET", + "socket_down_writer -> no active writer\r\n") ); + descP->currentWriterP = NULL; + descP->currentWriter.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentWriter.pid); + esock_monitor_init(&descP->currentWriter.mon); + } + + } 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, + ERL_NIF_TERM sockRef, + const ErlNifPid* pid) +{ + if (COMPARE_PIDS(&descP->currentReader.pid, pid) == 0) { + + SSDBG( descP, ("SOCKET", + "socket_down_reader -> " + "current reader - try activate next\r\n") ); + + if (!activate_next_reader(env, descP, sockRef)) { + SSDBG( descP, + ("SOCKET", "ncancel_recv_current -> no more readers\r\n") ); + descP->currentReaderP = NULL; + descP->currentReader.ref = esock_atom_undefined; + enif_set_pid_undefined(&descP->currentReader.pid); + esock_monitor_init(&descP->currentReader.mon); + } + + } 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); + } +} +#endif // if !defined(__WIN32__) + + + +/* ---------------------------------------------------------------------- + * 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 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 + // {"nif_open", 1, nif_open, 0}, + {"nif_open", 4, nif_open, 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", 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}, + {"nif_close", 1, nif_close, 0}, + {"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 */ + + /* "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", 3, nif_cancel, 0}, + {"nif_finalize_close", 1, nif_finalize_close, ERL_NIF_DIRTY_JOB_IO_BOUND} +}; + + +#if !defined(__WIN32__) +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, SOCKET_GLOBAL_DEBUG_DEFAULT); +} + +static +BOOLEAN_T extract_iow(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 iow = MKA(env, "iow"); + + return esock_extract_bool_from_map(env, map, iow, SOCKET_NIF_IOW_DEFAULT); +} +#endif // if !defined(__WIN32__) + + + +/* ======================================================================= + * 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) +{ +#if !defined(__WIN32__) + 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); + + /* +++ Global Counters +++ */ + 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; +#endif + + /* +++ Local atoms and error reason atoms +++ */ +#define LOCAL_ATOM_DECL(A) atom_##A = MKA(env, #A) +LOCAL_ATOMS +LOCAL_ERROR_REASON_ATOMS +#undef LOCAL_ATOM_DECL + + /* Global atom(s) and error reason atom(s) */ +#define GLOBAL_ATOM_DECL(A) esock_atom_##A = MKA(env, #A) +GLOBAL_ATOMS +GLOBAL_ERROR_REASON_ATOMS +#undef GLOBAL_ATOM_DECL + esock_atom_socket_tag = MKA(env, "$socket"); + + 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) diff --git a/erts/emulator/nifs/common/socket_tarray.c b/erts/emulator/nifs/common/socket_tarray.c new file mode 100644 index 0000000000..def22c4919 --- /dev/null +++ b/erts/emulator/nifs/common/socket_tarray.c @@ -0,0 +1,143 @@ +/* + * %CopyrightBegin% + * + * 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. + * 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. + * ---------------------------------------------------------------------- + * + */ + +/* #ifdef HAVE_CONFIG_H */ +/* #include "config.h" */ +/* #endif */ + +#include <stdio.h> + +#include <erl_nif.h> + +#include "socket_int.h" +#include <sys.h> +#include "socket_util.h" +#include "socket_tarray.h" + + + +/* ---------------------------------------------------------------------- + * Types + */ + +typedef struct { + Uint32 sz; + Uint32 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 needs); + + +/* ---------------------------------------------------------------------- + * API + */ + +extern +void* esock_tarray_create(Uint32 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 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 needs) +{ + if (taP->sz < (taP->idx + needs)) { + Uint32 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..4f1152fb9e --- /dev/null +++ b/erts/emulator/nifs/common/socket_tarray.h @@ -0,0 +1,47 @@ +/* + * %CopyrightBegin% + * + * 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. + * 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 sz); +extern void esock_tarray_delete(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, + 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 new file mode 100644 index 0000000000..5e18355308 --- /dev/null +++ b/erts/emulator/nifs/common/socket_util.c @@ -0,0 +1,1670 @@ +/* + * %CopyrightBegin% + * + * 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. + * 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 <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <time.h> +#include <stddef.h> + +#include "socket_int.h" +#include "sys.h" +#include "socket_util.h" +#include "socket_dbg.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??? */ + +#if defined(CLOCK_REALTIME) +static int realtime(struct timespec* tsP); +static int timespec2str(char *buf, + unsigned int len, + struct timespec *ts); +#endif + +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_encode_iov +++ + * + * 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 + * 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, + ErlNifBinary* data, + ERL_NIF_TERM* eIOV) +{ + int rem = read; + Uint16 i; + 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 */ + UDBG( ("SUTIL", "esock_encode_iov -> exact => done\r\n") ); + a[i] = MKBIN(env, &data[i]); + 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; + } 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); + rem = 0; // Besserwisser + done = TRUE; + } + } + + 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; +} + + + +/* +++ 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 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 = (caddr_t) 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 + * 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; + + 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, + &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_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; + + 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); + 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: + *eSockAddr = esock_atom_undefined; + 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 (beside the mandatory family + * attribute, which is "inherited" from the "sockaddr" type): + * + * 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; + char* xres; + + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> entry\r\n") ); + + /* 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 */ + 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 */ + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try decode (ip) address\r\n") ); + 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; +} + + + +/* +++ 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; + + UDBG( ("SUTIL", "esock_encode_sockaddr_in4 -> entry\r\n") ); + + 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 { + 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; + } + + 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 (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) + * + * 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; + 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 + 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; + + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> port: %d\r\n", port) ); + + 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; + + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> flowinfo: %d\r\n", flowInfo) ); + + 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; + + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> scopeId: %d\r\n", scopeId) ); + + 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 ((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 + + + +/* +++ 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 + * (beside the mandatory family attribute, which is "inherited" from + * the "sockaddr" type): + * + * 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 + + + +/* +++ 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: + * + * + Then atom 'any' + * + Then atom 'loopback' + * + An ip4_address() (4 tuple) + * + * Note that this *only* decodes the "address" part of a + * (IPv4) socket address. + */ + +extern +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' */ + + 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; + } + + inAddrP->s_addr = addr.s_addr; + + } 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(inAddrP, &addr, sizeof(addr)); + + } + + return NULL; +} + + + +/* +++ 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; + ERL_NIF_TERM addr; + + /* The address */ + for (i = 0; i < atLen; i++) { + at[i] = MKI(env, a[i]); + } + + 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; +} + + + +/* +++ esock_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 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; + + 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; + } + + *inAddrP = *addr; + + } else { + /* This is a 8-tuple */ + + const ERL_NIF_TERM* addrt; + int addrtSz; + 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 (ai = 0; ai < 8; ai++) { + if (!GET_INT(env, addrt[ai], &v)) + return ESOCK_STR_EINVAL; + put_int16(v, a); + a += 2; + } + + sys_memcpy(inAddrP, &addr, addrLen); + + } + + return NULL; +} +#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_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: + * + * 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_type +++ + * + * 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; +} + + + +/* +++ esock_encode_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) { +#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) { + *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_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 + * 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_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. + * + */ +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} + * + * 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)); +} + + + +/* 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(); +} + + + +/* *** 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... +#if defined(CLOCK_REALTIME) + char stamp[64]; // Just in case... + struct timespec ts; +#endif + 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 defined(CLOCK_REALTIME) + if (!realtime(&ts) && + (timespec2str(stamp, sizeof(stamp), &ts) == 0)) { + res = enif_snprintf(f, sizeof(f), + "=WARNING MSG==== %s ===\r\n%s", + stamp, format); + } else { + res = enif_snprintf(f, sizeof(f), "=WARNING MSG==== %s", format); + } +#else + res = enif_snprintf(f, sizeof(f), "=WARNING MSG==== %s", format); +#endif + + if (res > 0) { + va_start (args, format); + enif_vfprintf (stdout, f, args); + va_end (args); + fflush(stdout); + } + + return; +} + + +#if defined(CLOCK_REALTIME) +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; +} +#endif + + +/* =================================================================== * + * * + * Various (internal) 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 new file mode 100644 index 0000000000..1b5d003155 --- /dev/null +++ b/erts/emulator/nifs/common/socket_util.h @@ -0,0 +1,205 @@ +/* + * %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 <erl_nif.h> +#include "socket_int.h" + +#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))) + +extern +char* esock_encode_iov(ErlNifEnv* env, + int read, + struct iovec* iov, + size_t len, + 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, + 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 +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 +extern +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 +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, + ERL_NIF_TERM* eAddr); + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +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, + 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, + 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 +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 +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, + 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); + +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); +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); + +extern +void esock_warning_msg(const char* format, ... ); + + +#endif // SOCKET_UTIL_H__ |