/*
* %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"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* 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(HAVE_LOCALTIME_R) && defined(HAVE_STRFTIME))
#define ESOCK_USE_PRETTY_TIMESTAMP 1
#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,
ESockAddress* 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,
ESockAddress* 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 (SIZEOF_INT == 4)
{
int usec;
if (!GET_INT(env, eUSec, &usec))
return ESOCK_STR_EINVAL;
timeP->tv_usec = (typeof(timeP->tv_usec)) usec;
}
#elif (SIZEOF_LONG == 4)
{
long usec;
if (!GET_LONG(env, eUSec, &usec))
return ESOCK_STR_EINVAL;
timeP->tv_usec = (typeof(timeP->tv_usec)) usec;
}
#else
/* Ok, we give up... */
if (!GET_LONG(env, eUSec, &timeP->tv_usec))
return ESOCK_STR_EINVAL;
#endif
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...
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 (esock_timestamp(stamp, sizeof(stamp))) {
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);
}
if (res > 0) {
va_start (args, format);
enif_vfprintf (stdout, f, args);
va_end (args);
fflush(stdout);
}
return;
}
/* *** esock_timestamp ***
*
* Create a timestamp string.
* If awailable, we use the localtime_r and strftime function(s)
* to produces a nice readable timestamp. But if not (awailable),
* it produces a timestamp in the form of an "Epoch" (A real epoch
* is the number of seconds since 1/1 1970, but our timestamp is
* the number micro seconds since 1/1 1970).
*/
extern
BOOLEAN_T esock_timestamp(char *buf, unsigned int len)
{
int ret;
ErlNifTime monTime = enif_monotonic_time(ERL_NIF_USEC);
ErlNifTime offTime = enif_time_offset(ERL_NIF_USEC);
ErlNifTime time = monTime + offTime;
#if defined(ESOCK_USE_PRETTY_TIMESTAMP)
time_t sec = time / 1000000; // (if _MSEC) sec = time / 1000;
time_t usec = time % 1000000; // (if _MSEC) msec = time % 1000;
int buflen;
struct tm t;
if (localtime_r(&sec, &t) == NULL)
return FALSE;
ret = strftime(buf, len, "%d-%B-%Y::%T", &t);
if (ret == 0)
return FALSE;
len -= ret - 1;
buflen = strlen(buf);
ret = enif_snprintf(&buf[buflen], len, ".%06b64d", usec);
if (ret >= len)
return FALSE;
return TRUE;
#else
ret = enif_snprintf(buf, len, "%b64d", time);
if (ret == 0)
return FALSE;
else
return TRUE;
#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_local, 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;
}
}