From 1b31432a2c60364dc3e7b2a18fa8494475344271 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 15 Jun 2018 18:51:48 +0200 Subject: [socket+net-nif] Introduced a couple of common files Started to move the common stuff, such as common utility functions, debug and encode / decode of basic types. OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 659 ++++++++++++++++++++++++++++++++ 1 file changed, 659 insertions(+) create mode 100644 erts/emulator/nifs/common/socket_util.c (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c new file mode 100644 index 0000000000..818e259ae8 --- /dev/null +++ b/erts/emulator/nifs/common/socket_util.c @@ -0,0 +1,659 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018-2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Utility functions for the socket and net NIF(s). + * ---------------------------------------------------------------------- + * + */ + +#include +#include "socket_int.h" +#include "socket_util.h" +#include "sys.h" + + +/* THIS IS JUST TEMPORARY */ +extern char* erl_errno_id(int error); + + +/* +++ esock_decode_sockaddr +++ + * + * Decode a socket address - sockaddr. In erlang its represented as + * a map, which has a specific set of attributes, depending on one + * mandatory attribute; family. So depending on the value of the family + * attribute: + * + * local - sockaddr_un: path + * inet - sockaddr_in4: port, addr + * inet6 - sockaddr_in6: port, addr, flowinfo, scope_id + */ + +extern +char* esock_decode_sockaddr(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + SocketAddress* sockAddrP, + unsigned int* addrLen) +{ + ERL_NIF_TERM efam; + int fam; + char* xres; + + if (!IS_MAP(env, eSockAddr)) + return ESOCK_STR_EINVAL; + + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_family, &efam)) + return ESOCK_STR_EINVAL; + + if ((xres = esock_decode_domain(env, efam, &fam)) != NULL) + return xres; + + switch (fam) { + case AF_INET: + xres = esock_decode_sockaddr_in4(env, eSockAddr, + &sockAddrP->in4, addrLen); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + xres = esock_decode_sockaddr_in6(env, eSockAddr, + &sockAddrP->in6, addrLen); + break; +#endif + +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + xres = esock_decode_sockaddr_un(env, eSockAddr, + &sockAddrP->un, addrLen); + break; +#endif + + default: + xres = ESOCK_STR_EAFNOSUPPORT; + break; + + } + + return xres; +} + + + +/* +++ esock_decode_sockaddr_in4 +++ + * + * Decode a IPv4 socket address - sockaddr_in4. In erlang its represented as + * a map, which has a specific set of attributes: + * + * port :: port_numbber() + * addr :: ip4_address() + * + * The erlang module ensures that both of these has values exist, so there + * is no need for any elaborate error handling. + */ + +extern +char* esock_decode_sockaddr_in4(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_in* sockAddrP, + unsigned int* addrLen) +{ + ERL_NIF_TERM eport, eaddr; + int port; + + /* Basic init */ + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); + +#ifndef NO_SA_LEN + sockAddrP->sin_len = sizeof(struct sockaddr_in); +#endif + + sockAddrP->sin_family = AF_INET; + + /* Extract (e) port number from map */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_port, &eport)) + return ESOCK_STR_EINVAL; + + /* Decode port number */ + if (!GET_INT(env, eport, &port)) + return ESOCK_STR_EINVAL; + + sockAddrP->sin_port = htons(port); + + /* Extract (e) address from map */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_addr, &eaddr)) + return ESOCK_STR_EINVAL; + + /* Decode address */ + if (!esock_decode_ip4_address(env, eaddr, sockAddrP, addrLen)) + return ESOCK_STR_EINVAL; + + return NULL; +} + + +/* +++ decode_sockaddr_in6 +++ + * + * Decode a IPv6 socket address - sockaddr_in6. In erlang its represented as + * a map, which has a specific set of attributes: + * + * port :: port_numbber() (integer) + * addr :: ip6_address() (tuple) + * flowinfo :: in6_flow_info() (integer) + * scope_id :: in6_scope_id() (integer) + * + * The erlang module ensures that all of these has values exist, so there + * is no need for any elaborate error handling here. + */ + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_decode_sockaddr_in6(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_in6* sockAddrP, + unsigned int* addrLen) +{ + ERL_NIF_TERM eport, eaddr, eflowInfo, escopeId; + int port; + unsigned int flowInfo, scopeId; + + /* Basic init */ + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); +#ifndef NO_SA_LEN + sockAddrP->sin6_len = sizeof(struct sockaddr_in); +#endif + + sockAddrP->sin6_family = AF_INET6; + + /* *** Extract (e) port number from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_port, &eport)) + return ESOCK_STR_EINVAL; + + /* Decode port number */ + if (!GET_INT(env, eport, &port)) + return ESOCK_STR_EINVAL; + + sockAddrP->sin6_port = htons(port); + + /* *** Extract (e) flowinfo from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_flowinfo, &eflowInfo)) + return ESOCK_STR_EINVAL; + + /* 4: Get the flowinfo */ + if (!GET_UINT(env, eflowInfo, &flowInfo)) + return ESOCK_STR_EINVAL; + + sockAddrP->sin6_flowinfo = flowInfo; + + /* *** Extract (e) scope_id from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_scope_id, &escopeId)) + return ESOCK_STR_EINVAL; + + /* *** Get the scope_id *** */ + if (!GET_UINT(env, escopeId, &scopeId)) + return ESOCK_STR_EINVAL; + + sockAddrP->sin6_scope_id = scopeId; + + /* *** Extract (e) address from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_addr, &eaddr)) + return ESOCK_STR_EINVAL; + + /* Decode address */ + if (!esock_decode_ip6_address(env, eaddr, sockAddrP, addrLen)) + return ESOCK_STR_EINVAL; + + return NULL; +} +#endif + + + +/* +++ esock_decode_sockaddr_un +++ + * + * Decode a Unix Domain socket address - sockaddr_un. In erlang its + * represented as a map, which has a specific set of attributes: + * + * path :: binary() + * + * The erlang module ensures that this value exist, so there + * is no need for any elaborate error handling here. + */ + +#ifdef HAVE_SYS_UN_H +extern +char* esock_decode_sockaddr_un(ErlNifEnv* env, + ERL_NIF_TERM eSockAddr, + struct sockaddr_un* sockAddrP, + unsigned int* addrLen) +{ + ErlNifBinary bin; + ERL_NIF_TERM epath; + unsigned int len; + + /* *** Extract (e) path (a binary) from map *** */ + if (!GET_MAP_VAL(env, eSockAddr, esock_atom_path, &epath)) + return ESOCK_STR_EINVAL; + + /* Get the path */ + if (!GET_BIN(env, epath, &bin)) + return ESOCK_STR_EINVAL; + + if ((bin.size + +#ifdef __linux__ + /* Make sure the address gets zero terminated + * except when the first byte is \0 because then it is + * sort of zero terminated although the zero termination + * comes before the address... + * This fix handles Linux's nonportable + * abstract socket address extension. + */ + (bin.data[0] == '\0' ? 0 : 1) +#else + 1 +#endif + ) > sizeof(sockAddrP->sun_path)) + return ESOCK_STR_EINVAL; + + + sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_un)); + sockAddrP->sun_family = AF_UNIX; + + sys_memcpy(sockAddrP->sun_path, bin.data, bin.size); + len = offsetof(struct sockaddr_un, sun_path) + bin.size; + +#ifndef NO_SA_LEN + sockAddrP->sun_len = len; +#endif + *addrLen = len; + + return NULL; +} +#endif + + + +/* +++ decode_ip4_address +++ + * + * Decode a IPv4 address. This can be three things: + * + * + Then atom 'any' + * + Then atom 'loopback' + * + An ip4_address() (4 tuple) + * + * Note that this *only* decodes the "address" part of a + * (IPv4) socket address. There are several other things (port). + */ + +extern +char* esock_decode_ip4_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct sockaddr_in* sockAddrP, + unsigned int* addrLen) +{ + if (IS_ATOM(env, eAddr)) { + /* This is either 'any' or 'loopback' */ + struct in_addr addr; + + if (COMPARE(esock_atom_loopback, eAddr) == 0) { + addr.s_addr = htonl(INADDR_LOOPBACK); + } else if (COMPARE(esock_atom_any, eAddr) == 0) { + addr.s_addr = htonl(INADDR_ANY); + } else { + return ESOCK_STR_EINVAL; + } + + sockAddrP->sin_addr.s_addr = addr.s_addr; + *addrLen = sizeof(struct sockaddr_in); + + } else { + /* This is a 4-tuple */ + + const ERL_NIF_TERM* addrt; + int addrtSz; + int a, v; + char addr[4]; + + if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) + return ESOCK_STR_EINVAL; + + if (addrtSz != 4) + return ESOCK_STR_EINVAL; + + for (a = 0; a < 4; a++) { + if (!GET_INT(env, addrt[a], &v)) + return ESOCK_STR_EINVAL; + addr[a] = v; + } + + sys_memcpy(&sockAddrP->sin_addr, &addr, sizeof(addr)); + *addrLen = sizeof(struct sockaddr_in); + + } + + return NULL; +} + + + +/* +++ decode_ip6_address +++ + * + * Decode a IPv6 address. This can be three things: + * + * + Then atom 'any' + * + Then atom 'loopback' + * + An ip6_address() (8 tuple) + * + * Note that this *only* decodes the "address" part of a + * (IPv6) socket address. There are several other things + * (port, flowinfo and scope_id) that are handled elsewhere). + */ + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_decode_ip6_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct sockaddr_in6* sockAddrP, + unsigned int* addrLen) +{ + if (IS_ATOM(env, eAddr)) { + /* This is either 'any' or 'loopback' */ + const struct in6_addr* addr; + + if (COMPARE(esock_atom_loopback, eAddr) == 0) { + addr = &in6addr_loopback; + } else if (COMPARE(esock_atom_any, eAddr) == 0) { + addr = &in6addr_any; + } else { + return ESOCK_STR_EINVAL; + } + + sockAddrP->sin6_addr = *addr; + *addrLen = sizeof(struct sockaddr_in6); + + } else { + /* This is a 8-tuple */ + + const ERL_NIF_TERM* addrt; + int addrtSz; + int a, v; + char addr[16]; + + if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) + return ESOCK_STR_EINVAL; + + if (addrtSz != 8) + return ESOCK_STR_EINVAL; + + for (a = 0; a < 8; a++) { + if (!GET_INT(env, addrt[a], &v)) + return ESOCK_STR_EINVAL; + addr[a*2 ] = ((v >> 8) & 0xFF); + addr[a*2+1] = (v & 0xFF); + } + + sys_memcpy(&sockAddrP->sin6_addr, &addr, sizeof(addr)); + *addrLen = sizeof(struct sockaddr_in6); + + } + + return NULL; +} +#endif + + +/* +++ esock_decode_domain +++ + * + * Decode the Erlang form of the 'domain' type, that is: + * + * inet => AF_INET + * inet6 => AF_INET6 + * local => AF_UNIX + * + */ +extern +char* esock_decode_domain(ErlNifEnv* env, + ERL_NIF_TERM eDomain, + int* domain) +{ + char* xres = NULL; + + if (COMPARE(esock_atom_inet, eDomain) == 0) { + *domain = AF_INET; + +#if defined(HAVE_IN6) && defined(AF_INET6) + } else if (COMPARE(esock_atom_inet6, eDomain) == 0) { + *domain = AF_INET6; +#endif + +#ifdef HAVE_SYS_UN_H + } else if (COMPARE(esock_atom_local, eDomain) == 0) { + *domain = AF_UNIX; +#endif + + } else { + *domain = -1; + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + +/* +++ esock_encode_domain +++ + * + * Encode the native domain to the Erlang form, that is: + * + * AF_INET => inet + * AF_INET6 => inet6 + * AF_UNIX => local + * + */ +extern +char* esock_encode_domain(ErlNifEnv* env, + int domain, + ERL_NIF_TERM* eDomain) +{ + char* xres = NULL; + + switch (domain) { + case AF_INET: + *eDomain = esock_atom_inet; + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + *eDomain = esock_atom_inet6; + break; +#endif + +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + *eDomain = esock_atom_local; + break; +#endif + + default: + *eDomain = esock_atom_undefined; // Just in case + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + +/* +++ esock_decode_type +++ + * + * Decode the Erlang form of the 'type' type, that is: + * + * stream => SOCK_STREAM + * dgram => SOCK_DGRAM + * raw => SOCK_RAW + * seqpacket => SOCK_SEQPACKET + * + */ +extern +char* esock_decode_type(ErlNifEnv* env, + ERL_NIF_TERM eType, + int* type) +{ + char* xres = NULL; + + if (COMPARE(esock_atom_stream, eType) == 0) { + *type = SOCK_STREAM; + } else if (COMPARE(esock_atom_dgram, eType) == 0) { + *type = SOCK_DGRAM; + } else if (COMPARE(esock_atom_raw, eType) == 0) { + *type = SOCK_RAW; + +#if defined(HAVE_SCTP) + } else if (COMPARE(esock_atom_seqpacket, eType) == 0) { + *type = SOCK_SEQPACKET; +#endif + + } else { + *type = -1; + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + +/* +++ esock_decode_domain +++ + * + * Encode the native type to the Erlang form, that is: + * + * SOCK_STREAM => stream + * SOCK_DGRAM => dgram + * SOCK_RAW => raw + * SOCK_SEQPACKET => seqpacket + * + */ +extern +char* esock_encode_type(ErlNifEnv* env, + int type, + ERL_NIF_TERM* eType) +{ + char* xres = NULL; + + switch (type) { + case SOCK_STREAM: + *eType = esock_atom_stream; + break; + + case SOCK_DGRAM: + *eType = esock_atom_dgram; + break; + + case SOCK_RAW: + *eType = esock_atom_raw; + break; + +#if defined(HAVE_SCTP) + case SOCK_SEQPACKET: + *eType = esock_atom_seqpacket; + break; +#endif + + default: + *eType = esock_atom_undefined; // Just in case + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + +/* Create an ok two (2) tuple in the form: + * + * {ok, Any} + * + * The second element (Any) is already in the form of an + * ERL_NIF_TERM so all we have to do is create the tuple. + */ +extern +ERL_NIF_TERM esock_make_ok2(ErlNifEnv* env, ERL_NIF_TERM any) +{ + return MKT2(env, esock_atom_ok, any); +} + + +/* Create an ok three (3) tuple in the form: + * + * {ok, Val1, Val2} + * + * The second (Val1) and third (Val2) elements are already in + * the form of an ERL_NIF_TERM so all we have to do is create + * the tuple. + */ +extern +ERL_NIF_TERM esock_make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2) +{ + return MKT3(env, esock_atom_ok, val1, val2); +} + + + +/* Create an error two (2) tuple in the form: + * + * {error, Reason} + * + * The second element (Reason) is already in the form of an + * ERL_NIF_TERM so all we have to do is create the tuple. + */ +extern +ERL_NIF_TERM esock_make_error(ErlNifEnv* env, ERL_NIF_TERM reason) +{ + return MKT2(env, esock_atom_error, reason); +} + + + +/* Create an error two (2) tuple in the form: {error, Reason}. + * + * {error, Reason} + * + * The second element, Reason, is the reason string that has + * converted into an atom. + */ +extern +ERL_NIF_TERM esock_make_error_str(ErlNifEnv* env, char* reason) +{ + return esock_make_error(env, MKA(env, reason)); +} + + +/* Create an error two (2) tuple in the form: + * + * {error, Reason} + * + * The second element, Reason, is the errno value in its + * basic form (integer) which has been converted into an atom. + */ +extern +ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err) +{ + return esock_make_error_str(env, erl_errno_id(err)); +} + + -- cgit v1.2.3 From 978a8a855c57bdfb20d3bcd8a6055968f3be3887 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 18 Jun 2018 15:29:53 +0200 Subject: [socket+net-nif] Backup --- erts/emulator/nifs/common/socket_util.c | 474 ++++++++++++++++++++++++++++++-- 1 file changed, 457 insertions(+), 17 deletions(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 818e259ae8..37adee682b 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -29,8 +29,22 @@ #include "sys.h" -/* THIS IS JUST TEMPORARY */ -extern char* erl_errno_id(int error); + +extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */ + +static char* make_sockaddr_in4(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM* sa); +static char* make_sockaddr_in6(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM flowInfo, + ERL_NIF_TERM scopeId, + ERL_NIF_TERM* sa); +static char* make_sockaddr_un(ErlNifEnv* env, + ERL_NIF_TERM path, + ERL_NIF_TERM* sa); /* +++ esock_decode_sockaddr +++ @@ -95,10 +109,59 @@ char* esock_decode_sockaddr(ErlNifEnv* env, +/* +++ esock_encode_sockaddr +++ + * + * Encode a socket address - sockaddr. In erlang its represented as + * a map, which has a specific set of attributes, depending on one + * mandatory attribute; family. So depending on the value of the family + * attribute: + * + * local - sockaddr_un: path + * inet - sockaddr_in4: port, addr + * inet6 - sockaddr_in6: port, addr, flowinfo, scope_id + */ + +extern +char* esock_encode_sockaddr(ErlNifEnv* env, + SocketAddress* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr) +{ + char* xres; + + switch (sockAddrP->sa.sa_family) { + case AF_INET: + xres = esock_encode_sockaddr_in4(env, &sockAddrP->in4, addrLen, eSockAddr); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + xres = esock_encode_sockaddr_in6(env, &sockAddrP->in6, addrLen, eSockAddr); + break; +#endif + +#ifdef HAVE_SYS_UN_H + case AF_UNIX: + xres = esock_encode_sockaddr_un(env, &sockAddrP->un, addrLen, eSockAddr); + break; +#endif + + default: + xres = ESOCK_STR_EAFNOSUPPORT; + break; + + } + + return xres; +} + + + /* +++ esock_decode_sockaddr_in4 +++ * * Decode a IPv4 socket address - sockaddr_in4. In erlang its represented as - * a map, which has a specific set of attributes: + * a map, which has a specific set of attributes (beside the mandatory family + * attribute, which is "inherited" from the "sockaddr" type): * * port :: port_numbber() * addr :: ip4_address() @@ -147,10 +210,58 @@ char* esock_decode_sockaddr_in4(ErlNifEnv* env, } -/* +++ decode_sockaddr_in6 +++ + +/* +++ esock_encode_sockaddr_in4 +++ + * + * Encode a IPv4 socket address - sockaddr_in4. In erlang its represented as + * a map, which has a specific set of attributes (beside the mandatory family + * attribute, which is "inherited" from the "sockaddr" type): + * + * port :: port_numbber() + * addr :: ip4_address() + * + */ + +extern +char* esock_encode_sockaddr_in4(ErlNifEnv* env, + struct sockaddr_in* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr) +{ + ERL_NIF_TERM ePort, eAddr; + int port; + char* xres = NULL; + + if (addrLen >= sizeof(struct sockaddr_in)) { + /* The port */ + port = ntohs(sockAddrP->sin_port); + ePort = MKI(env, port); + + /* The address */ + if ((xres = esock_encode_ip4_address(env, &sockAddrP->sin_addr, + &eAddr)) != NULL) { + /* And finally construct the in4_sockaddr record */ + xres = make_sockaddr_in4(env, ePort, eAddr, eSockAddr); + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + return xres; +} + + + +/* +++ esock_decode_sockaddr_in6 +++ * * Decode a IPv6 socket address - sockaddr_in6. In erlang its represented as - * a map, which has a specific set of attributes: + * a map, which has a specific set of attributes (beside the mandatory family + * attribute, which is "inherited" from the "sockaddr" type): * * port :: port_numbber() (integer) * addr :: ip6_address() (tuple) @@ -224,10 +335,67 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, +/* +++ esock_encode_sockaddr_in6 +++ + * + * Encode a IPv6 socket address - sockaddr_in6. In erlang its represented as + * a map, which has a specific set of attributes (beside the mandatory family + * attribute, which is "inherited" from the "sockaddr" type): + * + * port :: port_numbber() (integer) + * addr :: ip6_address() (tuple) + * flowinfo :: in6_flow_info() (integer) + * scope_id :: in6_scope_id() (integer) + * + */ + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_encode_sockaddr_in6(ErlNifEnv* env, + struct sockaddr_in6* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr) +{ + ERL_NIF_TERM ePort, eAddr, eFlowInfo, eScopeId; + char* xres; + + if (addrLen >= sizeof(struct sockaddr_in6)) { + /* The port */ + ePort = MKI(env, ntohs(sockAddrP->sin6_port)); + + /* The flowInfo */ + eFlowInfo = MKI(env, sockAddrP->sin6_flowinfo); + + /* The scopeId */ + eScopeId = MKI(env, sockAddrP->sin6_scope_id); + + /* The address */ + if ((xres = esock_encode_ip6_address(env, &sockAddrP->sin6_addr, + &eAddr)) != NULL) { + /* And finally construct the in6_sockaddr record */ + xres = make_sockaddr_in6(env, + ePort, eAddr, eFlowInfo, eScopeId, eSockAddr); + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + return xres; +} +#endif + + + /* +++ esock_decode_sockaddr_un +++ * * Decode a Unix Domain socket address - sockaddr_un. In erlang its - * represented as a map, which has a specific set of attributes: + * represented as a map, which has a specific set of attributes + * (beside the mandatory family attribute, which is "inherited" from + * the "sockaddr" type): * * path :: binary() * @@ -256,16 +424,16 @@ char* esock_decode_sockaddr_un(ErlNifEnv* env, if ((bin.size + #ifdef __linux__ - /* Make sure the address gets zero terminated - * except when the first byte is \0 because then it is - * sort of zero terminated although the zero termination - * comes before the address... - * This fix handles Linux's nonportable - * abstract socket address extension. - */ - (bin.data[0] == '\0' ? 0 : 1) + /* Make sure the address gets zero terminated + * except when the first byte is \0 because then it is + * sort of zero terminated although the zero termination + * comes before the address... + * This fix handles Linux's nonportable + * abstract socket address extension. + */ + (bin.data[0] == '\0' ? 0 : 1) #else - 1 + 1 #endif ) > sizeof(sockAddrP->sun_path)) return ESOCK_STR_EINVAL; @@ -288,7 +456,65 @@ char* esock_decode_sockaddr_un(ErlNifEnv* env, -/* +++ decode_ip4_address +++ +/* +++ esock_encode_sockaddr_un +++ + * + * Encode a Unix Domain socket address - sockaddr_un. In erlang its + * represented as a map, which has a specific set of attributes + * (beside the mandatory family attribute, which is "inherited" from + * the "sockaddr" type): + * + * path :: binary() + * + */ + +#ifdef HAVE_SYS_UN_H +extern +char* esock_encode_sockaddr_un(ErlNifEnv* env, + struct sockaddr_un* sockAddrP, + unsigned int addrLen, + ERL_NIF_TERM* eSockAddr) +{ + ERL_NIF_TERM ePath; + size_t n, m; + char* xres; + + if (addrLen >= offsetof(struct sockaddr_un, sun_path)) { + n = addrLen - offsetof(struct sockaddr_un, sun_path); + if (255 < n) { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } else { + m = esock_strnlen(sockAddrP->sun_path, n); +#ifdef __linux__ + /* Assume that the address is a zero terminated string, + * except when the first byte is \0 i.e the string length is 0, + * then use the reported length instead. + * This fix handles Linux's nonportable + * abstract socket address extension. + */ + if (m == 0) { + m = n; + } +#endif + + /* And finally build the 'path' attribute */ + ePath = MKSL(env, sockAddrP->sun_path, m); + + /* And the socket address */ + xres = make_sockaddr_un(env, ePath, eSockAddr); + } + } else { + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EINVAL; + } + + return xres; +} +#endif + + + +/* +++ esock_decode_ip4_address +++ * * Decode a IPv4 address. This can be three things: * @@ -351,7 +577,39 @@ char* esock_decode_ip4_address(ErlNifEnv* env, -/* +++ decode_ip6_address +++ +/* +++ esock_encode_ip4_address +++ + * + * Encode a IPv4 address: + * + * + An ip4_address() (4 tuple) + * + * Note that this *only* decodes the "address" part of a + * (IPv4) socket address. There are several other things (port). + */ + +extern +char* esock_encode_ip4_address(ErlNifEnv* env, + struct in_addr* addrP, + ERL_NIF_TERM* eAddr) +{ + unsigned int i; + ERL_NIF_TERM at[4]; + unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); + unsigned char* a = (unsigned char*) addrP; + + /* The address */ + for (i = 0; i < atLen; i++) { + at[i] = MKI(env, a[i]); + } + + *eAddr = MKTA(env, at, atLen); + + return NULL; +} + + + +/* +++ esock_decode_ip6_address +++ * * Decode a IPv6 address. This can be three things: * @@ -417,6 +675,42 @@ char* esock_decode_ip6_address(ErlNifEnv* env, #endif + +/* +++ esock_encode_ip6_address +++ + * + * Encode a IPv6 address: + * + * + An ip6_address() (8 tuple) + * + * Note that this *only* encodes the "address" part of a + * (IPv6) socket address. There are several other things + * (port, flowinfo and scope_id) that are handled elsewhere). + */ + +#if defined(HAVE_IN6) && defined(AF_INET6) +extern +char* esock_encode_ip6_address(ErlNifEnv* env, + struct in6_addr* addrP, + ERL_NIF_TERM* eAddr) +{ + unsigned int i; + ERL_NIF_TERM at[8]; + unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); + unsigned char* a = (unsigned char*) &addrP; + + /* The address */ + for (i = 0; i < atLen; i++) { + at[i] = MKI(env, get_int16(a + i*2)); + } + + *eAddr = MKTA(env, at, atLen); + + return NULL; +} +#endif + + + /* +++ esock_decode_domain +++ * * Decode the Erlang form of the 'domain' type, that is: @@ -584,6 +878,36 @@ char* esock_encode_type(ErlNifEnv* env, +/* *** esock_decode_bool *** + * + * Decode a boolean value. + * + */ +extern +BOOLEAN_T esock_decode_bool(ERL_NIF_TERM val) +{ + if (COMPARE(esock_atom_true, val) == 0) + return TRUE; + else + return FALSE; +} + + +/* *** esock_encode_bool *** + * + * Encode a boolean value. + * + */ +extern +ERL_NIF_TERM esock_encode_bool(BOOLEAN_T val) +{ + if (val) + return esock_atom_true; + else + return esock_atom_false; +} + + /* Create an ok two (2) tuple in the form: * * {ok, Any} @@ -657,3 +981,119 @@ ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err) } + +/* strnlen doesn't exist everywhere */ +extern +size_t esock_strnlen(const char *s, size_t maxlen) +{ + size_t i = 0; + while (i < maxlen && s[i] != '\0') + i++; + return i; +} + + + +/* *** esock_abort *** + * + * Generate an abort with "extra" info. This should be called + * via the ESOCK_ABORT macro. + * Basically it prints the extra info onto stderr before aborting. + * + */ +extern +void esock_abort(const char* expr, + const char* func, + const char* file, + int line) +{ + fflush(stdout); + fprintf(stderr, "%s:%d:%s() Assertion failed: %s\n", + file, line, func, expr); + fflush(stderr); + abort(); +} + + + + +/* =================================================================== * + * * + * Various utility functions * + * * + * =================================================================== */ + +/* Construct the IPv4 socket address */ +static +char* make_sockaddr_in4(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM* sa) +{ + ERL_NIF_TERM keys[] = {esock_atom_family, esock_atom_port, esock_atom_addr}; + ERL_NIF_TERM vals[] = {esock_atom_inet, port, addr}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, sa)) { + *sa = esock_atom_undefined; + return ESOCK_STR_EINVAL; + } else { + return NULL; + } +} + + +/* Construct the IPv6 socket address */ +static +char* make_sockaddr_in6(ErlNifEnv* env, + ERL_NIF_TERM port, + ERL_NIF_TERM addr, + ERL_NIF_TERM flowInfo, + ERL_NIF_TERM scopeId, + ERL_NIF_TERM* sa) +{ + ERL_NIF_TERM keys[] = {esock_atom_family, + esock_atom_port, + esock_atom_addr, + esock_atom_flowinfo, + esock_atom_scope_id}; + ERL_NIF_TERM vals[] = {esock_atom_inet6, port, addr, flowInfo, scopeId}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, sa)) { + *sa = esock_atom_undefined; + return ESOCK_STR_EINVAL; + } else { + return NULL; + } +} + + +/* Construct the Unix Domain socket address */ +static +char* make_sockaddr_un(ErlNifEnv* env, + ERL_NIF_TERM path, + ERL_NIF_TERM* sa) +{ + ERL_NIF_TERM keys[] = {esock_atom_family, esock_atom_path}; + ERL_NIF_TERM vals[] = {esock_atom_inet, path}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, sa)) { + *sa = esock_atom_undefined; + return ESOCK_STR_EINVAL; + } else { + return NULL; + } +} + + -- cgit v1.2.3 From b63a16d0958bd748644d22f13f35f8956a903d6c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 18 Jun 2018 18:19:39 +0200 Subject: [socket+net-nif] Moved common functions into util files The common stuff, like decode and encode of common types (soch as socket address), has been moved into a util file (socket_util). The debug stuff has also been moved into its own file. Also introduced a common include file for common macros and types. OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 168 ++++++++++++++++++++++++++++++-- 1 file changed, 160 insertions(+), 8 deletions(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 37adee682b..05fb40e286 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -26,9 +26,23 @@ #include #include "socket_int.h" #include "socket_util.h" +#include "socket_dbg.h" #include "sys.h" +/* We don't have a "debug flag" to check here, so we + * should use the compile debug flag, whatever that is... + */ + +// #define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 +#if defined(COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK) +#define UTIL_DEBUG TRUE +#else +#define UTIL_DEBUG FALSE +#endif + +#define UDBG( proto ) ESOCK_DBG_PRINTF( UTIL_DEBUG , proto ) + extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */ @@ -69,15 +83,19 @@ char* esock_decode_sockaddr(ErlNifEnv* env, int fam; char* xres; + UDBG( ("SUTIL", "esock_decode_sockaddr -> entry\r\n") ); + if (!IS_MAP(env, eSockAddr)) return ESOCK_STR_EINVAL; if (!GET_MAP_VAL(env, eSockAddr, esock_atom_family, &efam)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr -> try decode domain (%T)\r\n", efam) ); if ((xres = esock_decode_domain(env, efam, &fam)) != NULL) return xres; + UDBG( ("SUTIL", "esock_decode_sockaddr -> fam: %d\r\n", fam) ); switch (fam) { case AF_INET: xres = esock_decode_sockaddr_in4(env, eSockAddr, @@ -147,7 +165,8 @@ char* esock_encode_sockaddr(ErlNifEnv* env, #endif default: - xres = ESOCK_STR_EAFNOSUPPORT; + *eSockAddr = esock_atom_undefined; + xres = ESOCK_STR_EAFNOSUPPORT; break; } @@ -178,6 +197,9 @@ char* esock_decode_sockaddr_in4(ErlNifEnv* env, { ERL_NIF_TERM eport, eaddr; int port; + char* xres; + + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> entry\r\n") ); /* Basic init */ sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in)); @@ -189,22 +211,28 @@ char* esock_decode_sockaddr_in4(ErlNifEnv* env, sockAddrP->sin_family = AF_INET; /* Extract (e) port number from map */ + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try get port number\r\n") ); if (!GET_MAP_VAL(env, eSockAddr, esock_atom_port, &eport)) return ESOCK_STR_EINVAL; /* Decode port number */ + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try decode port number\r\n") ); if (!GET_INT(env, eport, &port)) return ESOCK_STR_EINVAL; sockAddrP->sin_port = htons(port); /* Extract (e) address from map */ + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try get (ip) address\r\n") ); if (!GET_MAP_VAL(env, eSockAddr, esock_atom_addr, &eaddr)) return ESOCK_STR_EINVAL; /* Decode address */ - if (!esock_decode_ip4_address(env, eaddr, sockAddrP, addrLen)) - return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try decode (ip) address\r\n") ); + if ((xres = esock_decode_ip4_address(env, eaddr, sockAddrP, addrLen)) != NULL) + return xres; + + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> done\r\n") ); return NULL; } @@ -232,6 +260,8 @@ char* esock_encode_sockaddr_in4(ErlNifEnv* env, int port; char* xres = NULL; + UDBG( ("SUTIL", "esock_encode_sockaddr_in4 -> entry\r\n") ); + if (addrLen >= sizeof(struct sockaddr_in)) { /* The port */ port = ntohs(sockAddrP->sin_port); @@ -239,15 +269,23 @@ char* esock_encode_sockaddr_in4(ErlNifEnv* env, /* The address */ if ((xres = esock_encode_ip4_address(env, &sockAddrP->sin_addr, - &eAddr)) != NULL) { + &eAddr)) == NULL) { /* And finally construct the in4_sockaddr record */ xres = make_sockaddr_in4(env, ePort, eAddr, eSockAddr); } else { + UDBG( ("SUTIL", "esock_encode_sockaddr_in4 -> " + "failed encoding (ip) address: " + "\r\n xres: %s" + "\r\n", xres) ); *eSockAddr = esock_atom_undefined; xres = ESOCK_STR_EINVAL; } } else { + UDBG( ("SUTIL", "esock_encode_sockaddr_in4 -> wrong size: " + "\r\n addrLen: %d" + "\r\n addr size: %d" + "\r\n", addrLen, sizeof(struct sockaddr_in)) ); *eSockAddr = esock_atom_undefined; xres = ESOCK_STR_EINVAL; } @@ -282,6 +320,7 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, ERL_NIF_TERM eport, eaddr, eflowInfo, escopeId; int port; unsigned int flowInfo, scopeId; + char* xres; /* Basic init */ sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); @@ -326,8 +365,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, return ESOCK_STR_EINVAL; /* Decode address */ - if (!esock_decode_ip6_address(env, eaddr, sockAddrP, addrLen)) - return ESOCK_STR_EINVAL; + if ((xres = esock_decode_ip6_address(env, eaddr, sockAddrP, addrLen)) != NULL) + return xres; return NULL; } @@ -370,7 +409,7 @@ char* esock_encode_sockaddr_in6(ErlNifEnv* env, /* The address */ if ((xres = esock_encode_ip6_address(env, &sockAddrP->sin6_addr, - &eAddr)) != NULL) { + &eAddr)) == NULL) { /* And finally construct the in6_sockaddr record */ xres = make_sockaddr_in6(env, ePort, eAddr, eFlowInfo, eScopeId, eSockAddr); @@ -532,15 +571,22 @@ char* esock_decode_ip4_address(ErlNifEnv* env, struct sockaddr_in* sockAddrP, unsigned int* addrLen) { + UDBG( ("SUTIL", "esock_decode_ip4_address -> entry with" + "\r\n eAddr: %T" + "\r\n", eAddr) ); + if (IS_ATOM(env, eAddr)) { /* This is either 'any' or 'loopback' */ struct in_addr addr; if (COMPARE(esock_atom_loopback, eAddr) == 0) { + UDBG( ("SUTIL", "esock_decode_ip4_address -> address: lookback\r\n") ); addr.s_addr = htonl(INADDR_LOOPBACK); } else if (COMPARE(esock_atom_any, eAddr) == 0) { + UDBG( ("SUTIL", "esock_decode_ip4_address -> address: any\r\n") ); addr.s_addr = htonl(INADDR_ANY); } else { + UDBG( ("SUTIL", "esock_decode_ip4_address -> address: unknown\r\n") ); return ESOCK_STR_EINVAL; } @@ -596,13 +642,17 @@ char* esock_encode_ip4_address(ErlNifEnv* env, ERL_NIF_TERM at[4]; unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); unsigned char* a = (unsigned char*) addrP; + ERL_NIF_TERM addr; /* The address */ for (i = 0; i < atLen; i++) { at[i] = MKI(env, a[i]); } - *eAddr = MKTA(env, at, atLen); + addr = MKTA(env, at, atLen); + UDBG( ("SUTIL", "esock_encode_ip4_address -> addr: %T\r\n", addr) ); + // *eAddr = MKTA(env, at, atLen); + *eAddr = addr; return NULL; } @@ -878,6 +928,108 @@ char* esock_encode_type(ErlNifEnv* env, +/* +++ esock_decode_protocol +++ + * + * Encode the native protocol to the Erlang form, that is: + * + * SOL_IP | IPPROTO_IP => ip + * SOL_IPV6 => ipv6 + * SOL_TCP => tcp + * SOL_UDP => udp + * SOL_SCTP => sctp + * + */ +extern +char* esock_encode_protocol(ErlNifEnv* env, + int proto, + ERL_NIF_TERM* eProto) +{ + char* xres = NULL; + + switch (proto) { +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + *eProto = esock_atom_ip; + break; + +#if defined(SOL_IPV6) + case SOL_IPV6: + *eProto = esock_atom_ipv6; + break; +#endif + + case IPPROTO_TCP: + *eProto = esock_atom_tcp; + break; + + case IPPROTO_UDP: + *eProto = esock_atom_udp; + break; + +#if defined(HAVE_SCTP) + case IPPROTO_SCTP: + *eProto = esock_atom_sctp; + break; +#endif + + default: + *eProto = esock_atom_undefined; + xres = ESOCK_STR_EAFNOSUPPORT; + break; + } + + return xres; +} + + + +/* +++ esock_decode_protocol +++ + * + * Decode the Erlang form of the 'protocol' type, that is: + * + * ip => SOL_IP | IPPROTO_IP + * ipv6 => SOL_IPV6 + * tcp => SOL_TCP + * udp => SOL_UDP + * sctp => SOL_SCTP + * + */ +extern +char* esock_decode_protocol(ErlNifEnv* env, + ERL_NIF_TERM eProto, + int* proto) +{ + char* xres = NULL; + + if (COMPARE(esock_atom_ip, eProto) == 0) { +#if defined(SOL_IP) + *proto = SOL_IP; +#else + *proto = IPPROTO_IP; +#endif + } else if (COMPARE(esock_atom_ipv6, eProto) == 0) { + *proto = SOL_IPV6; + } else if (COMPARE(esock_atom_tcp, eProto) == 0) { + *proto = IPPROTO_TCP; + } else if (COMPARE(esock_atom_udp, eProto) == 0) { + *proto = IPPROTO_UDP; +#if defined(HAVE_SCTP) + } else if (COMPARE(esock_atom_sctp, eProto) == 0) { + *proto = IPPROTO_SCTP; +#endif + } else { + *proto = -1; + xres = ESOCK_STR_EAFNOSUPPORT; + } + + return xres; +} + + + /* *** esock_decode_bool *** * * Decode a boolean value. -- cgit v1.2.3 From 24be0729fe3a1ccfd5f0713b565463d6557d8aa7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 29 Jun 2018 18:23:55 +0200 Subject: [socket-nif] Fixed (stream) recv Fixed handling of closed in the recv function. We still need to properly handle when we get 0 bytes of data for other types ock sockets then stream (its valid for dgram for instance). OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 89 ++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 05fb40e286..397f69f58d 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -29,6 +29,11 @@ #include "socket_dbg.h" #include "sys.h" +#include +#include +#include +#include +#include /* We don't have a "debug flag" to check here, so we * should use the compile debug flag, whatever that is... @@ -46,6 +51,9 @@ extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */ +static int realtime(struct timespec* tsP); +static int timespec2str(char *buf, unsigned int len, struct timespec *ts); + static char* make_sockaddr_in4(ErlNifEnv* env, ERL_NIF_TERM port, ERL_NIF_TERM addr, @@ -1168,10 +1176,89 @@ void esock_abort(const char* expr, +/* *** esock_warning_msg *** + * + * Temporary function for issuing warning messages. + * + */ +extern +void esock_warning_msg( const char* format, ... ) +{ + va_list args; + char f[512 + sizeof(format)]; // This has to suffice... + char stamp[32]; + struct timespec ts; + int res; + + /* + * We should really include self in the printout, so we can se which process + * are executing the code. But then I must change the API.... + * ....something for later. + */ + + // 2018-06-29 12:13:21.232089 + // 29-Jun-2018::13:47:25.097097 + + if (!realtime(&ts)) { + if (timespec2str(stamp, sizeof(stamp), &ts) != 0) { + res = enif_snprintf(f, sizeof(f), "=WARNING MSG==== %s", format); + } else { + res = enif_snprintf(f, sizeof(f), + "=WARNING MSG==== %s ===\r\n%s" , stamp, format); + } + + if (res > 0) { + va_start (args, format); + enif_vfprintf (stdout, f, args); + va_end (args); + fflush(stdout); + } + } + + return; +} + + +static +int realtime(struct timespec* tsP) +{ + return clock_gettime(CLOCK_REALTIME, tsP); +} + + +/* + * Convert a timespec struct into a readable/printable string. + * + * "%F::%T" => 2018-06-29 12:13:21[.232089] + * "%d-%b-%Y::%T" => 29-Jun-2018::13:47:25.097097 + */ +static +int timespec2str(char *buf, unsigned int len, struct timespec *ts) +{ + int ret, buflen; + struct tm t; + + tzset(); + if (localtime_r(&(ts->tv_sec), &t) == NULL) + return 1; + + ret = strftime(buf, len, "%d-%B-%Y::%T", &t); + if (ret == 0) + return 2; + len -= ret - 1; + buflen = strlen(buf); + + ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); + if (ret >= len) + return 3; + + return 0; +} + /* =================================================================== * * * - * Various utility functions * + * Various (internal) utility functions * * * * =================================================================== */ -- cgit v1.2.3 From 6632cb103336786f3ca12f9d1f1d3338fc76c237 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 20 Jun 2018 12:33:18 +0200 Subject: [net-nif] Changed return type of getaddrinfo The returned address info was supposed to be a map, by was instead (still) a record. OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 43 ++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 397f69f58d..5014688852 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -890,7 +890,7 @@ char* esock_decode_type(ErlNifEnv* env, -/* +++ esock_decode_domain +++ +/* +++ esock_decode_type +++ * * Encode the native type to the Erlang form, that is: * @@ -1038,6 +1038,47 @@ char* esock_decode_protocol(ErlNifEnv* env, +/* *** esock_decode_string *** + * + * Decode a string value. A successful decode results in an + * allocation of the string, which the caller has to free + * once the string has been used. + */ +extern +BOOLEAN_T esock_decode_string(ErlNifEnv* env, + const ERL_NIF_TERM eString, + char** stringP) +{ + BOOLEAN_T result; + unsigned int len; + char* bufP; + + if (!GET_LIST_LEN(env, eString, &len) && (len != 0)) { + *stringP = NULL; + result = FALSE; + } else { + + UDBG( ("SUTIL", "esock_decode_string -> len: %d\r\n", len) ); + + bufP = MALLOC(len + 1); // We shall NULL-terminate + + if (GET_STR(env, eString, bufP, len+1)) { + UDBG( ("SUTIL", "esock_decode_string -> buf: %s\r\n", bufP) ); + // bufP[len] = '\0'; + *stringP = bufP; + result = TRUE; + } else { + *stringP = NULL; + result = FALSE; + FREE(bufP); + } + } + + return result; +} + + + /* *** esock_decode_bool *** * * Decode a boolean value. -- cgit v1.2.3 From 8118227d80fc41efac23d30a5601fdfadb75931f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 10 Jul 2018 17:41:40 +0200 Subject: [socket-nif] Add support for socket (level ip) option multicast_if Added support for the IP option MULTICAST_IF. OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 83 ++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 32 deletions(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 5014688852..512c1d38e0 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -237,9 +237,13 @@ char* esock_decode_sockaddr_in4(ErlNifEnv* env, /* Decode address */ UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> try decode (ip) address\r\n") ); - if ((xres = esock_decode_ip4_address(env, eaddr, sockAddrP, addrLen)) != NULL) + if ((xres = esock_decode_ip4_address(env, + eaddr, + &sockAddrP->sin_addr)) != NULL) return xres; + *addrLen = sizeof(struct sockaddr_in); + UDBG( ("SUTIL", "esock_decode_sockaddr_in4 -> done\r\n") ); return NULL; @@ -330,6 +334,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, unsigned int flowInfo, scopeId; char* xres; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> entry\r\n") ); + /* Basic init */ sys_memzero((char*) sockAddrP, sizeof(struct sockaddr_in6)); #ifndef NO_SA_LEN @@ -346,6 +352,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, if (!GET_INT(env, eport, &port)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> port: %d\r\n", port) ); + sockAddrP->sin6_port = htons(port); /* *** Extract (e) flowinfo from map *** */ @@ -356,6 +364,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, if (!GET_UINT(env, eflowInfo, &flowInfo)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> flowinfo: %d\r\n", flowInfo) ); + sockAddrP->sin6_flowinfo = flowInfo; /* *** Extract (e) scope_id from map *** */ @@ -366,6 +376,8 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, if (!GET_UINT(env, escopeId, &scopeId)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> scopeId: %d\r\n", scopeId) ); + sockAddrP->sin6_scope_id = scopeId; /* *** Extract (e) address from map *** */ @@ -373,9 +385,15 @@ char* esock_decode_sockaddr_in6(ErlNifEnv* env, return ESOCK_STR_EINVAL; /* Decode address */ - if ((xres = esock_decode_ip6_address(env, eaddr, sockAddrP, addrLen)) != NULL) + if ((xres = esock_decode_ip6_address(env, + eaddr, + &sockAddrP->sin6_addr)) != NULL) return xres; + *addrLen = sizeof(struct sockaddr_in6); + + UDBG( ("SUTIL", "esock_decode_sockaddr_in6 -> done\r\n") ); + return NULL; } #endif @@ -570,22 +588,22 @@ char* esock_encode_sockaddr_un(ErlNifEnv* env, * + An ip4_address() (4 tuple) * * Note that this *only* decodes the "address" part of a - * (IPv4) socket address. There are several other things (port). + * (IPv4) socket address. */ extern -char* esock_decode_ip4_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in* sockAddrP, - unsigned int* addrLen) +char* esock_decode_ip4_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in_addr* inAddrP) { + struct in_addr addr; + UDBG( ("SUTIL", "esock_decode_ip4_address -> entry with" "\r\n eAddr: %T" "\r\n", eAddr) ); if (IS_ATOM(env, eAddr)) { /* This is either 'any' or 'loopback' */ - struct in_addr addr; if (COMPARE(esock_atom_loopback, eAddr) == 0) { UDBG( ("SUTIL", "esock_decode_ip4_address -> address: lookback\r\n") ); @@ -598,8 +616,7 @@ char* esock_decode_ip4_address(ErlNifEnv* env, return ESOCK_STR_EINVAL; } - sockAddrP->sin_addr.s_addr = addr.s_addr; - *addrLen = sizeof(struct sockaddr_in); + inAddrP->s_addr = addr.s_addr; } else { /* This is a 4-tuple */ @@ -621,8 +638,7 @@ char* esock_decode_ip4_address(ErlNifEnv* env, addr[a] = v; } - sys_memcpy(&sockAddrP->sin_addr, &addr, sizeof(addr)); - *addrLen = sizeof(struct sockaddr_in); + sys_memcpy(inAddrP, &addr, sizeof(addr)); } @@ -682,11 +698,14 @@ char* esock_encode_ip4_address(ErlNifEnv* env, #if defined(HAVE_IN6) && defined(AF_INET6) extern -char* esock_decode_ip6_address(ErlNifEnv* env, - ERL_NIF_TERM eAddr, - struct sockaddr_in6* sockAddrP, - unsigned int* addrLen) +char* esock_decode_ip6_address(ErlNifEnv* env, + ERL_NIF_TERM eAddr, + struct in6_addr* inAddrP) { + UDBG( ("SUTIL", "esock_decode_ip6_address -> entry with" + "\r\n eAddr: %T" + "\r\n", eAddr) ); + if (IS_ATOM(env, eAddr)) { /* This is either 'any' or 'loopback' */ const struct in6_addr* addr; @@ -698,34 +717,34 @@ char* esock_decode_ip6_address(ErlNifEnv* env, } else { return ESOCK_STR_EINVAL; } - - sockAddrP->sin6_addr = *addr; - *addrLen = sizeof(struct sockaddr_in6); - + + *inAddrP = *addr; + } else { /* This is a 8-tuple */ - + const ERL_NIF_TERM* addrt; int addrtSz; - int a, v; - char addr[16]; + int ai, v; + unsigned char addr[16]; + unsigned char* a = addr; + unsigned int addrLen = sizeof(addr) / sizeof(unsigned char); if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) return ESOCK_STR_EINVAL; if (addrtSz != 8) return ESOCK_STR_EINVAL; - - for (a = 0; a < 8; a++) { - if (!GET_INT(env, addrt[a], &v)) + + for (ai = 0; ai < 8; ai++) { + if (!GET_INT(env, addrt[ai], &v)) return ESOCK_STR_EINVAL; - addr[a*2 ] = ((v >> 8) & 0xFF); - addr[a*2+1] = (v & 0xFF); + put_int16(v, a); + a += 2; } - - sys_memcpy(&sockAddrP->sin6_addr, &addr, sizeof(addr)); - *addrLen = sizeof(struct sockaddr_in6); + sys_memcpy(inAddrP, &addr, addrLen); + } return NULL; @@ -754,7 +773,7 @@ char* esock_encode_ip6_address(ErlNifEnv* env, unsigned int i; ERL_NIF_TERM at[8]; unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); - unsigned char* a = (unsigned char*) &addrP; + unsigned char* a = (unsigned char*) addrP; /* The address */ for (i = 0; i < atLen; i++) { -- cgit v1.2.3 From 416644989e26ac76038523511d81ebf9e0b8fc4f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 Jul 2018 12:34:06 +0200 Subject: [net+socket-nif] Updated on_load stuff The net nif-module had debug on by default. Fixed this and also made it an option, just as for the socket nif-module. OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 512c1d38e0..e6eb21adcf 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1098,6 +1098,33 @@ BOOLEAN_T esock_decode_string(ErlNifEnv* env, +/* *** esock_extract_bool_from_map *** + * + * Extract an boolean item from a map. + * + */ +extern +BOOLEAN_T esock_extract_bool_from_map(ErlNifEnv* env, + ERL_NIF_TERM map, + ERL_NIF_TERM key, + BOOLEAN_T def) +{ + ERL_NIF_TERM val; + + if (!GET_MAP_VAL(env, map, key, &val)) + return def; + + if (!IS_ATOM(env, val)) + return def; + + if (COMPARE(val, esock_atom_true) == 0) + return TRUE; + else + return FALSE; +} + + + /* *** esock_decode_bool *** * * Decode a boolean value. -- cgit v1.2.3 From 165666d8b8b1b21ffaf43ac436cfc1657ba83649 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 30 Jul 2018 18:22:46 +0200 Subject: [socket-nif] Add support for recvmsg Added preliminary support for function recvmsg. At the moment this only works on *nix (Windows has another function, WSARecvMsg, which has a slightly different API). Also we have "no" cmsg decode at the moment (just level and type). OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 267 +++++++++++++++++++++++++++++++- 1 file changed, 261 insertions(+), 6 deletions(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index e6eb21adcf..5998ff35a4 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -23,17 +23,18 @@ * */ -#include -#include "socket_int.h" -#include "socket_util.h" -#include "socket_dbg.h" -#include "sys.h" - #include #include #include #include #include +#include + +#include "socket_int.h" +#include "socket_tarray.h" +#include "socket_util.h" +#include "socket_dbg.h" +#include "sys.h" /* We don't have a "debug flag" to check here, so we * should use the compile debug flag, whatever that is... @@ -69,6 +70,260 @@ static char* make_sockaddr_un(ErlNifEnv* env, ERL_NIF_TERM* sa); +/* +++ esock_encode_msghdr +++ + * + * Encode a msghdr (recvmsg). In erlang its represented as + * a map, which has a specific set of attributes: + * + * addr (source address) - sockaddr() + * iov - [binary()] + * ctrl - [cmsghdr()] + * flags - msghdr_flags() + */ + +extern +char* esock_encode_msghdr(ErlNifEnv* env, + int read, + struct msghdr* msgHdrP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM* eSockAddr) +{ + char* xres; + ERL_NIF_TERM addr, iov, ctrl, flags; + + if ((xres = esock_encode_sockaddr(env, + (SocketAddress*) msgHdrP->msg_name, + msgHdrP->msg_namelen, + &addr)) != NULL) + return xres; + + if ((xres = esock_encode_iov(env, + read, + msgHdrP->msg_iov, + msgHdrP->msg_iovlen, + &iov)) != NULL) + return xres; + + if ((xres = esock_encode_cmsghdrs(env, + ctrlBufP, + msgHdrP, + &ctrl)) != NULL) + return xres; + + if ((xres = esock_encode_mshghdr_flags(env, + msgHdrP->msg_flags, + &flags)) != NULL) + return xres; + + { + ERL_NIF_TERM keys[] = {esock_atom_addr, + esock_atom_iov, + esock_atom_ctrl, + esock_atom_flags}; + ERL_NIF_TERM vals[] = {addr, iov, ctrl, flags}; + + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, eSockAddr)) + return ESOCK_STR_EINVAL; + + } + + return NULL; +} + + + +/* +++ esock_encode_iov +++ + * + * Encode a IO Vector. In erlang we represented this as a list of binaries. + * + * We iterate through the IO vector, and as long as the remaining (rem) + * number of bytes is greater than the size of the current buffer, we + * contunue. When we have a buffer that is greater than rem, we have found + * the last buffer (it may be empty, and then the previous was last). + * We may need to split this (if 0 < rem < bufferSz). + */ + +extern +char* esock_encode_iov(ErlNifEnv* env, + int read, + struct iovec* iov, + size_t len, + ERL_NIF_TERM* eIOV) +{ + int rem = read; + uint16_t i; + BOOLEAN_T done = FALSE; + ERL_NIF_TERM a[len]; // At most this length + + if (len == 0) { + *eIOV = MKEL(env); + return NULL; + } + + for (i = 0; (!done) && (i < len); i++) { + if (iov[i].iov_len == rem) { + /* We have the exact amount - we are done */ + a[i] = MKBIN(env, iov[i].iov_base); + done = TRUE; + } else if (iov[i].iov_len < rem) { + /* Filled another buffer - continue */ + a[i] = MKBIN(env, iov[i].iov_base); + } else if (iov[i].iov_len > rem) { + /* Partly filled buffer (=> split) - we are done */ + a[i] = MKBIN(env, iov[i].iov_base); + a[i] = MKSBIN(env, a[i], 0, rem); + done = TRUE; + } + } + + *eIOV = MKLA(env, a, i+1); + + return NULL; +} + + + +/* +++ esock_encode_cmsghdrs +++ + * + * Encode a list of cmsghdr(). The X can 0 or more cmsghdr blocks. + * + * Our problem is that we have no idea how many control messages + * we have. + * + * The cmsgHdrP arguments points to the start of the control data buffer, + * an actual binary. Its the only way to create sub-binaries. So, what we + * need to continue processing this is to tern that into an binary erlang + * term (which can then in turn be turned into sub-binaries). + * + * We need the cmsgBufP (even though cmsgHdrP points to it) to be able + * to create sub-binaries (one for each HDR). + * + * The TArray is created with the size of 128, which should be enough. + * But if its not, then it will be automatically realloc'ed during add. + * Once we are done adding hdr's to it, we convert it to a list. + */ + +extern +char* esock_encode_cmsghdrs(ErlNifEnv* env, + ErlNifBinary* cmsgBinP, + struct msghdr* msgHdrP, + ERL_NIF_TERM* eCMsgHdr) +{ + ERL_NIF_TERM ctrlBuf = MKBIN(env, cmsgBinP); // The *entire* binary + SocketTArray cmsghdrs = TARRAY_CREATE(128); + struct cmsghdr* firstP = CMSG_FIRSTHDR(msgHdrP); + struct cmsghdr* currentP; + + for (currentP = firstP; + currentP != NULL; + currentP = CMSG_NXTHDR(msgHdrP, currentP)) { + + /* MUST check this since on Linux the returned "cmsg" may actually + * go too far! + */ + if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) > + msgHdrP->msg_controllen) { + /* Ouch, fatal error - give up + * We assume we cannot trust any data if this is wrong. + */ + TARRAY_DELETE(cmsghdrs); + return ESOCK_STR_EINVAL; + } else { + ERL_NIF_TERM level; + ERL_NIF_TERM type = MKI(env, currentP->cmsg_type); + unsigned char* dataP = (unsigned char*) CMSG_DATA(currentP); + size_t dataPos = dataP - cmsgBinP->data; + size_t dataLen = currentP->cmsg_len - (CHARP(currentP)-CHARP(dataP)); + ERL_NIF_TERM dataBin = MKSBIN(env, ctrlBuf, dataPos, dataLen); + + /* We can't give up just because its an unknown protocol, + * so if its a protocol we don't know, we return its integer + * value and leave it to the user. + */ + if (esock_encode_protocol(env, currentP->cmsg_level, &level) != NULL) + level = MKI(env, currentP->cmsg_level); + + /* And finally create the 'cmsghdr' map - + * and if successfull add it to the tarray. + */ + { + ERL_NIF_TERM keys[] = {esock_atom_level, + esock_atom_type, + esock_atom_data}; + ERL_NIF_TERM vals[] = {level, type, dataBin}; + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM cmsgHdr; + + /* Guard agains cut-and-paste errors */ + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, &cmsgHdr)) { + TARRAY_DELETE(cmsghdrs); + return ESOCK_STR_EINVAL; + } + + /* And finally add it to the list... */ + TARRAY_ADD(cmsghdrs, cmsgHdr); + } + } + } + + /* The tarray is populated - convert it to a list */ + TARRAY_TOLIST(cmsghdrs, env, eCMsgHdr); + + return NULL; +} + + + +/* +++ esock_encode_mshghdr_flags +++ + * + * Encode a list of msghdr_flag(). + * + * The following flags are handled: eor | trunc | ctrunc | oob | errqueue. + */ + +extern +char* esock_encode_mshghdr_flags(ErlNifEnv* env, + int msgFlags, + ERL_NIF_TERM* flags) +{ + if (msgFlags == 0) { + *flags = MKEL(env); + return NULL; + } else { + SocketTArray ta = TARRAY_CREATE(10); // Just to be on the safe side + + if ((msgFlags & MSG_EOR) == MSG_EOR) + TARRAY_ADD(ta, esock_atom_eor); + + if ((msgFlags & MSG_TRUNC) == MSG_TRUNC) + TARRAY_ADD(ta, esock_atom_trunc); + + if ((msgFlags & MSG_CTRUNC) == MSG_CTRUNC) + TARRAY_ADD(ta, esock_atom_ctrunc); + + if ((msgFlags & MSG_OOB) == MSG_OOB) + TARRAY_ADD(ta, esock_atom_oob); + + if ((msgFlags & MSG_ERRQUEUE) == MSG_ERRQUEUE) + TARRAY_ADD(ta, esock_atom_errqueue); + + TARRAY_TOLIST(ta, env, flags); + + return NULL; + } +} + + + + /* +++ esock_decode_sockaddr +++ * * Decode a socket address - sockaddr. In erlang its represented as -- cgit v1.2.3 From d4c6b555aea77198d662822c7f5a134a16cff8af Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 31 Jul 2018 12:53:52 +0200 Subject: [socket-nif] Debugged and stuff It seems to work, with atleast some of the cmsg options. More testing needed... OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 104 ++++++++++++++++++++++++++------ 1 file changed, 87 insertions(+), 17 deletions(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 5998ff35a4..50cc94c263 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -40,7 +40,7 @@ * should use the compile debug flag, whatever that is... */ -// #define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 +#define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 #if defined(COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK) #define UTIL_DEBUG TRUE #else @@ -85,53 +85,76 @@ extern char* esock_encode_msghdr(ErlNifEnv* env, int read, struct msghdr* msgHdrP, + ErlNifBinary* dataBufP, ErlNifBinary* ctrlBufP, ERL_NIF_TERM* eSockAddr) { char* xres; ERL_NIF_TERM addr, iov, ctrl, flags; + UDBG( ("SUTIL", "esock_encode_msghdr -> entry with" + "\r\n read: %d" + "\r\n", read) ); + if ((xres = esock_encode_sockaddr(env, (SocketAddress*) msgHdrP->msg_name, msgHdrP->msg_namelen, &addr)) != NULL) return xres; + UDBG( ("SUTIL", "esock_encode_msghdr -> try encode iov\r\n") ); if ((xres = esock_encode_iov(env, read, msgHdrP->msg_iov, msgHdrP->msg_iovlen, + dataBufP, &iov)) != NULL) return xres; + UDBG( ("SUTIL", "esock_encode_msghdr -> try encode cmsghdrs\r\n") ); if ((xres = esock_encode_cmsghdrs(env, ctrlBufP, msgHdrP, &ctrl)) != NULL) return xres; + UDBG( ("SUTIL", "esock_encode_msghdr -> try encode flags\r\n") ); if ((xres = esock_encode_mshghdr_flags(env, msgHdrP->msg_flags, &flags)) != NULL) return xres; + UDBG( ("SUTIL", "esock_encode_msghdr -> components encoded:" + "\r\n addr: %T" + "\r\n iov: %T" + "\r\n ctrl: %T" + "\r\n flags: %T" + "\r\n", addr, iov, ctrl, flags) ); { - ERL_NIF_TERM keys[] = {esock_atom_addr, - esock_atom_iov, - esock_atom_ctrl, - esock_atom_flags}; - ERL_NIF_TERM vals[] = {addr, iov, ctrl, flags}; - + ERL_NIF_TERM keys[] = {esock_atom_addr, + esock_atom_iov, + esock_atom_ctrl, + esock_atom_flags}; + ERL_NIF_TERM vals[] = {addr, iov, ctrl, flags}; unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + ERL_NIF_TERM tmp; ESOCK_ASSERT( (numKeys == numVals) ); - if (!MKMA(env, keys, vals, numKeys, eSockAddr)) + UDBG( ("SUTIL", "esock_encode_msghdr -> create msghdr map\r\n") ); + if (!MKMA(env, keys, vals, numKeys, &tmp)) return ESOCK_STR_EINVAL; + UDBG( ("SUTIL", "esock_encode_msghdr -> msghdr: " + "\r\n %T" + "\r\n", tmp) ); + + *eSockAddr = tmp; } + UDBG( ("SUTIL", "esock_encode_msghdr -> done\r\n") ); + return NULL; } @@ -153,6 +176,7 @@ char* esock_encode_iov(ErlNifEnv* env, int read, struct iovec* iov, size_t len, + ErlNifBinary* data, ERL_NIF_TERM* eIOV) { int rem = read; @@ -160,28 +184,51 @@ char* esock_encode_iov(ErlNifEnv* env, BOOLEAN_T done = FALSE; ERL_NIF_TERM a[len]; // At most this length + UDBG( ("SUTIL", "esock_encode_iov -> entry with" + "\r\n read: %d" + "\r\n (IOV) len: %d" + "\r\n", read, len) ); + if (len == 0) { *eIOV = MKEL(env); return NULL; } for (i = 0; (!done) && (i < len); i++) { + UDBG( ("SUTIL", "esock_encode_iov -> process iov:" + "\r\n iov[%d].iov_len: %d" + "\r\n rem: %d" + "\r\n", i, iov[i].iov_len, rem) ); if (iov[i].iov_len == rem) { /* We have the exact amount - we are done */ - a[i] = MKBIN(env, iov[i].iov_base); + UDBG( ("SUTIL", "esock_encode_iov -> exact => done\r\n") ); + a[i] = MKBIN(env, &data[i]); + UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); + rem = 0; // Besserwisser done = TRUE; } else if (iov[i].iov_len < rem) { /* Filled another buffer - continue */ - a[i] = MKBIN(env, iov[i].iov_base); + UDBG( ("SUTIL", "esock_encode_iov -> filled => continue\r\n") ); + a[i] = MKBIN(env, &data[i]); + rem -= iov[i].iov_len; + UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); } else if (iov[i].iov_len > rem) { /* Partly filled buffer (=> split) - we are done */ - a[i] = MKBIN(env, iov[i].iov_base); - a[i] = MKSBIN(env, a[i], 0, rem); + ERL_NIF_TERM tmp; + UDBG( ("SUTIL", "esock_encode_iov -> split => done\r\n") ); + tmp = MKBIN(env, &data[i]); + a[i] = MKSBIN(env, tmp, 0, rem); + UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); + rem = 0; // Besserwisser done = TRUE; } } - *eIOV = MKLA(env, a, i+1); + UDBG( ("SUTIL", "esock_encode_iov -> create the IOV list (%d)\r\n", i) ); + + *eIOV = MKLA(env, a, i); + + UDBG( ("SUTIL", "esock_encode_msghdr -> done\r\n") ); return NULL; } @@ -201,11 +248,11 @@ char* esock_encode_iov(ErlNifEnv* env, * term (which can then in turn be turned into sub-binaries). * * We need the cmsgBufP (even though cmsgHdrP points to it) to be able - * to create sub-binaries (one for each HDR). + * to create sub-binaries (one for each cmsg hdr). * - * The TArray is created with the size of 128, which should be enough. - * But if its not, then it will be automatically realloc'ed during add. - * Once we are done adding hdr's to it, we convert it to a list. + * The TArray (term array) is created with the size of 128, which should + * be enough. But if its not, then it will be automatically realloc'ed during + * add. Once we are done adding hdr's to it, we convert the tarray to a list. */ extern @@ -219,10 +266,16 @@ char* esock_encode_cmsghdrs(ErlNifEnv* env, struct cmsghdr* firstP = CMSG_FIRSTHDR(msgHdrP); struct cmsghdr* currentP; + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> entry\r\n") ); + for (currentP = firstP; currentP != NULL; currentP = CMSG_NXTHDR(msgHdrP, currentP)) { + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> process cmsg header when" + "\r\n TArray Size: %d" + "\r\n", TARRAY_SZ(cmsghdrs)) ); + /* MUST check this since on Linux the returned "cmsg" may actually * go too far! */ @@ -248,6 +301,11 @@ char* esock_encode_cmsghdrs(ErlNifEnv* env, if (esock_encode_protocol(env, currentP->cmsg_level, &level) != NULL) level = MKI(env, currentP->cmsg_level); + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> " + "\r\n level: %T" + "\r\n type: %T" + "\r\n", level, type) ); + /* And finally create the 'cmsghdr' map - * and if successfull add it to the tarray. */ @@ -274,6 +332,10 @@ char* esock_encode_cmsghdrs(ErlNifEnv* env, } } + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> cmsg headers processed when" + "\r\n TArray Size: %d" + "\r\n", TARRAY_SZ(cmsghdrs)) ); + /* The tarray is populated - convert it to a list */ TARRAY_TOLIST(cmsghdrs, env, eCMsgHdr); @@ -294,6 +356,10 @@ char* esock_encode_mshghdr_flags(ErlNifEnv* env, int msgFlags, ERL_NIF_TERM* flags) { + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> entry with" + "\r\n msgFlags: %d (0x%lX)" + "\r\n", msgFlags, msgFlags) ); + if (msgFlags == 0) { *flags = MKEL(env); return NULL; @@ -315,6 +381,10 @@ char* esock_encode_mshghdr_flags(ErlNifEnv* env, if ((msgFlags & MSG_ERRQUEUE) == MSG_ERRQUEUE) TARRAY_ADD(ta, esock_atom_errqueue); + UDBG( ("SUTIL", "esock_encode_cmsghdrs -> flags processed when" + "\r\n TArray size: %d" + "\r\n", TARRAY_SZ(ta)) ); + TARRAY_TOLIST(ta, env, flags); return NULL; -- cgit v1.2.3 From 25c38eff5c1e8d4dc6325afa62031874e23262dc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 31 Jul 2018 18:01:55 +0200 Subject: [socket-nif] Add more control message decoding Added decoding of the "known" control message options: pktinfo, recvtos, recvttl and recvorigdstaddr). OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 259 +------------------------------- 1 file changed, 3 insertions(+), 256 deletions(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 50cc94c263..232e8200df 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -31,7 +31,6 @@ #include #include "socket_int.h" -#include "socket_tarray.h" #include "socket_util.h" #include "socket_dbg.h" #include "sys.h" @@ -40,7 +39,7 @@ * should use the compile debug flag, whatever that is... */ -#define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 +// #define COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK 1 #if defined(COMPILE_DEBUG_FLAG_WE_NEED_TO_CHECK) #define UTIL_DEBUG TRUE #else @@ -70,96 +69,6 @@ static char* make_sockaddr_un(ErlNifEnv* env, ERL_NIF_TERM* sa); -/* +++ esock_encode_msghdr +++ - * - * Encode a msghdr (recvmsg). In erlang its represented as - * a map, which has a specific set of attributes: - * - * addr (source address) - sockaddr() - * iov - [binary()] - * ctrl - [cmsghdr()] - * flags - msghdr_flags() - */ - -extern -char* esock_encode_msghdr(ErlNifEnv* env, - int read, - struct msghdr* msgHdrP, - ErlNifBinary* dataBufP, - ErlNifBinary* ctrlBufP, - ERL_NIF_TERM* eSockAddr) -{ - char* xres; - ERL_NIF_TERM addr, iov, ctrl, flags; - - UDBG( ("SUTIL", "esock_encode_msghdr -> entry with" - "\r\n read: %d" - "\r\n", read) ); - - if ((xres = esock_encode_sockaddr(env, - (SocketAddress*) msgHdrP->msg_name, - msgHdrP->msg_namelen, - &addr)) != NULL) - return xres; - - UDBG( ("SUTIL", "esock_encode_msghdr -> try encode iov\r\n") ); - if ((xres = esock_encode_iov(env, - read, - msgHdrP->msg_iov, - msgHdrP->msg_iovlen, - dataBufP, - &iov)) != NULL) - return xres; - - UDBG( ("SUTIL", "esock_encode_msghdr -> try encode cmsghdrs\r\n") ); - if ((xres = esock_encode_cmsghdrs(env, - ctrlBufP, - msgHdrP, - &ctrl)) != NULL) - return xres; - - UDBG( ("SUTIL", "esock_encode_msghdr -> try encode flags\r\n") ); - if ((xres = esock_encode_mshghdr_flags(env, - msgHdrP->msg_flags, - &flags)) != NULL) - return xres; - - UDBG( ("SUTIL", "esock_encode_msghdr -> components encoded:" - "\r\n addr: %T" - "\r\n iov: %T" - "\r\n ctrl: %T" - "\r\n flags: %T" - "\r\n", addr, iov, ctrl, flags) ); - { - ERL_NIF_TERM keys[] = {esock_atom_addr, - esock_atom_iov, - esock_atom_ctrl, - esock_atom_flags}; - ERL_NIF_TERM vals[] = {addr, iov, ctrl, flags}; - unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); - unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); - ERL_NIF_TERM tmp; - - ESOCK_ASSERT( (numKeys == numVals) ); - - UDBG( ("SUTIL", "esock_encode_msghdr -> create msghdr map\r\n") ); - if (!MKMA(env, keys, vals, numKeys, &tmp)) - return ESOCK_STR_EINVAL; - - UDBG( ("SUTIL", "esock_encode_msghdr -> msghdr: " - "\r\n %T" - "\r\n", tmp) ); - - *eSockAddr = tmp; - } - - UDBG( ("SUTIL", "esock_encode_msghdr -> done\r\n") ); - - return NULL; -} - - - /* +++ esock_encode_iov +++ * * Encode a IO Vector. In erlang we represented this as a list of binaries. @@ -203,23 +112,20 @@ char* esock_encode_iov(ErlNifEnv* env, /* We have the exact amount - we are done */ UDBG( ("SUTIL", "esock_encode_iov -> exact => done\r\n") ); a[i] = MKBIN(env, &data[i]); - UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); - rem = 0; // Besserwisser + rem = 0; // Besserwisser done = TRUE; } else if (iov[i].iov_len < rem) { /* Filled another buffer - continue */ UDBG( ("SUTIL", "esock_encode_iov -> filled => continue\r\n") ); a[i] = MKBIN(env, &data[i]); rem -= iov[i].iov_len; - UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); } else if (iov[i].iov_len > rem) { /* Partly filled buffer (=> split) - we are done */ ERL_NIF_TERM tmp; UDBG( ("SUTIL", "esock_encode_iov -> split => done\r\n") ); tmp = MKBIN(env, &data[i]); a[i] = MKSBIN(env, tmp, 0, rem); - UDBG( ("SUTIL", "esock_encode_iov -> a[%d]: %T\r\n", i, a[i]) ); - rem = 0; // Besserwisser + rem = 0; // Besserwisser done = TRUE; } } @@ -235,165 +141,6 @@ char* esock_encode_iov(ErlNifEnv* env, -/* +++ esock_encode_cmsghdrs +++ - * - * Encode a list of cmsghdr(). The X can 0 or more cmsghdr blocks. - * - * Our problem is that we have no idea how many control messages - * we have. - * - * The cmsgHdrP arguments points to the start of the control data buffer, - * an actual binary. Its the only way to create sub-binaries. So, what we - * need to continue processing this is to tern that into an binary erlang - * term (which can then in turn be turned into sub-binaries). - * - * We need the cmsgBufP (even though cmsgHdrP points to it) to be able - * to create sub-binaries (one for each cmsg hdr). - * - * The TArray (term array) is created with the size of 128, which should - * be enough. But if its not, then it will be automatically realloc'ed during - * add. Once we are done adding hdr's to it, we convert the tarray to a list. - */ - -extern -char* esock_encode_cmsghdrs(ErlNifEnv* env, - ErlNifBinary* cmsgBinP, - struct msghdr* msgHdrP, - ERL_NIF_TERM* eCMsgHdr) -{ - ERL_NIF_TERM ctrlBuf = MKBIN(env, cmsgBinP); // The *entire* binary - SocketTArray cmsghdrs = TARRAY_CREATE(128); - struct cmsghdr* firstP = CMSG_FIRSTHDR(msgHdrP); - struct cmsghdr* currentP; - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> entry\r\n") ); - - for (currentP = firstP; - currentP != NULL; - currentP = CMSG_NXTHDR(msgHdrP, currentP)) { - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> process cmsg header when" - "\r\n TArray Size: %d" - "\r\n", TARRAY_SZ(cmsghdrs)) ); - - /* MUST check this since on Linux the returned "cmsg" may actually - * go too far! - */ - if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) > - msgHdrP->msg_controllen) { - /* Ouch, fatal error - give up - * We assume we cannot trust any data if this is wrong. - */ - TARRAY_DELETE(cmsghdrs); - return ESOCK_STR_EINVAL; - } else { - ERL_NIF_TERM level; - ERL_NIF_TERM type = MKI(env, currentP->cmsg_type); - unsigned char* dataP = (unsigned char*) CMSG_DATA(currentP); - size_t dataPos = dataP - cmsgBinP->data; - size_t dataLen = currentP->cmsg_len - (CHARP(currentP)-CHARP(dataP)); - ERL_NIF_TERM dataBin = MKSBIN(env, ctrlBuf, dataPos, dataLen); - - /* We can't give up just because its an unknown protocol, - * so if its a protocol we don't know, we return its integer - * value and leave it to the user. - */ - if (esock_encode_protocol(env, currentP->cmsg_level, &level) != NULL) - level = MKI(env, currentP->cmsg_level); - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> " - "\r\n level: %T" - "\r\n type: %T" - "\r\n", level, type) ); - - /* And finally create the 'cmsghdr' map - - * and if successfull add it to the tarray. - */ - { - ERL_NIF_TERM keys[] = {esock_atom_level, - esock_atom_type, - esock_atom_data}; - ERL_NIF_TERM vals[] = {level, type, dataBin}; - unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); - unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); - ERL_NIF_TERM cmsgHdr; - - /* Guard agains cut-and-paste errors */ - ESOCK_ASSERT( (numKeys == numVals) ); - - if (!MKMA(env, keys, vals, numKeys, &cmsgHdr)) { - TARRAY_DELETE(cmsghdrs); - return ESOCK_STR_EINVAL; - } - - /* And finally add it to the list... */ - TARRAY_ADD(cmsghdrs, cmsgHdr); - } - } - } - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> cmsg headers processed when" - "\r\n TArray Size: %d" - "\r\n", TARRAY_SZ(cmsghdrs)) ); - - /* The tarray is populated - convert it to a list */ - TARRAY_TOLIST(cmsghdrs, env, eCMsgHdr); - - return NULL; -} - - - -/* +++ esock_encode_mshghdr_flags +++ - * - * Encode a list of msghdr_flag(). - * - * The following flags are handled: eor | trunc | ctrunc | oob | errqueue. - */ - -extern -char* esock_encode_mshghdr_flags(ErlNifEnv* env, - int msgFlags, - ERL_NIF_TERM* flags) -{ - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> entry with" - "\r\n msgFlags: %d (0x%lX)" - "\r\n", msgFlags, msgFlags) ); - - if (msgFlags == 0) { - *flags = MKEL(env); - return NULL; - } else { - SocketTArray ta = TARRAY_CREATE(10); // Just to be on the safe side - - if ((msgFlags & MSG_EOR) == MSG_EOR) - TARRAY_ADD(ta, esock_atom_eor); - - if ((msgFlags & MSG_TRUNC) == MSG_TRUNC) - TARRAY_ADD(ta, esock_atom_trunc); - - if ((msgFlags & MSG_CTRUNC) == MSG_CTRUNC) - TARRAY_ADD(ta, esock_atom_ctrunc); - - if ((msgFlags & MSG_OOB) == MSG_OOB) - TARRAY_ADD(ta, esock_atom_oob); - - if ((msgFlags & MSG_ERRQUEUE) == MSG_ERRQUEUE) - TARRAY_ADD(ta, esock_atom_errqueue); - - UDBG( ("SUTIL", "esock_encode_cmsghdrs -> flags processed when" - "\r\n TArray size: %d" - "\r\n", TARRAY_SZ(ta)) ); - - TARRAY_TOLIST(ta, env, flags); - - return NULL; - } -} - - - - /* +++ esock_decode_sockaddr +++ * * Decode a socket address - sockaddr. In erlang its represented as -- cgit v1.2.3 From 90a150771faa3cf01e82919b0c17854de9987783 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 1 Aug 2018 19:42:32 +0200 Subject: [socket-nif] Processing of more cmsg headers Added processing or more cmsg headers (for more options). Now (also) supports: socket:timestamp. Also various fixes and cleanups. For some reason calling getopt(Sock, 0, {13, int}) (or similar) fails with badarg even though the nif-function (nif_getopt) actually returns a valid value (for instance: {ok, 0}). OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 73 +++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 232e8200df..59cd1a3408 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -227,6 +227,11 @@ char* esock_encode_sockaddr(ErlNifEnv* env, { char* xres; + UDBG( ("SUTIL", "esock_encode_sockaddr -> entry with" + "\r\n family: %d" + "\r\n addrLen: %d" + "\r\n", sockAddrP->sa.sa_family, addrLen) ); + switch (sockAddrP->sa.sa_family) { case AF_INET: xres = esock_encode_sockaddr_in4(env, &sockAddrP->in4, addrLen, eSockAddr); @@ -860,6 +865,74 @@ char* esock_encode_ip6_address(ErlNifEnv* env, +/* +++ esock_encode_timeval +++ + * + * Encode a timeval struct into its erlang form, a map with two fields: + * + * sec + * usec + * + */ +extern +char* esock_encode_timeval(ErlNifEnv* env, + struct timeval* timeP, + ERL_NIF_TERM* eTime) +{ + ERL_NIF_TERM keys[] = {esock_atom_sec, esock_atom_usec}; + ERL_NIF_TERM vals[] = {MKL(env, timeP->tv_sec), MKL(env, timeP->tv_usec)}; + + unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM); + unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM); + + ESOCK_ASSERT( (numKeys == numVals) ); + + if (!MKMA(env, keys, vals, numKeys, eTime)) + return ESOCK_STR_EINVAL; + + return NULL; +} + + + +/* +++ esock_decode_timeval +++ + * + * Decode a timeval in its erlang form (a map) into its native form, + * a timeval struct. + * + */ +extern +char* esock_decode_timeval(ErlNifEnv* env, + ERL_NIF_TERM eTime, + struct timeval* timeP) +{ + ERL_NIF_TERM eSec, eUSec; + size_t sz; + + // It must be a map + if (!IS_MAP(env, eTime)) + return ESOCK_STR_EINVAL; + + // It must have atleast two attributes + if (!enif_get_map_size(env, eTime, &sz) || (sz < 2)) + return ESOCK_STR_EINVAL; + + if (!GET_MAP_VAL(env, eTime, esock_atom_sec, &eSec)) + return ESOCK_STR_EINVAL; + + if (!GET_MAP_VAL(env, eTime, esock_atom_usec, &eUSec)) + return ESOCK_STR_EINVAL; + + if (!GET_LONG(env, eSec, &timeP->tv_sec)) + return ESOCK_STR_EINVAL; + + if (!GET_LONG(env, eUSec, &timeP->tv_usec)) + return ESOCK_STR_EINVAL; + + return NULL; +} + + + /* +++ esock_decode_domain +++ * * Decode the Erlang form of the 'domain' type, that is: -- cgit v1.2.3 From d8b1eace1cfe3184497752f345e5f6bc5def9769 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 3 Aug 2018 12:33:22 +0200 Subject: [socket-nif] Add preliminary support for sendmsg Added function sendmsg/2,3,4. Actually worked on the first try. Something must be wrong... Still no supported cmsghdr's (only support headers where the data part is already a binary, which therefor does not require any processing). So if the cmsghdrs actually work is unclear. OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 59 +++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 59cd1a3408..a73b40cd29 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -71,7 +71,7 @@ static char* make_sockaddr_un(ErlNifEnv* env, /* +++ esock_encode_iov +++ * - * Encode a IO Vector. In erlang we represented this as a list of binaries. + * Encode an IO Vector. In erlang we represented this as a list of binaries. * * We iterate through the IO vector, and as long as the remaining (rem) * number of bytes is greater than the size of the current buffer, we @@ -141,6 +141,61 @@ char* esock_encode_iov(ErlNifEnv* env, +/* +++ esock_decode_iov +++ + * + * Decode an IO Vector. In erlang we represented this as a list of binaries. + * + * We assume that we have already figured out how long the iov (actually + * eIOV) is (len), and therefor allocated an array of bins and iov to be + * used. + */ + +extern +char* esock_decode_iov(ErlNifEnv* env, + ERL_NIF_TERM eIOV, + ErlNifBinary* bufs, + struct iovec* iov, + size_t len, + ssize_t* totSize) +{ + uint16_t i; + ssize_t sz; + ERL_NIF_TERM elem, tail, list; + + UDBG( ("SUTIL", "esock_decode_iov -> entry with" + "\r\n (IOV) len: %d" + "\r\n", read, len) ); + + for (i = 0, list = eIOV, sz = 0; (i < len); i++) { + + UDBG( ("SUTIL", "esock_decode_iov -> " + "\r\n iov[%d].iov_len: %d" + "\r\n rem: %d" + "\r\n", i) ); + + if (!GET_LIST_ELEM(env, list, &elem, &tail)) + return ESOCK_STR_EINVAL; + + if (IS_BIN(env, elem) && GET_BIN(env, elem, &bufs[i])) { + iov[i].iov_base = bufs[i].data; + iov[i].iov_len = bufs[i].size; + sz += bufs[i].size; + } else { + return ESOCK_STR_EINVAL; + } + + list = tail; + } + + *totSize = sz; + + UDBG( ("SUTIL", "esock_decode_msghdr -> done (%d)\r\n", sz) ); + + return NULL; +} + + + /* +++ esock_decode_sockaddr +++ * * Decode a socket address - sockaddr. In erlang its represented as @@ -1100,7 +1155,7 @@ char* esock_encode_type(ErlNifEnv* env, -/* +++ esock_decode_protocol +++ +/* +++ esock_encode_protocol +++ * * Encode the native protocol to the Erlang form, that is: * -- cgit v1.2.3 From e3ace52f8e7d893d170ff5eb60d22c27ab9ec1e5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 20 Sep 2018 17:42:03 +0200 Subject: [socket-nif] Various fixes related to FreeBSD environment --- erts/emulator/nifs/common/socket_util.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index a73b40cd29..8bb725fb5b 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1238,7 +1238,11 @@ char* esock_decode_protocol(ErlNifEnv* env, *proto = IPPROTO_IP; #endif } else if (COMPARE(esock_atom_ipv6, eProto) == 0) { +#if defined(SOL_IPV6) *proto = SOL_IPV6; +#else + *proto = IPPROTO_IPV6; +#endif } else if (COMPARE(esock_atom_tcp, eProto) == 0) { *proto = IPPROTO_TCP; } else if (COMPARE(esock_atom_udp, eProto) == 0) { -- cgit v1.2.3 From 1c412c62ba3be17b7a818f264049a7ee7942351e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 28 Sep 2018 18:40:08 +0200 Subject: [socket-nif] Add support for socket (level otp) buffer options Add support for otp level socket options rcvbuf, rcvctrlbuf and sndctrlbuf. These options define default sizes for these buffers. The 'rcvbuf' is used when receiving messages when calling the recv, recvfrom and recvmsg functions. The 'rcvctrlbuf' is used for the control message header info when calling the recvmsg function. The 'sndctrlbuf' is used for the control message header info when calling the sendmsg function. OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 8bb725fb5b..ff50fd2384 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1261,6 +1261,35 @@ char* esock_decode_protocol(ErlNifEnv* env, +/* +++ esock_decode_bufsz +++ + * + * Decode an buffer size. The size of a buffer is: + * + * Sz > 0 => Use provided value + * Sz => Use provided default + * + */ +extern +char* esock_decode_bufsz(ErlNifEnv* env, + ERL_NIF_TERM eVal, + size_t defSz, + size_t* sz) +{ + int val; + + if (!GET_INT(env, eVal, &val)) + return ESOCK_STR_EINVAL; + + if (val > 0) + *sz = (size_t) val; + else + *sz = defSz; + + return NULL; +} + + + /* *** esock_decode_string *** * * Decode a string value. A successful decode results in an -- cgit v1.2.3 From 043624804888dc021a75b01c0a3d8c1c0a1fde23 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 16 Oct 2018 15:53:37 +0200 Subject: [socket-nif] Recv handling of closing sockets Fixed more issues regarding closing sockets. Specifically with respect to recv calls. Also, added demonitor for all *current* processes. Also fixed a buffer overflow problem when writing an warning message (the timestamp buffer was not large enough). OTP-14831 --- erts/emulator/nifs/common/socket_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index ff50fd2384..766d3724c1 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1506,7 +1506,7 @@ void esock_warning_msg( const char* format, ... ) { va_list args; char f[512 + sizeof(format)]; // This has to suffice... - char stamp[32]; + char stamp[64]; // Just in case... struct timespec ts; int res; -- cgit v1.2.3 From 875825874d4a8d52ec5cc593f5024afc696c29df Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 31 Jan 2019 11:34:03 +0100 Subject: [socket-nif] nosup expection of win32 and type(s) replacements The nif callback functions (nif_open) now instead cause an 'nosup' exception if called (instead of badarg). The basic type uint16_t, uint32_t and int32_t (C99) replaced "own" (that is, defined by "us") types Uint16, Uint32 and Sint32. The point of this is that our Windows build system seems to be a bit lacking when it comes to types... Removed "some stuff" that was if-defed. Different solution when win32 support for sockets has been improved. Make sure the socket_*.c util modules are not included in the building for windows. OTP-15526 --- erts/emulator/nifs/common/socket_util.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'erts/emulator/nifs/common/socket_util.c') diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index 766d3724c1..f6e4781977 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2018-2018. All Rights Reserved. + * Copyright Ericsson AB 2018-2019. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,9 +31,9 @@ #include #include "socket_int.h" +#include "sys.h" #include "socket_util.h" #include "socket_dbg.h" -#include "sys.h" /* We don't have a "debug flag" to check here, so we * should use the compile debug flag, whatever that is... @@ -89,7 +89,7 @@ char* esock_encode_iov(ErlNifEnv* env, ERL_NIF_TERM* eIOV) { int rem = read; - uint16_t i; + Uint16 i; BOOLEAN_T done = FALSE; ERL_NIF_TERM a[len]; // At most this length @@ -158,7 +158,7 @@ char* esock_decode_iov(ErlNifEnv* env, size_t len, ssize_t* totSize) { - uint16_t i; + Uint16 i; ssize_t sz; ERL_NIF_TERM elem, tail, list; -- cgit v1.2.3