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