diff options
Diffstat (limited to 'erts/emulator/nifs/common/socket_util.c')
-rw-r--r-- | erts/emulator/nifs/common/socket_util.c | 1658 |
1 files changed, 1658 insertions, 0 deletions
diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c new file mode 100644 index 0000000000..f6e4781977 --- /dev/null +++ b/erts/emulator/nifs/common/socket_util.c @@ -0,0 +1,1658 @@ +/* + * %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??? */ + +static int realtime(struct timespec* tsP); +static int timespec2str(char *buf, unsigned int len, struct timespec *ts); + +static char* make_sockaddr_in4(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + 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 = 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... + char stamp[64]; // Just in case... + struct timespec ts; + int res; + + /* + * We should really include self in the printout, so we can se which process + * are executing the code. But then I must change the API.... + * ....something for later. + */ + + // 2018-06-29 12:13:21.232089 + // 29-Jun-2018::13:47:25.097097 + + if (!realtime(&ts)) { + if (timespec2str(stamp, sizeof(stamp), &ts) != 0) { + res = enif_snprintf(f, sizeof(f), "=WARNING MSG==== %s", format); + } else { + res = enif_snprintf(f, sizeof(f), + "=WARNING MSG==== %s ===\r\n%s" , stamp, format); + } + + if (res > 0) { + va_start (args, format); + enif_vfprintf (stdout, f, args); + va_end (args); + fflush(stdout); + } + } + + return; +} + + +static +int realtime(struct timespec* tsP) +{ + return clock_gettime(CLOCK_REALTIME, tsP); +} + + +/* + * Convert a timespec struct into a readable/printable string. + * + * "%F::%T" => 2018-06-29 12:13:21[.232089] + * "%d-%b-%Y::%T" => 29-Jun-2018::13:47:25.097097 + */ +static +int timespec2str(char *buf, unsigned int len, struct timespec *ts) +{ + int ret, buflen; + struct tm t; + + tzset(); + if (localtime_r(&(ts->tv_sec), &t) == NULL) + return 1; + + ret = strftime(buf, len, "%d-%B-%Y::%T", &t); + if (ret == 0) + return 2; + len -= ret - 1; + buflen = strlen(buf); + + ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); + if (ret >= len) + return 3; + + return 0; +} + + +/* =================================================================== * + * * + * Various (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; + } +} + + |