aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--erts/emulator/Makefile.in3
-rw-r--r--erts/emulator/nifs/common/net_nif.c1788
-rw-r--r--erts/preloaded/src/net.erl55
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).
%% ===========================================================================