erts/emulator/nifs/common/socket_util.c (1,189 lines of code) (raw):
/*
* %CopyrightBegin%
*
* Copyright Ericsson AB 2018-2020. 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
#if defined(HAVE_SCTP_H)
#include <netinet/sctp.h>
#ifndef HAVE_SCTP
# define HAVE_SCTP
#endif
#endif
#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 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 void esock_encode_packet_addr_tuple(ErlNifEnv* env,
unsigned char len,
unsigned char* addr,
ERL_NIF_TERM* eAddr);
static void esock_encode_sockaddr_unknown(ErlNifEnv* env,
struct sockaddr* sa,
unsigned int len,
ERL_NIF_TERM* eSockAddr);
static void esock_encode_sockaddr_broken(ErlNifEnv* env,
struct sockaddr* sa,
unsigned int len,
ERL_NIF_TERM* eSockAddr);
static void make_sockaddr_in(ErlNifEnv* env,
ERL_NIF_TERM port,
ERL_NIF_TERM addr,
ERL_NIF_TERM* sa);
static void 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 void make_sockaddr_un(ErlNifEnv* env,
ERL_NIF_TERM path,
ERL_NIF_TERM* sa);
#if defined(HAVE_NETPACKET_PACKET_H)
static void make_sockaddr_ll(ErlNifEnv* env,
ERL_NIF_TERM proto,
ERL_NIF_TERM ifindex,
ERL_NIF_TERM hatype,
ERL_NIF_TERM pkttype,
ERL_NIF_TERM addr,
ERL_NIF_TERM* sa);
#endif
/* *** esock_get_bool_from_map ***
*
* Simple utility function used to extract a boolean value from a map.
* If it fails to extract the value (for whatever reason) the default
* value is returned.
*/
extern
BOOLEAN_T esock_get_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;
} else {
if (COMPARE(val, esock_atom_true) == 0)
return TRUE;
else if (COMPARE(val, esock_atom_false) == 0)
return FALSE;
else
return def;
}
}
/* +++ 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
* continue. 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
void 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;
}
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") );
}
/* +++ 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
BOOLEAN_T 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 FALSE;
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 FALSE;
}
list = tail;
}
*totSize = sz;
UDBG( ("SUTIL", "esock_decode_iov -> done (%d)\r\n", sz) );
return TRUE;
}
/* +++ 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
BOOLEAN_T esock_decode_sockaddr(ErlNifEnv* env,
ERL_NIF_TERM eSockAddr,
ESockAddress* sockAddrP,
unsigned int* addrLen)
{
ERL_NIF_TERM efam;
int fam;
UDBG( ("SUTIL", "esock_decode_sockaddr -> entry\r\n") );
if (!IS_MAP(env, eSockAddr))
return FALSE;
if (!GET_MAP_VAL(env, eSockAddr, esock_atom_family, &efam))
return FALSE;
UDBG( ("SUTIL",
"esock_decode_sockaddr -> try decode domain (%T)\r\n",
efam) );
if (! esock_decode_domain(env, efam, &fam))
return FALSE;
UDBG( ("SUTIL", "esock_decode_sockaddr -> fam: %d\r\n", fam) );
switch (fam) {
case AF_INET:
return esock_decode_sockaddr_in(env, eSockAddr,
&sockAddrP->in4, addrLen);
#if defined(HAVE_IN6) && defined(AF_INET6)
case AF_INET6:
return esock_decode_sockaddr_in6(env, eSockAddr,
&sockAddrP->in6, addrLen);
#endif
#ifdef HAS_AF_LOCAL
case AF_LOCAL:
return esock_decode_sockaddr_un(env, eSockAddr,
&sockAddrP->un, addrLen);
#endif
default:
return FALSE;
}
}
/* +++ 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
* packet - sockaddr_ll: protocol, ifindex, hatype, pkttype, addr
*/
extern
void esock_encode_sockaddr(ErlNifEnv* env,
ESockAddress* sockAddrP,
unsigned int addrLen,
ERL_NIF_TERM* eSockAddr)
{
// Sanity check
if (addrLen < (char *)&sockAddrP->sa.sa_data - (char *)sockAddrP) {
// We got crap, cannot even know the address family
esock_encode_sockaddr_broken(env, &sockAddrP->sa, addrLen, eSockAddr);
return;
}
UDBG( ("SUTIL", "esock_encode_sockaddr -> entry with"
"\r\n family: %d"
"\r\n addrLen: %d"
"\r\n", sockAddrP->ss.ss_family, addrLen) );
switch (sockAddrP->ss.ss_family) {
case AF_INET:
esock_encode_sockaddr_in(env, &sockAddrP->in4, addrLen,
eSockAddr);
break;
#if defined(HAVE_IN6) && defined(AF_INET6)
case AF_INET6:
esock_encode_sockaddr_in6(env, &sockAddrP->in6, addrLen,
eSockAddr);
break;
#endif
#ifdef HAS_AF_LOCAL
case AF_LOCAL:
esock_encode_sockaddr_un(env, &sockAddrP->un, addrLen,
eSockAddr);
break;
#endif
#if defined(HAVE_NETPACKET_PACKET_H)
case AF_PACKET:
esock_encode_sockaddr_ll(env, &sockAddrP->ll, addrLen, eSockAddr);
break;
#endif
default:
esock_encode_sockaddr_unknown(env, &sockAddrP->sa, addrLen, eSockAddr);
break;
}
}
/* +++ esock_decode_sockaddr_in +++
*
* 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
BOOLEAN_T esock_decode_sockaddr_in(ErlNifEnv* env,
ERL_NIF_TERM eSockAddr,
struct sockaddr_in* sockAddrP,
unsigned int* addrLen)
{
ERL_NIF_TERM eport, eaddr;
int port;
UDBG( ("SUTIL", "esock_decode_sockaddr_in -> 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_in -> try get port number\r\n") );
if (! GET_MAP_VAL(env, eSockAddr, esock_atom_port, &eport))
return FALSE;
/* Decode port number */
UDBG( ("SUTIL", "esock_decode_sockaddr_in -> try decode port number\r\n") );
if (! GET_INT(env, eport, &port))
return FALSE;
sockAddrP->sin_port = htons(port);
/* Extract (e) address from map */
UDBG( ("SUTIL", "esock_decode_sockaddr_in -> try get (ip) address\r\n") );
if (! GET_MAP_VAL(env, eSockAddr, esock_atom_addr, &eaddr))
return FALSE;
/* Decode address */
UDBG( ("SUTIL", "esock_decode_sockaddr_in -> try decode (ip) address\r\n") );
if (! esock_decode_in_addr(env,
eaddr,
&sockAddrP->sin_addr))
return FALSE;
*addrLen = sizeof(struct sockaddr_in);
UDBG( ("SUTIL", "esock_decode_sockaddr_in -> done\r\n") );
return TRUE;
}
/* +++ esock_encode_sockaddr_in +++
*
* 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
void esock_encode_sockaddr_in(ErlNifEnv* env,
struct sockaddr_in* sockAddrP,
unsigned int addrLen,
ERL_NIF_TERM* eSockAddr)
{
ERL_NIF_TERM ePort, eAddr;
int port;
UDBG( ("SUTIL", "esock_encode_sockaddr_in -> entry\r\n") );
if (addrLen >= sizeof(struct sockaddr_in)) {
/* The port */
port = ntohs(sockAddrP->sin_port);
ePort = MKI(env, port);
/* The address */
esock_encode_in_addr(env, &sockAddrP->sin_addr, &eAddr);
/* And finally construct the in4_sockaddr record */
make_sockaddr_in(env, ePort, eAddr, eSockAddr);
} else {
UDBG( ("SUTIL", "esock_encode_sockaddr_in -> wrong size: "
"\r\n addrLen: %d"
"\r\n addr size: %d"
"\r\n", addrLen, sizeof(struct sockaddr_in)) );
esock_encode_sockaddr_unknown(env, (struct sockaddr *)sockAddrP,
addrLen, eSockAddr);
}
}
/* +++ 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
BOOLEAN_T 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;
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 FALSE;
/* Decode port number */
if (! GET_INT(env, eport, &port))
return FALSE;
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 FALSE;
/* 4: Get the flowinfo */
if (! GET_UINT(env, eflowInfo, &flowInfo))
return FALSE;
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 FALSE;
/* *** Get the scope_id *** */
if (! GET_UINT(env, escopeId, &scopeId))
return FALSE;
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 FALSE;
/* Decode address */
if (!esock_decode_in6_addr(env,
eaddr,
&sockAddrP->sin6_addr))
return FALSE;
*addrLen = sizeof(struct sockaddr_in6);
UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> done\r\n") );
return TRUE;
}
#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
void esock_encode_sockaddr_in6(ErlNifEnv* env,
struct sockaddr_in6* sockAddrP,
unsigned int addrLen,
ERL_NIF_TERM* eSockAddr)
{
ERL_NIF_TERM ePort, eAddr, eFlowInfo, eScopeId;
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 */
esock_encode_in6_addr(env, &sockAddrP->sin6_addr, &eAddr);
/* And finally construct the in6_sockaddr record */
make_sockaddr_in6(env, ePort, eAddr,
eFlowInfo, eScopeId, eSockAddr);
} else {
esock_encode_sockaddr_unknown(env, (struct sockaddr *)sockAddrP,
addrLen, eSockAddr);
}
}
#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 HAS_AF_LOCAL
extern
BOOLEAN_T 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 FALSE;
/* Get the path */
if (! GET_BIN(env, epath, &bin))
return FALSE;
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 FALSE;
sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_un));
sockAddrP->sun_family = AF_LOCAL;
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 TRUE;
}
#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 HAS_AF_LOCAL
extern
void 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;
if (addrLen >= offsetof(struct sockaddr_un, sun_path)) {
n = addrLen - offsetof(struct sockaddr_un, sun_path);
if (255 < n) {
/* It would be dangerous to create a binary
* based on a presumably bad addrLen
*/
*eSockAddr = esock_atom_bad_data;
} else {
unsigned char *path;
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 */
path = enif_make_new_binary(env, m, &ePath);
ESOCK_ASSERT( path != NULL );
sys_memcpy(path, sockAddrP->sun_path, m);
/* And the socket address */
make_sockaddr_un(env, ePath, eSockAddr);
}
} else {
esock_encode_sockaddr_unknown(env, (struct sockaddr *)sockAddrP,
addrLen, eSockAddr);
}
}
#endif
/* +++ esock_encode_sockaddr_ll +++
*
* Encode a PACKET address - sockaddr_ll (link layer). In erlang it's
* represented as a map, which has a specific set of attributes
* (beside the mandatory family attribute, which is "inherited" from
* the "sockaddr" type):
*
* protocol: integer() (should be an atom really)
* ifindex: integer()
* hatype: integer() (should be an atom really)
* pkttype: integer() (should be an atom really)
* addr: list() (should be something usefull...)
*
*/
#if defined(HAVE_NETPACKET_PACKET_H)
extern
void esock_encode_sockaddr_ll(ErlNifEnv* env,
struct sockaddr_ll* sockAddrP,
unsigned int addrLen,
ERL_NIF_TERM* eSockAddr)
{
ERL_NIF_TERM eProto, eIfIdx, eHaType, ePktType, eAddr;
if (addrLen >= sizeof(struct sockaddr_ll)) {
/* protocol - the standard ethernet protocol type */
esock_encode_packet_protocol(env, ntohs(sockAddrP->sll_protocol),
&eProto);
/* ifindex - the interface index of the interface */
eIfIdx = MKI(env, sockAddrP->sll_ifindex);
/* hatype - is an ARP (hardware) type */
esock_encode_packet_hatype(env, sockAddrP->sll_hatype, &eHaType);
/* pkttype - the packet type */
esock_encode_packet_pkttype(env, sockAddrP->sll_pkttype, &ePktType);
/* addr - the physical-layer (e.g., IEEE 802.3) address */
esock_encode_packet_addr(env,
sockAddrP->sll_halen, sockAddrP->sll_addr,
&eAddr);
make_sockaddr_ll(env,
eProto, eIfIdx, eHaType, ePktType, eAddr,
eSockAddr);
} else {
esock_encode_sockaddr_unknown(env, (struct sockaddr *)sockAddrP,
addrLen, eSockAddr);
}
}
#endif
/* +++ esock_decode_in_addr +++
*
* Decode an 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
BOOLEAN_T esock_decode_in_addr(ErlNifEnv* env,
ERL_NIF_TERM eAddr,
struct in_addr* inAddrP)
{
struct in_addr addr;
UDBG( ("SUTIL", "esock_decode_in_addr -> entry with"
"\r\n eAddr: %T"
"\r\n", eAddr) );
if (IS_ATOM(env, eAddr)) {
/* This is either 'any' | 'broadcast' | 'loopback' */
if (COMPARE(esock_atom_loopback, eAddr) == 0) {
UDBG( ("SUTIL",
"esock_decode_in_addr -> address: loopback\r\n") );
addr.s_addr = htonl(INADDR_LOOPBACK);
} else if (COMPARE(esock_atom_any, eAddr) == 0) {
UDBG( ("SUTIL",
"esock_decode_in_addr -> address: any\r\n") );
addr.s_addr = htonl(INADDR_ANY);
} else if (COMPARE(esock_atom_broadcast, eAddr) == 0) {
UDBG( ("SUTIL",
"esock_decode_in_addr -> address: broadcast\r\n") );
addr.s_addr = htonl(INADDR_BROADCAST);
} else {
UDBG( ("SUTIL",
"esock_decode_in_addr -> address: unknown\r\n") );
return FALSE;
}
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 FALSE;
if (addrtSz != 4)
return FALSE;
for (a = 0; a < 4; a++) {
if (! GET_INT(env, addrt[a], &v))
return FALSE;
if (v < 0 || 255 < v)
return FALSE;
addr[a] = v;
}
sys_memcpy(inAddrP, &addr, sizeof(addr));
}
return TRUE;
}
/* +++ esock_encode_in_addr +++
*
* Encode an 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
void esock_encode_in_addr(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_in_addr -> addr: %T\r\n", addr) );
// *eAddr = MKTA(env, at, atLen);
*eAddr = addr;
}
/* +++ esock_decode_in6_addr +++
*
* Decode an 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
BOOLEAN_T esock_decode_in6_addr(ErlNifEnv* env,
ERL_NIF_TERM eAddr,
struct in6_addr* inAddrP)
{
UDBG( ("SUTIL", "esock_decode_in6_addr -> 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 FALSE;
}
*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 FALSE;
if (addrtSz != 8)
return FALSE;
for (ai = 0; ai < 8; ai++) {
if (! GET_INT(env, addrt[ai], &v))
return FALSE;
if (v < 0 || 65535 < v)
return FALSE;
put_int16(v, a);
a += 2;
}
sys_memcpy(inAddrP, &addr, addrLen);
}
return TRUE;
}
#endif
/* +++ esock_encode_in6_addr +++
*
* Encode an 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
void esock_encode_in6_addr(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);
}
#endif
/* +++ esock_encode_timeval +++
*
* Encode a timeval struct into its erlang form, a map with two fields:
*
* sec
* usec
*
*/
extern
void 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 );
ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eTime) );
}
/* +++ esock_decode_timeval +++
*
* Decode a timeval in its erlang form (a map) into its native form,
* a timeval struct.
*
*/
extern
BOOLEAN_T esock_decode_timeval(ErlNifEnv* env,
ERL_NIF_TERM eTime,
struct timeval* timeP)
{
ERL_NIF_TERM eSec, eUSec;
if (! GET_MAP_VAL(env, eTime, esock_atom_sec, &eSec))
return FALSE;
if (! GET_MAP_VAL(env, eTime, esock_atom_usec, &eUSec))
return FALSE;
/* Use the appropriate variable type and nif function
* to decode the value from Erlang into the struct timeval fields
*/
{ /* time_t tv_sec; */
#if (SIZEOF_TIME_T == 8)
ErlNifSInt64 sec;
if (! GET_INT64(env, eSec, &sec))
return FALSE;
#elif (SIZEOF_TIME_T == SIZEOF_INT)
int sec;
if (! GET_INT(env, eSec, &sec))
return FALSE;
#else /* long or other e.g undefined */
long sec;
if (! GET_LONG(env, eSec, &sec))
return FALSE;
#endif
timeP->tv_sec = sec;
}
{ /* suseconds_t tv_usec; */
#if (SIZEOF_SUSECONDS_T == 8)
ErlNifSInt64 usec;
if (! GET_INT64(env, eSec, &usec))
return FALSE;
#elif (SIZEOF_SUSECONDS_T == SIZEOF_INT)
int usec;
if (! GET_INT(env, eSec, &usec))
return FALSE;
#else /* long or other e.g undefined */
long usec;
if (! GET_LONG(env, eSec, &usec))
return FALSE;
#endif
timeP->tv_usec = usec;
}
return TRUE;
}
/* +++ esock_decode_domain +++
*
* Decode the Erlang form of the 'domain' type, that is:
*
* inet => AF_INET
* inet6 => AF_INET6
* local => AF_LOCAL
*
*/
extern
BOOLEAN_T esock_decode_domain(ErlNifEnv* env,
ERL_NIF_TERM eDomain,
int* domain)
{
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 HAS_AF_LOCAL
} else if (COMPARE(esock_atom_local, eDomain) == 0) {
*domain = AF_LOCAL;
#endif
} else {
int d = 0;
if (GET_INT(env, eDomain, &d))
*domain = d;
else
return FALSE;
}
return TRUE;
}
/* +++ esock_encode_domain +++
*
* Encode the native domain to the Erlang form, that is:
*
* AF_INET => inet
* AF_INET6 => inet6
* AF_LOCAL => local
*
*/
extern
void esock_encode_domain(ErlNifEnv* env,
int domain,
ERL_NIF_TERM* eDomain)
{
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 HAS_AF_LOCAL
case AF_LOCAL:
*eDomain = esock_atom_local;
break;
#endif
default:
*eDomain = MKI(env, domain);
}
}
/* +++ 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
BOOLEAN_T esock_decode_type(ErlNifEnv* env,
ERL_NIF_TERM eType,
int* type)
{
int cmp;
/* A manual binary search to minimize the number of COMPARE:s */
cmp = COMPARE(esock_atom_raw, eType);
if (cmp < 0) {
if (COMPARE(esock_atom_stream, eType) == 0) {
*type = SOCK_STREAM;
#ifdef SOCK_SEQPACKET
} else if (COMPARE(esock_atom_seqpacket, eType) == 0) {
*type = SOCK_SEQPACKET;
#endif
} else
goto integer;
} else if (0 < cmp) {
if (COMPARE(esock_atom_dgram, eType) == 0) {
*type = SOCK_DGRAM;
} else
goto integer;
} else
*type = SOCK_RAW;
return TRUE;
integer:
{
int t = 0;
if (GET_INT(env, eType, &t)) {
*type = t;
return TRUE;
}
}
return FALSE;
}
/* +++ esock_encode_type +++
*
* Encode the native type to the Erlang form, that is:
*
* SOCK_STREAM => stream
* SOCK_DGRAM => dgram
* SOCK_RAW => raw
* SOCK_SEQPACKET => seqpacket
*
*/
extern
void esock_encode_type(ErlNifEnv* env,
int type,
ERL_NIF_TERM* eType)
{
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;
#ifdef SOCK_SEQPACKET
case SOCK_SEQPACKET:
*eType = esock_atom_seqpacket;
break;
#endif
#ifdef SOCK_RDM
case SOCK_RDM:
*eType = esock_atom_rdm;
break;
#endif
default:
*eType = MKI(env, type);
}
}
/* +++ esock_encode_packet_protocol +++
*
* Encode the Link Layer sockaddr protocol.
*
* Currently we just represent this as an unsigned int.
*/
extern
void esock_encode_packet_protocol(ErlNifEnv* env,
unsigned short protocol,
ERL_NIF_TERM* eProtocol)
{
*eProtocol = MKUI(env, protocol);
}
/* +++ esock_encode_packet_hatype +++
*
* Encode the Link Layer sockaddr hatype.
*
* Currently we just represent this as an unsigned int.
*/
extern
void esock_encode_packet_hatype(ErlNifEnv* env,
unsigned short hatype,
ERL_NIF_TERM* eHaType)
{
*eHaType = MKUI(env, hatype);
}
/* +++ esock_encode_packet_pkttype +++
*
* Encode the Link Layer sockaddr pkttype.
*
* PACKET_HOST => host
* PACKET_BROADCAST => broadcast
* PACKET_MULTICAST => multicast
* PACKET_OTHERHOST => otherhost
* PACKET_OUTGOING => outgoing
* PACKET_LOOPBACK => loopback
* PACKET_USER => user
* PACKET_KERNEL => kernel
*
*/
extern
void esock_encode_packet_pkttype(ErlNifEnv* env,
unsigned short pkttype,
ERL_NIF_TERM* ePktType)
{
switch (pkttype) {
#if defined(PACKET_HOST)
case PACKET_HOST:
*ePktType = esock_atom_host;
break;
#endif
#if defined(PACKET_BROADCAST)
case PACKET_BROADCAST:
*ePktType = esock_atom_broadcast;
break;
#endif
#if defined(PACKET_MULTICAST)
case PACKET_MULTICAST:
*ePktType = esock_atom_multicast;
break;
#endif
#if defined(PACKET_OTHERHOST)
case PACKET_OTHERHOST:
*ePktType = esock_atom_otherhost;
break;
#endif
#if defined(PACKET_OUTGOING)
case PACKET_OUTGOING:
*ePktType = esock_atom_outgoing;
break;
#endif
/* Unused? Not user space? */
#if defined(PACKET_LOOPBACK)
case PACKET_LOOPBACK:
*ePktType = esock_atom_loopback;
break;
#endif
#if defined(PACKET_USER)
case PACKET_USER:
*ePktType = esock_atom_user;
break;
#endif
#if defined(PACKET_KERNEL)
case PACKET_KERNEL:
*ePktType = esock_atom_kernel;
break;
#endif
/* Unused? Not user space?
* Also, has the same value as PACKET_USER,
* so may result in a compiler error (at least
* on some platforms: ANDROID).
*
#if defined(PACKET_FASTROUTE)
case PACKET_FASTROUTE:
*ePktType = esock_atom_fastroute;
break;
#endif
*/
default:
*ePktType = MKUI(env, pkttype);
break;
}
}
/* +++ esock_encode_packet_addr +++
*
* Encode the Link Layer sockaddr address.
*
*/
extern
void esock_encode_packet_addr(ErlNifEnv* env,
unsigned char len,
unsigned char* addr,
ERL_NIF_TERM* eAddr)
{
#if defined(ESOCK_PACKET_ADDRESS_AS_TUPLE)
esock_encode_packet_addr_tuple(env, len, addr, eAddr);
#else
SOCKOPTLEN_T vsz = len;
ErlNifBinary val;
if (ALLOC_BIN(vsz, &val)) {
sys_memcpy(val.data, addr, len);
*eAddr = MKBIN(env, &val);
} else {
esock_encode_packet_addr_tuple(env, len, addr, eAddr);
}
#endif
}
static
void esock_encode_packet_addr_tuple(ErlNifEnv* env,
unsigned char len,
unsigned char* addr,
ERL_NIF_TERM* eAddr)
{
ERL_NIF_TERM array[len];
unsigned char i;
for (i = 0; i < len; i++) {
array[i] = MKUI(env, addr[i]);
}
*eAddr = MKTA(env, array, len);
}
/* Encode as #{family := integer(), addr := binary()}
* assuming at least the ->family field can be accessed
* and hence at least 0 bytes of address
*/
static
void esock_encode_sockaddr_unknown(ErlNifEnv* env,
struct sockaddr* addr,
unsigned int len,
ERL_NIF_TERM* eSockAddr)
{
size_t size;
ERL_NIF_TERM eFamily, eData;
size = ((char*)addr + len) - (char*)&addr->sa_data;
eFamily = MKI(env, addr->sa_family);
eData = esock_make_new_binary(env, &addr->sa_data, size);
{
ERL_NIF_TERM keys[] = {esock_atom_family, esock_atom_addr};
ERL_NIF_TERM vals[] = {eFamily, eData};
size_t numKeys = sizeof(keys) / sizeof(*keys);
size_t numVals = sizeof(vals) / sizeof(*vals);
ESOCK_ASSERT( numKeys == numVals );
ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eSockAddr) );
}
}
/* Encode as a raw binary() regarding the whole address
* structure as a blob
*/
static void esock_encode_sockaddr_broken(ErlNifEnv* env,
struct sockaddr* addr,
unsigned int len,
ERL_NIF_TERM* eSockAddr) {
*eSockAddr = esock_make_new_binary(env, addr, len);
}
/* +++ esock_decode_bufsz +++
*
* Decode an buffer size. The size of a buffer is:
*
* eVal > 0 => Use provided value
* eVal == 'default' => Use provided default
*
*/
extern
BOOLEAN_T esock_decode_bufsz(ErlNifEnv* env,
ERL_NIF_TERM eVal,
size_t defSz,
size_t* szp)
{
unsigned long val;
if (GET_ULONG(env, eVal, &val)) {
/* Check value */
defSz = (size_t) val;
if (val != (unsigned long) defSz || val == 0)
return FALSE;
} else {
if (COMPARE(eVal, esock_atom_default) != 0)
return FALSE;
}
*szp = defSz;
return TRUE;
}
/* *** 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_pid_from_map ***
*
* Extract a pid item from a map.
* Returns TRUE on success and FALSE on failure.
*
*/
extern
BOOLEAN_T esock_extract_pid_from_map(ErlNifEnv* env,
ERL_NIF_TERM map,
ERL_NIF_TERM key,
ErlNifPid* pid)
{
ERL_NIF_TERM val;
BOOLEAN_T res;
if (! GET_MAP_VAL(env, map, key, &val))
return FALSE;
res = enif_get_local_pid(env, val, pid);
return res;
}
/* *** esock_extract_int_from_map ***
*
* Simple utility function used to extract a integer value from a map.
*/
extern
BOOLEAN_T esock_extract_int_from_map(ErlNifEnv* env,
ERL_NIF_TERM map,
ERL_NIF_TERM key,
int* val)
{
ERL_NIF_TERM eval;
BOOLEAN_T ret;
if (! GET_MAP_VAL(env, map, key, &eval))
return FALSE;
ret = GET_INT(env, eval, val);
return ret;
}
/* *** esock_decode_bool ***
*
* Decode a boolean value.
*
*/
extern
BOOLEAN_T esock_decode_bool(ERL_NIF_TERM eVal, BOOLEAN_T* val)
{
if (COMPARE(esock_atom_true, eVal) == 0)
*val = TRUE;
else if (COMPARE(esock_atom_false, eVal) == 0)
*val = FALSE;
else
return FALSE;
return TRUE;
}
/* *** 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;
}
/* *** esock_decode_level ***
*
* Decode option or cmsg level - 'socket' or protocol number.
*
*/
extern
BOOLEAN_T esock_decode_level(ErlNifEnv* env, ERL_NIF_TERM eVal, int *val)
{
if (COMPARE(esock_atom_socket, eVal) == 0)
*val = SOL_SOCKET;
else if (! GET_INT(env, eVal, val))
return FALSE;
return TRUE;
}
/* *** esock_encode_level ***
*
* Encode option or cmsg level - SOL_SOCKET or protocol number.
*
*/
extern
ERL_NIF_TERM esock_encode_level(ErlNifEnv* env, int level)
{
if (level == SOL_SOCKET)
return esock_atom_socket;
else
return MKI(env, level);
}
/* 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));
}
/* Raise an exception {invalid, {What, Info}}
*/
extern
ERL_NIF_TERM esock_raise_invalid(ErlNifEnv* env,
ERL_NIF_TERM what, ERL_NIF_TERM info)
{
return enif_raise_exception(env,
MKT2(env,
esock_atom_invalid,
MKT2(env, what, info)));
}
/* 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_self ***
*
* This function returns the current pid (self) in term form,
* or the atom undefined if not executed in the context of an (erlang) process.
*/
extern
ERL_NIF_TERM esock_self(ErlNifEnv* env)
{
ErlNifPid pid;
/* Make an idiot test first just to ensure we don't kill ourselves */
if (env == NULL)
return esock_atom_undefined;
else if (enif_self(env, &pid) == NULL)
return esock_atom_undefined;
else
return enif_make_pid(env, &pid);
}
/* *** 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_str(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.
* 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
ErlNifTime esock_timestamp()
{
ErlNifTime monTime = enif_monotonic_time(ERL_NIF_USEC);
ErlNifTime offTime = enif_time_offset(ERL_NIF_USEC);
return (monTime + offTime);
}
/* *** esock_timestamp_str ***
*
* 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_str(char *buf, unsigned int len)
{
return esock_format_timestamp(esock_timestamp(), buf, len);
}
/* *** esock_format_timestamp ***
*
* Format a timestamp.
* 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_format_timestamp(ErlNifTime timestamp, char *buf, unsigned int len)
{
unsigned ret;
#if defined(ESOCK_USE_PRETTY_TIMESTAMP)
time_t sec = timestamp / 1000000; // (if _MSEC) sec = time / 1000;
time_t usec = timestamp % 1000000; // (if _MSEC) msec = time % 1000;
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;
buf += ret;
ret = enif_snprintf(buf, len, ".%06lu", (unsigned long) usec);
if (ret >= len)
return FALSE;
return TRUE;
#else
ret = enif_snprintf(buf, len, "%lu", (unsigned long) timestamp);
if (ret >= len)
return FALSE;
return TRUE;
#endif
}
/* =================================================================== *
* *
* Various (internal) utility functions *
* *
* =================================================================== */
/* Construct the IPv4 socket address */
static
void make_sockaddr_in(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 );
ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, sa) );
}
/* Construct the IPv6 socket address */
static
void 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 );
ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, sa) );
}
/* Construct the Unix Domain socket address */
static
void 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 );
ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, sa) );
}
/* Construct the Link Layer socket address */
#ifdef HAVE_NETPACKET_PACKET_H
static
void make_sockaddr_ll(ErlNifEnv* env,
ERL_NIF_TERM proto,
ERL_NIF_TERM ifindex,
ERL_NIF_TERM hatype,
ERL_NIF_TERM pkttype,
ERL_NIF_TERM addr,
ERL_NIF_TERM* sa)
{
ERL_NIF_TERM keys[] = {esock_atom_family,
esock_atom_protocol,
esock_atom_ifindex,
esock_atom_hatype,
esock_atom_pkttype,
esock_atom_addr};
ERL_NIF_TERM vals[] = {esock_atom_packet,
proto,
ifindex,
hatype,
pkttype,
addr};
unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM);
unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM);
ESOCK_ASSERT( numKeys == numVals );
ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, sa) );
}
#endif
extern
ERL_NIF_TERM esock_make_new_binary(ErlNifEnv *env, void *buf, size_t size) {
ERL_NIF_TERM term;
sys_memcpy(enif_make_new_binary(env, size, &term), buf, size);
return term;
}