diff options
-rw-r--r-- | erts/emulator/Makefile.in | 3 | ||||
-rw-r--r-- | erts/emulator/nifs/common/net_nif.c | 1788 | ||||
-rw-r--r-- | erts/preloaded/src/net.erl | 55 |
3 files changed, 1794 insertions, 52 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 6ef36ed3e4..3bfe393a12 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -1,3 +1,4 @@ + # # %CopyrightBegin% # @@ -636,6 +637,7 @@ GENERATE += $(TTF_DIR)/driver_tab.c PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ $(ERL_TOP)/erts/preloaded/ebin/erts_code_purger.beam \ $(ERL_TOP)/erts/preloaded/ebin/init.beam \ + $(ERL_TOP)/erts/preloaded/ebin/net.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_buffer.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_eval.beam \ $(ERL_TOP)/erts/preloaded/ebin/prim_inet.beam \ @@ -883,6 +885,7 @@ NIF_OBJS = \ $(OBJDIR)/prim_buffer_nif.o \ $(OBJDIR)/prim_file_nif.o \ $(OBJDIR)/socket_nif.o \ + $(OBJDIR)/net_nif.o \ $(OBJDIR)/zlib_nif.o ifeq ($(TARGET),win32) diff --git a/erts/emulator/nifs/common/net_nif.c b/erts/emulator/nifs/common/net_nif.c new file mode 100644 index 0000000000..b8fa6628a7 --- /dev/null +++ b/erts/emulator/nifs/common/net_nif.c @@ -0,0 +1,1788 @@ +/* + * %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 : The NIF (C) part of the net interface + * This is a module of miscellaneous functions. + * ---------------------------------------------------------------------- + * + */ + +/* #include <stdio.h> */ +/* #include <stdlib.h> */ +/* #include <stdarg.h> */ +/* #include <string.h> */ +/* #include <unistd.h> */ +/* #include <errno.h> */ +/* #include <netdb.h> */ +/* #include <sys/types.h> */ +/* #include <sys/wait.h> */ +/* #include <sys/socket.h> */ +/* #include <netinet/in.h> */ +/* #include <arpa/inet.h> */ +/* #include <sys/time.h> */ +/* #include <fcntl.h> */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* If we HAVE_SCTP_H and Solaris, we need to define the following in + * order to get SCTP working: + */ +#if (defined(HAVE_SCTP_H) && defined(__sun) && defined(__SVR4)) +#define SOLARIS10 1 +/* WARNING: This is not quite correct, it may also be Solaris 11! */ +#define _XPG4_2 +#define __EXTENSIONS__ +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <ctype.h> +#include <sys/types.h> +#include <errno.h> +#include <netinet/ip.h> + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_SYS_UIO_H +#include <sys/uio.h> +#endif + +#ifdef HAVE_NET_IF_DL_H +#include <net/if_dl.h> +#endif + +#ifdef HAVE_IFADDRS_H +#include <ifaddrs.h> +#endif + +#ifdef HAVE_NETPACKET_PACKET_H +#include <netpacket/packet.h> +#endif + +#ifdef HAVE_SYS_UN_H +#include <sys/un.h> +#endif + +/* SENDFILE STUFF HERE IF WE NEED IT... */ + +#if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__) +#define __DARWIN__ 1 +#endif + + +#ifdef __WIN32__ +#define STRNCASECMP strncasecmp +#define INCL_WINSOCK_API_TYPEDEFS 1 + +#ifndef WINDOWS_H_INCLUDES_WINSOCK2_H +#include <winsock2.h> +#endif +#include <windows.h> +#include <Ws2tcpip.h> /* NEED VC 6.0 or higher */ + +/* Visual studio 2008+: NTDDI_VERSION needs to be set for iphlpapi.h + * to define the right structures. It needs to be set to WINXP (or LONGHORN) + * for IPV6 to work and it's set lower by default, so we need to change it. + */ +#ifdef HAVE_SDKDDKVER_H +# include <sdkddkver.h> +# ifdef NTDDI_VERSION +# undef NTDDI_VERSION +# endif +# define NTDDI_VERSION NTDDI_WINXP +#endif +#include <iphlpapi.h> + +#undef WANT_NONBLOCKING +#include "sys.h" + +#else /* !__WIN32__ */ + +#include <sys/time.h> +#ifdef NETDB_H_NEEDS_IN_H +#include <netinet/in.h> +#endif +#include <netdb.h> + +#include <sys/socket.h> +#include <netinet/in.h> + +#ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H +#include <rpc/types.h> +#endif + +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <arpa/inet.h> + +#include <sys/param.h> +#ifdef HAVE_ARPA_NAMESER_H +#include <arpa/nameser.h> +#endif + +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#include <net/if.h> + +#ifdef HAVE_SCHED_H +#include <sched.h> +#endif + +#ifdef HAVE_SETNS_H +#include <setns.h> +#endif + +#define HAVE_UDP + +#ifndef WANT_NONBLOCKING +#define WANT_NONBLOCKING +#endif +#include "sys.h" + +#endif + +#include <erl_nif.h> + + +/* All platforms fail on malloc errors. */ +#define FATAL_MALLOC + + + +/* *** Boolean *type* stuff... *** */ +typedef unsigned int BOOLEAN_T; +#define TRUE 1 +#define FALSE 0 +#define BOOL2STR(__B__) ((__B__) ? "true" : "false") +#define BOOL2ATOM(__B__) ((__B__) ? atom_true : atom_false) + +/* Two byte integer decoding */ +#define get_int16(s) ((((unsigned char*) (s))[0] << 8) | \ + (((unsigned char*) (s))[1])) + +#define SASSERT(e) \ + ((void) ((e) ? 1 : (xabort(#e, __func__, __FILE__, __LINE__), 0))) + + +/* Debug stuff... */ +#define SOCKET_NIF_DEBUG_DEFAULT TRUE + +/* Various defaults... */ +#define SOCKET_DEBUG_DEFAULT TRUE +#define SOCKET_IOW_DEFAULT FALSE + +/* Counters and stuff (Don't know where to sent this stuff anyway) */ +#define SOCKET_NIF_IOW_DEFAULT FALSE + + +/* Used in debug printouts */ +#ifdef __WIN32__ +#define LLU "%I64u" +#else +#define LLU "%llu" +#endif +typedef unsigned long long llu_t; + + + +/* Socket stuff */ +// #define INVALID_SOCKET -1 +// #define INVALID_EVENT -1 +// #define SOCKET_ERROR -1 + +// #define SOCKET int +// #define HANDLE long int + + +/* *** Misc macros and defines *** */ + +#ifdef __WIN32__ +#define get_errno() WSAGetLastError() +#else +#define get_errno() errno +#endif + +// #if defined(TCP_CA_NAME_MAX) +// #define SOCKET_OPT_TCP_CONGESTION_NAME_MAX TCP_CA_NAME_MAX +// #else +/* This is really excessive, but just in case... */ +// #define SOCKET_OPT_TCP_CONGESTION_NAME_MAX 256 +// #endif + +#define HOSTNAME_LEN 256 +#define SERVICE_LEN 256 + + +/* =================================================================== * + * * + * Various enif macros * + * * + * =================================================================== */ + +#define MALLOC(SZ) enif_alloc((SZ)) +#define FREE(P) enif_free((P)) + +#define MKA(E,S) enif_make_atom((E), (S)) +#define MKBIN(E,B) enif_make_binary((E), (B)) +#define MKI(E,I) enif_make_int((E), (I)) +#define MKLA(E,A,L) enif_make_list_from_array((E), (A), (L)) +#define MKEL(E) enif_make_list((E), 0) +#define MKREF(E) enif_make_ref((E)) +#define MKS(E,S) enif_make_string((E), (S), ERL_NIF_LATIN1) +#define MKSL(E,S,L) enif_make_string_len((E), (S), (L), ERL_NIF_LATIN1) +#define MKSBIN(E,B,ST,SZ) enif_make_sub_binary((E), (B), (ST), (SZ)) +#define MKT2(E,E1,E2) enif_make_tuple2((E), (E1), (E2)) +#define MKT3(E,E1,E2,E3) enif_make_tuple3((E), (E1), (E2), (E3)) +#define MKT4(E,E1,E2,E3,E4) enif_make_tuple4((E), (E1), (E2), (E3), (E4)) +#define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ + enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) + +#define MCREATE(N) enif_mutex_create((N)) +#define MDESTROY(M) enif_mutex_destroy((M)) +#define MLOCK(M) enif_mutex_lock((M)) +#define MUNLOCK(M) enif_mutex_unlock((M)) + +#define MONP(E,D,P,M) enif_monitor_process((E), (D), (P), (M)) +#define DEMONP(E,D,M) enif_demonitor_process((E), (D), (M)) + +#define SELECT(E,FD,M,O,P,R) \ + enif_select((E), (FD), (M), (O), (P), (R)) +#define SELECT_READ(E, DP, P, R) \ + SELECT((E), (DP)->sock, (ERL_NIF_SELECT_READ), (DP), (P), (R)) +#define SELECT_WRITE(E, DP, P, R) \ + SELECT((E), (DP)->sock, (ERL_NIF_SELECT_WRITE), (DP), (P), (R)) +#define SELECT_STOP(E, DP) \ + enif_select((E), (DP)->sock, (ERL_NIF_SELECT_STOP), (DP), NULL, atom_undefined) + +#define IS_ATOM(E, TE) enif_is_atom((E), (TE)) +#define IS_BIN(E, TE) enif_is_binary((E), (TE)) +#define IS_NUM(E, TE) enif_is_number((E), (TE)) +#define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) +#define IS_LIST(E, TE) enif_is_list((E), (TE)) + +#define COMPARE(L, R) enif_compare((L), (R)) + +#define GET_ATOM_LEN(E, TE, LP) \ + enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) +#define GET_ATOM(E, TE, BP, MAX) \ + enif_get_atom((E), (TE), (BP), (MAX), ERL_NIF_LATIN1) +#define GET_BIN(E, TE, BP) enif_inspect_iolist_as_binary((E), (TE), (BP)) +#define GET_INT(E, TE, IP) enif_get_int((E), (TE), (IP)) +#define GET_LIST_ELEM(E, L, HP, TP) enif_get_list_cell((E), (L), (HP), (TP)) +#define GET_LIST_LEN(E, L, LP) enif_get_list_length((E), (L), (LP)) +#define GET_STR(E, L, B, SZ) \ + enif_get_string((E), (L), (B), (SZ), ERL_NIF_LATIN1) +#define GET_UINT(E, TE, IP) enif_get_uint((E), (TE), (IP)) +#define GET_TUPLE(E, TE, TSZ, TA) enif_get_tuple((E), (TE), (TSZ), (TA)) + +#define ALLOC_BIN(SZ, BP) enif_alloc_binary((SZ), (BP)) +#define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) + + +#ifdef HAVE_SOCKLEN_T +# define SOCKLEN_T socklen_t +#else +# define SOCKLEN_T size_t +#endif + + +/* The general purpose socket address */ +typedef union { + struct sockaddr sa; + + struct sockaddr_in in; + +#ifdef HAVE_IN6 + struct sockaddr_in6 in6; +#endif + +} SockAddress; + + + +/* ---------------------------------------------------------------------- + * F o r w a r d s + * ---------------------------------------------------------------------- + */ + +/* THIS IS JUST TEMPORARY */ +extern char* erl_errno_id(int error); + + +static ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_info(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); + +static ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); + +static ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); + +static ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM nif_if_names(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]); + +static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, + const SockAddress* saP, + socklen_t saLen, + int flags); +static ERL_NIF_TERM nif_name2index(ErlNifEnv* env, + char* ifn); +static ERL_NIF_TERM nif_index2name(ErlNifEnv* env, + unsigned int id); +static ERL_NIF_TERM nif_names(ErlNifEnv* env); +static unsigned int nif_names_length(struct if_nameindex* p); + +/* +static void net_dtor(ErlNifEnv* env, void* obj); +static void net_stop(ErlNifEnv* env, + void* obj, + int fd, + int is_direct_call); +static void net_down(ErlNifEnv* env, + void* obj, + const ErlNifPid* pid, + const ErlNifMonitor* mon); +*/ +static BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM eAddr, + SockAddress* saP, + socklen_t* saLen); +static BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* addrt, + SockAddress* saP, + socklen_t* saLen); +#if defined(HAVE_IN6) && defined(AF_INET6) +static BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* addrt, + SockAddress* saP, + socklen_t* saLen); +#endif +static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, + const ERL_NIF_TERM eflags, + int* flags); +static BOOLEAN_T decode_nameinfo_flags_list(ErlNifEnv* env, + const ERL_NIF_TERM eflags, + int* flags); +static ERL_NIF_TERM encode_address_info(ErlNifEnv* env, + struct addrinfo* addrInfo); +static unsigned int address_info_length(struct addrinfo* addrInfoP); + +static ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM val); +// static ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2); +static ERL_NIF_TERM make_error(ErlNifEnv* env, ERL_NIF_TERM reason); +static ERL_NIF_TERM make_error1(ErlNifEnv* env, char* reason); +static ERL_NIF_TERM make_error2(ErlNifEnv* env, int err); + +/* +static void xabort(const char* expr, + const char* func, + const char* file, + int line); +*/ + +static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); + + +#if HAVE_IN6 +# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY +# if HAVE_DECL_IN6ADDR_ANY_INIT +static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } }; +# else +static const struct in6_addr in6addr_any = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; +# endif /* HAVE_IN6ADDR_ANY_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_ANY */ + +# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK +# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT +static const struct in6_addr in6addr_loopback = + { { IN6ADDR_LOOPBACK_INIT } }; +# else +static const struct in6_addr in6addr_loopback = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }; +# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */ +#endif /* HAVE_IN6 */ + + + +/* *** String constants *** */ +static char str_dgram[] = "dgram"; +static char str_error[] = "error"; +static char str_idn[] = "idn"; +static char str_idna_allow_unassigned[] = "idna_allow_unassigned"; +static char str_idna_use_std3_ascii_rules[] = "idna_use_std3_ascii_rules"; +static char str_namereqd[] = "namereqd"; +static char str_name_info[] = "name_info"; +static char str_nofqdn[] = "nofqdn"; +static char str_numerichost[] = "numerichost"; +static char str_numericserv[] = "numericserv"; +static char str_ok[] = "ok"; +static char str_true[] = "true"; +static char str_undefined[] = "undefined"; + +// static char str_lowdelay[] = "lowdelay"; +// static char str_throughput[] = "throughput"; +// static char str_reliability[] = "reliability"; +// static char str_mincost[] = "mincost"; + +/* (special) error string constants */ +// static char str_eafnosupport[] = "eafnosupport"; +static char str_eagain[] = "eagain"; +static char str_ebadflags[] = "ebadflags"; +static char str_efail[] = "efail"; +static char str_efamily[] = "efamily"; +static char str_einval[] = "einval"; +// static char str_eisconn[] = "eisconn"; +static char str_emem[] = "emem"; +static char str_enoname[] = "enoname"; +// static char str_enotclosing[] = "enotclosing"; +// static char str_enotconn[] = "enotconn"; +static char str_eoverflow[] = "eoverflow"; +// static char str_exalloc[] = "exalloc"; +// static char str_exbadstate[] = "exbadstate"; +// static char str_exbusy[] = "exbusy"; +// static char str_exmon[] = "exmonitor"; // failed monitor +// static char str_exself[] = "exself"; // failed self +// static char str_exsend[] = "exsend"; // failed send + + +/* *** Atoms *** */ + +static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_dgram; +static ERL_NIF_TERM atom_idn; +static ERL_NIF_TERM atom_idna_allow_unassigned; +static ERL_NIF_TERM atom_idna_use_std3_ascii_rules; +static ERL_NIF_TERM atom_namereqd; +static ERL_NIF_TERM atom_name_info; +static ERL_NIF_TERM atom_nofqdn; +static ERL_NIF_TERM atom_numerichost; +static ERL_NIF_TERM atom_numericserv; +static ERL_NIF_TERM atom_ok; +// static ERL_NIF_TERM atom_select; +// static ERL_NIF_TERM atom_timeout; +static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_undefined; + +// static ERL_NIF_TERM atom_lowdelay; +// static ERL_NIF_TERM atom_throughput; +// static ERL_NIF_TERM atom_reliability; +// static ERL_NIF_TERM atom_mincost; + +// static ERL_NIF_TERM atom_eafnosupport; +static ERL_NIF_TERM atom_eagain; +static ERL_NIF_TERM atom_ebadflags; +static ERL_NIF_TERM atom_efail; +static ERL_NIF_TERM atom_efamily; +static ERL_NIF_TERM atom_einval; +// static ERL_NIF_TERM atom_eisconn; +static ERL_NIF_TERM atom_emem; +static ERL_NIF_TERM atom_enoname; +// static ERL_NIF_TERM atom_enotclosing; +// static ERL_NIF_TERM atom_enotconn; +static ERL_NIF_TERM atom_eoverflow; +// static ERL_NIF_TERM atom_exalloc; +// static ERL_NIF_TERM atom_exbadstate; +// static ERL_NIF_TERM atom_exbusy; +// static ERL_NIF_TERM atom_exmon; +// static ERL_NIF_TERM atom_exself; +// static ERL_NIF_TERM atom_exsend; + +/* *** net *** */ +static ErlNifResourceType* net; +/* Maybe all of these whould be NULL? */ +static ErlNifResourceTypeInit netInit = { + NULL, // net_dtor, + NULL, // net_stop, + NULL // (ErlNifResourceDown*) net_down +}; + + + +/* ---------------------------------------------------------------------- + * N I F F u n c t i o n s + * ---------------------------------------------------------------------- + * + * Utility and admin functions: + * ---------------------------- + * nif_is_loaded/0 + * nif_info/0 + * + * The "proper" net functions: + * ------------------------------ + * nif_getnameinfo/2 + * nif_getaddrinfo/3 + * nif_if_name2index/1 + * nif_if_index2name/1 + * nif_if_names/0 + * + */ + + +/* ---------------------------------------------------------------------- + * nif_is_loaded + * + * Description: + * This functions only purpose is to return the atom 'true'. + * This will happen *if* the (socket) nif library is loaded. + * If its not, the erlang (nif_is_loaded) will instead return + * 'false'. + */ +static +ERL_NIF_TERM nif_is_loaded(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 0) + return enif_make_badarg(env); + + return atom_true; +} + + +/* ---------------------------------------------------------------------- + * nif_info + * + * Description: + * This is currently just a placeholder... + */ +static +ERL_NIF_TERM nif_info(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM info = enif_make_new_map(env); + return info; +} + + + +/* ---------------------------------------------------------------------- + * nif_getnameinfo + * + * Description: + * Address-to-name translation in protocol-independent manner. + * + * Arguments: + * SockAddr - Socket Address (address and port) + * Flags - The flags argument modifies the behavior of getnameinfo(). + * Not used! + */ + +static +ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM eSockAddr; + unsigned int eFlags; + int flags = 0; // Just in case... + SockAddress sa; + socklen_t saLen = 0; // Just in case... + + if ((argc != 2) || + !GET_UINT(env, argv[1], &eFlags)) { + return enif_make_badarg(env); + } + eSockAddr = argv[0]; + + if (!decode_nameinfo_flags(env, eFlags, &flags)) + return enif_make_badarg(env); + + if (decode_in_sockaddr(env, eSockAddr, &sa, &saLen)) + return enif_make_badarg(env); + + return ngetnameinfo(env, &sa, saLen, flags); +} + + + +/* Given the provided sock(et) address (and honts), retreive the host and + * service info. + */ +static +ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, + const SockAddress* saP, + socklen_t saLen, + int flags) +{ + ERL_NIF_TERM result; + char host[HOSTNAME_LEN]; + socklen_t hostLen = sizeof(host); + char serv[SERVICE_LEN]; + socklen_t servLen = sizeof(serv); + + int res = getnameinfo((struct sockaddr*) saP, saLen, + host, hostLen, + serv, servLen, + flags); + + switch (res) { + case 0: + { + ERL_NIF_TERM info = MKT3(env, + atom_name_info, + MKS(env, host), + MKS(env, serv)); + result = make_ok2(env, info); + } + break; + + case EAI_AGAIN: + result = make_error(env, atom_eagain); + break; + + case EAI_BADFLAGS: + result = make_error(env, atom_ebadflags); + break; + + case EAI_FAIL: + result = make_error(env, atom_efail); + break; + + case EAI_FAMILY: + result = make_error(env, atom_efamily); + break; + + case EAI_MEMORY: + result = make_error(env, atom_emem); + break; + + case EAI_NONAME: + result = make_error(env, atom_enoname); + break; + + case EAI_OVERFLOW: + result = make_error(env, atom_eoverflow); + break; + + case EAI_SYSTEM: + result = make_error2(env, get_errno()); + break; + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + + +/* ---------------------------------------------------------------------- + * nif_getaddrinfo + * + * Description: + * Network address and service translation. + * + * Arguments: + * Host - Host name (either a string or the atom undefined) + * Service - Service name (either a string or the atom undefined) + * Hints - Hints for the lookup (address info record) (currently *ignored*) + */ + +static +ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM result; + char* hostName; + char* servName; + // struct addrinfo* hints; + + if (argc != 3) { + return enif_make_badarg(env); + } + eHostName = argv[0]; + eServName = argv[1]; + eHints = argv[2]; + + if (!decode_addrinfo_string(env, eHostName, &hostName)) + return enif_make_badarg(env); + + if (!decode_addrinfo_string(env, eServName, &servName)) + return enif_make_badarg(env); + + /* + if (decode_addrinfo_hints(env, eHints, &hints)) + return enif_make_badarg(env); + */ + + if ((hostName == NULL) && (servName == NULL)) + return enif_make_badarg(env); + + result = ngetaddrinfo(env, hostName, servName); + + if (hostName != NULL) + FREE(hostName); + + if (servName != NULL) + FREE(servName); + + /* + if (hints != NULL) + FREE(hints); + */ + + return result; +} + + +static +ERL_NIF_TERM nifgetaddrinfo(ErlNifEnv* env, + char* host, + char* serv) +{ + struct addrinfo* addrInfoP; + int res; + + res = getaddrinfo(host, serv, NULL, &addrInfoP); + + switch (res) { + case 0: + { + ERL_NIF_TERM addrInfo = encode_address_info(env, addrInfoP); + freeaddrinfo(addrInfoP); + result = make_ok2(env, addrInfo); + } + break; + + case EAI_ADDRFAMILY: + result = make_error(env, atom_eaddrfamily); + break; + + case EAI_AGAIN: + result = make_error(env, atom_eagain); + break; + + case EAI_BADFLAGS: + result = make_error(env, atom_ebadflags); + break; + + case EAI_FAIL: + result = make_error(env, atom_efail); + break; + + case EAI_FAMILY: + result = make_error(env, atom_efamily); + break; + + case EAI_MEMORY: + result = make_error(env, atom_emem); + break; + + case EAI_NODATA: + result = make_error(env, atom_enodata); + break; + + case EAI_NONAME: + result = make_error(env, atom_enoname); + break; + + case EAI_SERCVICE: + result = make_error(env, atom_eservice); + break; + + case EAI_SOCKTYPE: + result = make_error(env, atom_esocktype); + break; + + case EAI_SYSTEM: + result = make_error(env, atom_esystem); + break; + + default: + result = make_error(env, atom_einval); + break; + } + + return result; +} + + +/* ---------------------------------------------------------------------- + * nif_if_name2index + * + * Description: + * Perform a Interface Name to Interface Index translation. + * + * Arguments: + * Ifn - Interface name to be translated. + */ + +static +ERL_NIF_TERM nif_if_name2index(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + ERL_NIF_TERM eifn; + char ifn[IF_NAMESIZE+1]; + + if (argc != 1) { + return enif_make_badarg(env); + } + eifn = argv[0]; + + if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) + return make_error2(env, atom_einval); + + return nif_name2index(env, ifn); +} + + + +static +ERL_NIF_TERM nif_name2index(ErlNifEnv* env, + char* ifn) +{ + unsigned int idx = if_nametoindex(ifn); + + if (idx == 0) + return make_error2(env, get_errno()); + else + return make_ok2(env, idx); + +} + + + +/* ---------------------------------------------------------------------- + * nif_if_index2name + * + * Description: + * Perform a Interface Index to Interface Name translation. + * + * Arguments: + * Idx - Interface index to be translated. + */ + +static +ERL_NIF_TERM nif_if_index2name(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + unsigned int idx; + + if ((argc != 1) || + !GET_UINT(env, argv[0], &idx)) { + return enif_make_badarg(env); + } + + return nif_index2name(env, idx); +} + + + +static +ERL_NIF_TERM nif_index2name(ErlNifEnv* env, + unsigned int idx) +{ + ERL_NIF_TERM result; + char* ifn = MALLOC(IF_NAMESIZE+1); + + if (ifn == NULL) + return enif_make_badarg(env); // PLACEHOLDER + + if (NULL == if_indextoname(idx, ifn)) { + result = make_ok2(env, MKS(env, ifn)); + } else { + result = make_error2(env, get_errno()); + } + + FREE(ifn); + + return result; +} + + + +/* ---------------------------------------------------------------------- + * nif_if_names + * + * Description: + * Get network interface names and indexes. + * + */ + +static +ERL_NIF_TERM nif_if_names(ErlNifEnv* env, + int argc, + const ERL_NIF_TERM argv[]) +{ + if (argc != 0) { + return enif_make_badarg(env); + } + + return nif_names(env); +} + + + +static +ERL_NIF_TERM nif_names(ErlNifEnv* env) +{ + ERL_NIF_TERM result; + struct if_nameindex* ifs = if_nameindex(); + + if (ifs == NULL) { + result = make_error2(env, get_errno()); + } else { + /* + * We got some interfaces: + * 1) Calculate how many - the only way is to iterate through the list + * until its end (which is indicated by an entry with index = zero + * and if_name = NULL). + * 2) Allocate an ERL_NIF_TERM array of the calculated length. + * 3) Iterate through the array of interfaces and for each create + * a two tuple: {Idx, If} + * + * Or shall we instead build a list in reverse order and then when + * its done, reverse that? Check + */ + unsigned int len = nif_names_length(ifs); + + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i; + + for (i = 0; i < len; i++) { + array[i] = MKT2(env, + MKI(env, ifs[i].if_index), + MKS(env, ifs[i].if_name)); + } + + result = make_ok2(env, MKLA(env, array, len)); + FREE(array); + } else { + result = make_ok2(env, enif_make_list(env, 0)); + } + } + + if (ifs != NULL) + if_freenameindex(ifs); + + return result; +} + + +static +unsigned int nif_names_length(struct if_nameindex* p) +{ + unsigned int len = 0; + + while ((p[len].if_index == 0) && (p[len].if_name == NULL)) { + len++; + } + + return len; +} + + + +/* ---------------------------------------------------------------------- + * U t i l i t y F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* Decode an in_sockaddr (the socket address). + * This is the (erlang) type: in_sockaddr(), which is either + * a in4_sockaddr() (tuple of size 3) or a in6_sockaddr() + * (tuple of size 5). See the net erlang module for details. + * + * We first detect which which of the tuples it is: + * - Size 3: maybe in4_sockaddr + * - Size 5: maybe in6_sockaddr + */ +static +BOOLEAN_T decode_in_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM eAddr, + SockAddress* saP, + socklen_t* saLen) +{ + const ERL_NIF_TERM* addrt; + int addrtSz; + + if (!GET_TUPLE(env, eAddr, &addrtSz, &addrt)) + return FALSE; + + switch (addrtSz) { + case 3: + return decode_in4_sockaddr(env, addrt, saP, saLen); + break; + +#ifdef HAVE_IN6 + case 5: + return decode_in6_sockaddr(env, addrt, saP, saLen); + break; +#endif + + default: + return FALSE; + break; + } + +} + + +/* Decode an in4_sockaddr record. This is a tuple of size 3. + * The size has already been verified, but not its content. + * So, the first element should be the atom 'in4_sockaddr'. + * The second the port number, an integer. And the third, + * the actual ip address, a 4-tuple. + */ +static +BOOLEAN_T decode_in4_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* addrt, + SockAddress* saP, + socklen_t* saLen) +{ + unsigned int len; + char tag[16]; // Just in case... + int port; + int ipAddrSz; + const ERL_NIF_TERM* ipAddrT; + int a, v; + char addr[4]; + + /* The first element: Verify record tag (atom()): in4_sockaddr */ + + if (!(GET_ATOM_LEN(env, addrt[0], &len) && + (len > 0) && + (len <= (sizeof(tag))))) + return FALSE; + + if (!GET_ATOM(env, addrt[0], tag, sizeof(tag))) + return FALSE; + + if (strncmp(tag, "in4_sockaddr", len) != 0) + return FALSE; + + + /* Get second element: port number (integer) */ + + if (!GET_INT(env, addrt[1], &port)) + return FALSE; + + + /* And finally, get the third element, the ip address (a 4 tuple) */ + + if (!GET_TUPLE(env, addrt[2], &ipAddrSz, &ipAddrT)) + return FALSE; + + if (ipAddrSz != 4) + return FALSE; + + + /* And finally initialize the sockaddr structure (and size) */ + sys_memzero((char*)saP, sizeof(struct sockaddr_in)); + saP->in.sin_family = AF_INET; + saP->in.sin_port = htons(port); + for (a = 0; a < 4; a++) { + if (!GET_INT(env, ipAddrT[a], &v)) + return FALSE; + addr[a] = v; + } + sys_memcpy(&saP->in.sin_addr, &addr, sizeof(addr)); + *saLen = sizeof(struct sockaddr_in); + return TRUE; +} + + + +#if defined(HAVE_IN6) && defined(AF_INET6) +/* Decode an in6_sockaddr record. This is a tuple of size 5. + * The size has already been verified, but not its content. + * So, the first element should be the atom 'in6_sockaddr'. + * The second the port number, an integer. The third, the + * actual ip address, a 8-tuple. The forth, the flowinfo, + * an integer. And finally, the fifth element, the scope_id, + * also an integer. *Not used here*. + */ +static +BOOLEAN_T decode_in6_sockaddr(ErlNifEnv* env, + const ERL_NIF_TERM* addrt, + SockAddress* saP, + socklen_t* saLen) +{ + unsigned int len; + char tag[16]; // Just in case... + int port; + int ipAddrSz; + const ERL_NIF_TERM* ipAddrT; + int flowInfo; + int a, v; + char addr[16]; + + + /* The first element: Verify record tag (atom()): in6_sockaddr */ + + if (!(GET_ATOM_LEN(env, addrt[0], &len) && + (len > 0) && + (len <= (sizeof(tag))))) + return FALSE; + + if (!GET_ATOM(env, addrt[0], tag, sizeof(tag))) + return FALSE; + + if (strncmp(tag, "in6_sockaddr", len) != 0) + return FALSE; + + + /* Get second element: port number (integer) */ + + if (!GET_INT(env, addrt[1], &port)) + return FALSE; + + + /* Get the third element, the ip address (a 8 tuple) */ + + if (!GET_TUPLE(env, addrt[2], &ipAddrSz, &ipAddrT)) + return FALSE; + + if (ipAddrSz != 8) + return FALSE; + + + /* And finally, get the forth element, the flowinfo (integer) */ + + if (!GET_INT(env, addrt[3], &flowInfo)) + return FALSE; + + + /* And finally initialize the sockaddr structure (and size) */ + + sys_memzero((char*)saP, sizeof(struct sockaddr_in6)); + saP->in6.sin6_family = AF_INET6; + saP->in6.sin6_port = htons(port); + saP->in6.sin6_flowinfo = flowInfo; + /* The address tuple is of size 8 + * and each element is a two byte integer + */ + for (a = 0; a < 8; a++) { + if (!GET_INT(env, addrt[a], &v)) + return FALSE; + addr[a*2 ] = ((v >> 8) & 0xFF); + addr[a*2+1] = (v & 0xFF); + } + sys_memcpy(&saP->in6.sin6_addr, &addr, sizeof(addr)); + *saLen = sizeof(struct sockaddr_in6); + + return TRUE; +} +#endif + + + +/* The erlang format for a set of flags is a list of atoms. + * A special case is when there is no flags, which is + * represented by the atom undefined. + */ +static +BOOLEAN_T decode_nameinfo_flags(ErlNifEnv* env, + const ERL_NIF_TERM eflags, + int* flags) +{ + BOOLEAN_T result; + + if (IS_ATOM(env, eflags)) { + if (COMPARE(eflags, atom_undefined) == 0) { + *flags = 0; + result = TRUE; + } else { + result = FALSE; + } + } else if (IS_LIST(env, eflags)) { + result = decode_nameinfo_flags_list(env, eflags, flags); + } else { + result = FALSE; + } + + return result; +} + + + +static +BOOLEAN_T decode_nameinfo_flags_list(ErlNifEnv* env, + const ERL_NIF_TERM eflags, + int* flags) +{ + ERL_NIF_TERM elem, tail, list = eflags; + int tmp = 0; + BOOLEAN_T done = FALSE; + + while (!done) { + if (GET_LIST_ELEM(env, list, &elem, &tail)) { + if (COMPARE(elem, atom_namereqd) == 0) { + tmp |= NI_NAMEREQD; + } else if (COMPARE(elem, atom_dgram) == 0) { + tmp |= NI_DGRAM; + } else if (COMPARE(elem, atom_nofqdn) == 0) { + tmp |= NI_NOFQDN; + } else if (COMPARE(elem, atom_numerichost) == 0) { + tmp |= NI_NUMERICHOST; + } else if (COMPARE(elem, atom_numericserv) == 0) { + tmp |= NI_NUMERICSERV; + + /* Starting with glibc 2.3.4: */ + +#if defined(NI_IDN) + } else if (COMPARE(elem, atom_idn) == 0) { + tmp |= NI_IDN; +#endif + +#if defined(NI_IDN_ALLOW_UNASSIGNED) + } else if (COMPARE(elem, atom_idna_allow_unassigned) == 0) { + tmp |= NI_IDN_ALLOW_UNASSIGNED; +#endif + +#if defined(NI_IDN_USE_STD3_ASCII_RULES) + } else if (COMPARE(elem, atom_idna_use_std3_ascii_rules) == 0) { + tmp |= NI_IDN_USE_STD3_ASCII_RULES; +#endif + + } else { + return FALSE; + } + + list = tail; + + } else { + done = TRUE; + } + } + + *flags = tmp; + + return TRUE; +} + + + +/* Decode the address info string (hostname or service name) + * The string is either the atom undefined or an actual string. + */ +static +BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, + const ERL_NIF_TERM eString, + char** stringP) +{ + BOOLEAN_T result; + + if (IS_ATOM(env, eString)) { + if (COMPARE(eString, atom_undefined) == 0) { + *stringP = NULL; + result = TRUE; + } else { + *stringP = NULL; + result = FALSE; + } + } else { + unsigned int len; + char* bufP; + + if (!GET_LIST_LEN(env, eString, &len) && (len != 0)) { + *stringP = NULL; + result = FALSE; + } + + bufP = ALLOC(len); + + if (GET_STR(env, eString, bufP, len)) { + *stringP = bufP; + result = TRUE; + } else { + *stringP = NULL; + result = FALSE; + } + } + + return result; + +} + + + +/* Encode the address info + * The address info is a linked list och address info, which + * will result in the result being a list of zero or more length. + */ +static +ERL_NIF_TERM encode_address_info(ErlNifEnv* env, + struct addrinfo* addrInfo) +{ + ERL_NIF_TERM result; + unsigned int len = address_info_length(addrInfo); + + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i; + + for (i = 0; i < len; i++) { + array[i] = make_address_info(env, &addrInfo[i]); + } + + result = mkake_ok2(env, MKLA(env, array, len)); + } else { + result = MKEL(env); + } + + return result; +} + + + +/* Calculate the length of the adress info linked list + * The list is NULL-terminated, so the only way is to + * iterate through the list until we find next = NULL. + */ +static +unsigned int address_info_length(struct addrinfo* addrInfoP) +{ + unsigned int len; + struct addrinfo* tmp = addrInfoP; + + if (tmp != NULL) { + len = 1; + while (tmp->ai_next != NULL) { + tmp = tmp->ai_next; + len++; + } + } else { + len = 0; + } + + return len; +} + + + +/* Create one (erlang) instance of the address info record + * Should we have address info as a record or as a map? + * + * {address_info, Fam, Type, Proto, Addr} + */ +static +ERL_NIF_TERM make_adress_info(ErlNifEnv* env, + struct addrinfo* addrInfoP) +{ + ERL_NIF_TERM Fam = make_addrinfo_family(env, addrInfoP->ai_family); + ERL_NIF_TERM Type = make_addrinfo_type(env, addrInfoP->ai_socktype); + ERL_NIF_TERM Proto = make_addrinfo_proto(env, addrInfoP->ai_protocol); + ERL_NIF_TERM Addr = make_addrinfo_addr(env, + addrInfoP->ai_addr, + addrInfoP->ai_addrlen); + + return MKT5(env, atom_address_info, Fam, Type, Proto, Addr); + +} + + +/* Convert an "native" family to an erlang family + * Note that this is not currently exhaustive, but only supports + * inet and inet6. Other values will be returned as is, that is + * in the form of an integer. + */ +static +ERL_NIF_TERM make_addrinfo_family(ErlNifEnv* env, + int family) +{ + ERL_NIF_TERM efam; + + switch (family) { + case AF_INET: + efam = atom_inet; + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + efam = atom_inet6; + break; +#endif + + default: + efam = MKI(env, family); + break; + } + + return efam; +} + + + +/* Convert an "native" socket type to an erlang socket type + * Note that this is not currently exhaustive, but only supports + * stream and dgram. Other values will be returned as is, that is + * in the form of an integer. + */ +static +ERL_NIF_TERM make_addrinfo_type(ErlNifEnv* env, + int socktype) +{ + ERL_NIF_TERM etype; + + switch (socktype) { + case SOCK_STREAM: + etype = atom_stream; + break; + + case SOCK_DGRAM: + etype = atom_dgram; + break; + + default: + etype = MKI(env, socktype); + break; + } + + return etype; +} + + + +/* Convert an "native" protocol to an erlang protocol + * Note that this is not currently exhaustive, but only supports + * tcp and udp. Other values will be returned as is, that is + * in the form of an integer. + */ +static +ERL_NIF_TERM make_addrinfo_proto(ErlNifEnv* env, + int proto) +{ + ERL_NIF_TERM eproto; + + switch (proto) { +#if defined(SOL_IP) + case SOL_IP: +#else + case IPPROTO_IP: +#endif + eproto = atom_ip; + break; + + case IPPROTO_TCP: + eproto = atom_tcp; + break; + + case IPPROTO_UDP: + eproto = atom_udp; + break; + + default: + eproto = MKI(env, proto); + break; + } + + return eproto; +} + + + +/* Convert an "native" address to an erlang address + * Note that this is not currently exhaustive, but only supports + * IPv4 and IPv6 addresses. Values of other families will be + * returned as an undefined. + */ +static +ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, + struct sockaddr* addrP, + socklen_t addrLen) +{ + ERL_NIF_TERM port, addr, eaddr; + SockAddress* p = (SockAddress*) addrP; + + switch (addrP->sa_family) { + case AF_INET: + port = ntohs(p->in.sin_port); + addr = MKT4(env, + MKI(env, p->in.sin_addr[0]), + MKI(env, p->in.sin_addr[1]), + MKI(env, p->in.sin_addr[2]), + MKI(env, p->in.sin_addr[3])); + eaddr = MKT2(env, port, addr); + break; + +#if defined(HAVE_IN6) && defined(AF_INET6) + case AF_INET6: + port = ntohs(p->in6.sin6_port); + addr = MKT8(env, + MKI(env, p->in6.sin_addr[0]), + MKI(env, p->in6.sin_addr[1]), + MKI(env, p->in6.sin_addr[2]), + MKI(env, p->in6.sin_addr[3]), + MKI(env, p->in6.sin_addr[3]), + MKI(env, p->in6.sin_addr[3]), + MKI(env, p->in6.sin_addr[3]), + MKI(env, p->in6.sin_addr[3])); + eaddr = MKT2(env, port, addr); + break; +#endif + + default: + eaddr = atom_undefined; + break; + } + + return eaddr; +} + + + +/* 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. + */ +static +ERL_NIF_TERM make_ok2(ErlNifEnv* env, ERL_NIF_TERM any) +{ + return MKT2(env, 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. + */ +/* +static +ERL_NIF_TERM make_ok3(ErlNifEnv* env, ERL_NIF_TERM val1, ERL_NIF_TERM val2) +{ + return MKT3(env, 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. + */ +static +ERL_NIF_TERM make_error(ErlNifEnv* env, ERL_NIF_TERM reason) +{ + return MKT2(env, atom_error, reason); +} + + +/* Create an error two (2) tuple in the form: {error, Reason}. + * The second element, Reason, is a string to be converted into + * an atom. + */ +static +ERL_NIF_TERM make_error1(ErlNifEnv* env, char* reason) +{ + return 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 will (eventually) be converted + * into an atom. + */ +static +ERL_NIF_TERM make_error2(ErlNifEnv* env, int err) +{ + return make_error1(env, erl_errno_id(err)); +} + + +/* +static +void xabort(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(); +} +*/ + + +/* ---------------------------------------------------------------------- + * C o u n t e r F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* ---------------------------------------------------------------------- + * C a l l b a c k F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +/* ========================================================================= + * net_dtor - Callback function for resource destructor + * + */ +/* +static +void net_dtor(ErlNifEnv* env, void* obj) +{ +} +*/ + + +/* ========================================================================= + * net_stop - Callback function for resource stop + * + */ +/* +static +void net_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call) +{ +} +*/ + + + + +/* ========================================================================= + * net_down - Callback function for resource down (monitored processes) + * + */ +/* +static +void net_down(ErlNifEnv* env, + void* obj, + const ErlNifPid* pid, + const ErlNifMonitor* mon) +{ +} +*/ + + + +/* ---------------------------------------------------------------------- + * L o a d / u n l o a d / u p g r a d e F u n c t i o n s + * ---------------------------------------------------------------------- + */ + +static +ErlNifFunc net_funcs[] = +{ + // Some utility functions + {"nif_is_loaded", 0, nif_is_loaded, 0}, + {"nif_info", 0, nif_info, 0}, + + /* address and name translation in protocol-independent manner */ + {"nif_getnameinfo", 2, nif_getnameinfo, 0}, + + /* Network interface (name and/or index) functions */ + {"nif_if_name2index", 1, nif_if_name2index, 0}, + {"nif_if_index2name", 1, nif_if_index2name, 0}, + {"nif_if_names", 0, nif_if_names, 0} +}; + + + +/* ======================================================================= + * load_info - A map of misc info (e.g global debug) + */ + +static +int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + /* +++ Misc atoms +++ */ + atom_dgram = MKA(env, str_dgram); + atom_error = MKA(env, str_error); + atom_idn = MKA(env, str_idn); + atom_idna_allow_unassigned = MKA(env, str_idna_allow_unassigned); + atom_idna_use_std3_ascii_rules = MKA(env, str_idna_use_std3_ascii_rules); + atom_namereqd = MKA(env, str_namereqd); + atom_name_info = MKA(env, str_name_info); + atom_nofqdn = MKA(env, str_nofqdn); + atom_numerichost = MKA(env, str_numerichost); + atom_numericserv = MKA(env, str_numericserv); + atom_ok = MKA(env, str_ok); + atom_true = MKA(env, str_true); + atom_undefined = MKA(env, str_undefined); + // atom_version = MKA(env, str_version); + + // atom_lowdelay = MKA(env, str_lowdelay); + // atom_throughput = MKA(env, str_throughput); + // atom_reliability = MKA(env, str_reliability); + // atom_mincost = MKA(env, str_mincost); + + /* Error codes */ + // atom_eafnosupport = MKA(env, str_eafnosupport); + atom_eagain = MKA(env, str_eagain); + atom_ebadflags = MKA(env, str_ebadflags); + atom_efail = MKA(env, str_efail); + atom_efamily = MKA(env, str_efamily); + atom_einval = MKA(env, str_einval); + // atom_eisconn = MKA(env, str_eisconn); + atom_emem = MKA(env, str_emem); + atom_enoname = MKA(env, str_enoname); + // atom_enotclosing = MKA(env, str_enotclosing); + // atom_enotconn = MKA(env, str_enotconn); + atom_eoverflow = MKA(env, str_eoverflow); + // atom_exalloc = MKA(env, str_exalloc); + // atom_exbadstate = MKA(env, str_exbadstate); + // atom_exbusy = MKA(env, str_exbusy); + // atom_exnotopen = MKA(env, str_exnotopen); + // atom_exmon = MKA(env, str_exmon); + // atom_exself = MKA(env, str_exself); + // atom_exsend = MKA(env, str_exsend); + + // For storing "global" things... + // socketData.env = enif_alloc_env(); // We should really check + // socketData.version = MKA(env, ERTS_VERSION); + // socketData.buildDate = MKA(env, ERTS_BUILD_DATE); + + net = enif_open_resource_type_x(env, + "net", + &netInit, + ERL_NIF_RT_CREATE, + NULL); + + return !net; +} + +ERL_NIF_INIT(net, net_funcs, on_load, NULL, NULL, NULL) diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl index 39c907eca3..bdd81ea93a 100644 --- a/erts/preloaded/src/net.erl +++ b/erts/preloaded/src/net.erl @@ -55,8 +55,6 @@ network_interface_index/0 ]). --record(name_info, {flags, host, service}). --record(address_info, {flags, family, socket_type, protocol, addr}). %% Many of these should be moved to the socket module. -type ip_address() :: ip4_address() | ip6_address(). @@ -96,21 +94,14 @@ -type name_info_flag_ext() :: idn | idna_allow_unassigned | idna_use_std3_ascii_rules. +-record(name_info, {host, service}). -type name_info() :: #name_info{}. +-record(address_info, {family, socktype, protocol, addr}). -type address_info() :: #address_info{}. -type network_interface_name() :: string(). -type network_interface_index() :: non_neg_integer(). --define(NET_NAME_INFO_NAMEREQD, 0). --define(NET_NAME_INFO_DGRAM, 1). --define(NET_NAME_INFO_NOFQDN, 2). --define(NET_NAME_INFO_NUMERICHOST, 3). --define(NET_NAME_INFO_NUMERICSERV, 4). --define(NET_NAME_INFO_IDN, 5). --define(NET_NAME_INFO_IDNA_ALLOW_UNASSIGNED, 6). --define(NET_NAME_INFO_IDNA_USE_STD3_ASCII_RULES, 7). - %% =========================================================================== %% @@ -183,47 +174,7 @@ getnameinfo(SockAddr, Flags) when (is_record(SockAddr, in4_sockaddr) orelse is_record(SockAddr, in6_sockaddr)) andalso is_list(Flags) -> - try - begin - EFlags = enc_name_info_flags(Flags), - nif_getnameinfo(SockAddr, EFlags) - end - catch - throw:T -> - T; - error:Reason -> - {error, Reason} - end. - - -enc_name_info_flags([]) -> - 0; -enc_name_info_flags(Flags) -> - EFlags = [{namereqd, ?NET_NAME_INFO_NAMEREQD}, - {dgram, ?NET_NAME_INFO_DGRAM}, - {nofqdn, ?NET_NAME_INFO_NOFQDN}, - {numerichost, ?NET_NAME_INFO_NUMERICHOST}, - {numericserv, ?NET_NAME_INFO_NUMERICSERV}, - - %% The below flags was introduce with glibc 2.3.4. - {idn, ?NET_NAME_INFO_IDN}, - {idna_allow_unassigned, ?NET_NAME_INFO_IDNA_ALLOW_UNASSIGNED}, - {idna_use_std3_ascii_rules,?NET_NAME_INFO_IDNA_USE_STD3_ASCII_RULES}], - enc_flags(Flags, EFlags). - - -enc_flags([], _) -> - 0; -enc_flags(Flags, EFlags) -> - F = fun(Flag, Acc) -> - case lists:keysearch(Flag, 1, EFlags) of - {value, {Flag, EFlag}} -> - Acc bor (1 bsl EFlag); - false -> - throw({error, {unknown_flag, Flag}}) - end - end, - lists:foldl(F, 0, Flags). + nif_getnameinfo(SockAddr, EFlags). %% =========================================================================== |