/* * %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. * ---------------------------------------------------------------------- * */ #define STATIC_ERLANG_NIF 1 /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ /* #include */ #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 #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_UIO_H #include #endif #ifdef HAVE_NET_IF_DL_H #include #endif #ifdef HAVE_IFADDRS_H #include #endif #ifdef HAVE_NETPACKET_PACKET_H #include #endif #ifdef HAVE_SYS_UN_H #include #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 #endif #include #include /* 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 # ifdef NTDDI_VERSION # undef NTDDI_VERSION # endif # define NTDDI_VERSION NTDDI_WINXP #endif #include #undef WANT_NONBLOCKING #include "sys.h" #else /* !__WIN32__ */ #include #ifdef NETDB_H_NEEDS_IN_H #include #endif #include #include #include #ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H #include #endif #include #include #include #include #ifdef HAVE_ARPA_NAMESER_H #include #endif #ifdef HAVE_SYS_SOCKIO_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #include #ifdef HAVE_SCHED_H #include #endif #ifdef HAVE_SETNS_H #include #endif #define HAVE_UDP #ifndef WANT_NONBLOCKING #define WANT_NONBLOCKING #endif #include "sys.h" #endif #include #include "socket_dbg.h" #include "socket_int.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; #ifdef __WIN32__ #define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__)) #else #define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__)) #endif // __WIN32__ /* 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 /* MAXHOSTNAMELEN could be 64 or 255 depending * on the platform. Instead, use INET_MAXHOSTNAMELEN * which is always 255 across all platforms */ #define NET_MAXHOSTNAMELEN 255 /* =================================================================== * * * * 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 MKT5(E,E1,E2,E3,E4,E5) \ */ /* enif_make_tuple5((E), (E1), (E2), (E3), (E4), (E5)) */ /* #define MKT8(E,E1,E2,E3,E4,E5,E6,E7,E8) \ */ /* enif_make_tuple8((E), (E1), (E2), (E3), (E4), (E5), (E6), (E7), (E8)) */ /* #define MKTA(E, A, AL) enif_make_tuple_from_array((E), (A), (AL)) */ /* #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 #define NDEBUG( ___COND___ , proto ) \ if ( ___COND___ ) { \ dbg_printf proto; \ fflush(stdout); \ } #define NDBG( proto ) NDEBUG( data.debug , proto ) /* The general purpose socket address */ typedef union { struct sockaddr sa; struct sockaddr_in in4; #ifdef HAVE_IN6 struct sockaddr_in6 in6; #endif #ifdef HAVE_SYS_UN_H struct sockaddr_un un; #endif } SockAddress; typedef struct { BOOLEAN_T debug; } NetData; static NetData data; /* ---------------------------------------------------------------------- * 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_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM nif_gethostname(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 ncommand(ErlNifEnv* env, ERL_NIF_TERM cmd); static ERL_NIF_TERM ngethostname(ErlNifEnv* env); static ERL_NIF_TERM ngetnameinfo(ErlNifEnv* env, const SockAddress* saP, SOCKLEN_T saLen, int flags); static ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, char* host, char* serv); 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 ERL_NIF_TERM encode_in_sockaddr(ErlNifEnv* env, struct sockaddr* addrP, SOCKLEN_T addrLen); static ERL_NIF_TERM encode_in4_sockaddr(ErlNifEnv* env, struct sockaddr_in* addrP, SOCKLEN_T addrLen); #if defined(HAVE_IN6) && defined(AF_INET6) static ERL_NIF_TERM encode_in6_sockaddr(ErlNifEnv* env, struct sockaddr_in6* addrP, SOCKLEN_T addrLen); #endif #ifdef HAVE_SYS_UN_H static ERL_NIF_TERM encode_un_sockaddr(ErlNifEnv* env, struct sockaddr_un* addrP, SOCKLEN_T addrLen); #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 BOOLEAN_T decode_addrinfo_string(ErlNifEnv* env, const ERL_NIF_TERM eString, char** stringP); static ERL_NIF_TERM decode_bool(ErlNifEnv* env, ERL_NIF_TERM eBool, BOOLEAN_T* bool); static ERL_NIF_TERM encode_address_infos(ErlNifEnv* env, struct addrinfo* addrInfo); static ERL_NIF_TERM encode_address_info(ErlNifEnv* env, struct addrinfo* addrInfoP); static unsigned int address_info_length(struct addrinfo* addrInfoP); static ERL_NIF_TERM encode_address_info_family(ErlNifEnv* env, int family); static ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env, int socktype); static ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env, int proto); /* static ERL_NIF_TERM make_address_info(ErlNifEnv* env, */ /* struct addrinfo* addrInfoP); */ /* static ERL_NIF_TERM make_addrinfo_addr(ErlNifEnv* env, */ /* struct sockaddr* addrP, */ /* SOCKLEN_T addrLen); */ static ERL_NIF_TERM make_in4_sockaddr(ErlNifEnv* env, ERL_NIF_TERM port, ERL_NIF_TERM addr); #if defined(HAVE_IN6) && defined(AF_INET6) static ERL_NIF_TERM make_in6_sockaddr(ErlNifEnv* env, ERL_NIF_TERM port, ERL_NIF_TERM addr, ERL_NIF_TERM flowInfo, ERL_NIF_TERM scopeId); #endif static ERL_NIF_TERM make_address_info(ErlNifEnv* env, ERL_NIF_TERM fam, ERL_NIF_TERM sockType, ERL_NIF_TERM proto, ERL_NIF_TERM addr); 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); #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) static size_t my_strnlen(const char *s, size_t maxlen); #endif static void dbg_printf( const char* format, ... ); static int dbg_realtime(struct timespec* tsP); static int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts); /* 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_address_info[] = "address_info"; static char str_dccp[] = "dccp"; static char str_debug[] = "debug"; static char str_dgram[] = "dgram"; static char str_error[] = "error"; static char str_false[] = "false"; 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_in4_sockaddr[] = "in4_sockaddr"; static char str_in6_sockaddr[] = "in6_sockaddr"; static char str_inet[] = "inet"; static char str_inet6[] = "inet6"; static char str_ip[] = "ip"; static char str_ipv6[] = "ipv6"; 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_raw[] = "raw"; static char str_rdm[] = "rdm"; static char str_seqpacket[] = "seqpacket"; static char str_stream[] = "stream"; static char str_tcp[] = "tcp"; static char str_true[] = "true"; static char str_udp[] = "udp"; 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_eaddrfamily[] = "eaddrfamily"; static char str_eagain[] = "eagain"; static char str_ebadflags[] = "ebadflags"; static char str_efail[] = "efail"; static char str_efamily[] = "efamily"; static char str_efault[] = "efault"; static char str_einval[] = "einval"; // static char str_eisconn[] = "eisconn"; static char str_emem[] = "emem"; static char str_enametoolong[] = "enametoolong"; static char str_enodata[] = "enodata"; static char str_enoname[] = "enoname"; // static char str_enotclosing[] = "enotclosing"; // static char str_enotconn[] = "enotconn"; static char str_enxio[] = "enxio"; static char str_eoverflow[] = "eoverflow"; static char str_eservice[] = "eservice"; static char str_esocktype[] = "esocktype"; static char str_esystem[] = "esystem"; // 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_address_info; static ERL_NIF_TERM atom_dccp; static ERL_NIF_TERM atom_debug; static ERL_NIF_TERM atom_dgram; static ERL_NIF_TERM atom_error; static ERL_NIF_TERM atom_false; 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_in4_sockaddr; static ERL_NIF_TERM atom_in6_sockaddr; static ERL_NIF_TERM atom_inet; static ERL_NIF_TERM atom_inet6; static ERL_NIF_TERM atom_ip; static ERL_NIF_TERM atom_ipv6; 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_raw; static ERL_NIF_TERM atom_rdm; // static ERL_NIF_TERM atom_select; static ERL_NIF_TERM atom_stream; static ERL_NIF_TERM atom_seqpacket; // static ERL_NIF_TERM atom_timeout; static ERL_NIF_TERM atom_tcp; static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_udp; 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_eaddrfamily; 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_efault; static ERL_NIF_TERM atom_einval; // static ERL_NIF_TERM atom_eisconn; static ERL_NIF_TERM atom_emem; static ERL_NIF_TERM atom_enametoolong; static ERL_NIF_TERM atom_enodata; static ERL_NIF_TERM atom_enoname; // static ERL_NIF_TERM atom_enotclosing; // static ERL_NIF_TERM atom_enotconn; static ERL_NIF_TERM atom_enxio; static ERL_NIF_TERM atom_eoverflow; static ERL_NIF_TERM atom_eservice; static ERL_NIF_TERM atom_esocktype; static ERL_NIF_TERM atom_esystem; // 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_gethostname/0 * 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, tmp; NDBG( ("info -> entry\r\n") ); tmp = enif_make_new_map(env); if (!enif_make_map_put(env, tmp, atom_debug, BOOL2ATOM(data.debug), &info)) info = tmp; NDBG( ("info -> done: %T\r\n", info) ); return info; } /* ---------------------------------------------------------------------- * nif_command * * Description: * This is a general purpose utility function. * * Arguments: * Command - This is a general purpose command, of any type. * Currently, the only supported command is: * * {debug, boolean()} */ static ERL_NIF_TERM nif_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM ecmd, result; NDBG( ("command -> entry (%d)\r\n", argc) ); if (argc != 1) return enif_make_badarg(env); ecmd = argv[0]; NDBG( ("command -> ecmd: %T\r\n", ecmd) ); result = ncommand(env, ecmd); NDBG( ("command -> result: %T\r\n", result) ); return result; } /* * The command can, in principle, be anything, though currently we only * support a debug command. */ static ERL_NIF_TERM ncommand(ErlNifEnv* env, ERL_NIF_TERM cmd) { const ERL_NIF_TERM* t; int tsz; if (IS_TUPLE(env, cmd)) { /* Could be the debug tuple */ if (!GET_TUPLE(env, cmd, &tsz, &t)) return make_error(env, atom_einval); if (tsz != 2) return make_error(env, atom_einval); /* First element should be the atom 'debug' */ if (COMPARE(t[0], atom_debug) != 0) return make_error(env, atom_einval); return decode_bool(env, t[1], &data.debug); } else { return make_error(env, atom_einval); } } /* ---------------------------------------------------------------------- * nif_gethostname * * Description: * Access the hostname of the current processor. * */ static ERL_NIF_TERM nif_gethostname(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM result; NDBG( ("nif_gethostname -> entry (%d)\r\n", argc) ); if (argc != 0) return enif_make_badarg(env); result = ngethostname(env); NDBG( ("nif_gethostname -> done when result: %T\r\n", result) ); return result; } static ERL_NIF_TERM ngethostname(ErlNifEnv* env) { ERL_NIF_TERM result; char buf[NET_MAXHOSTNAMELEN + 1]; int res; res = net_gethostname(buf, sizeof(buf)); NDBG( ("ngethostname -> gethostname res: %d\r\n", res) ); switch (res) { case 0: result = make_ok2(env, MKS(env, buf)); break; case EFAULT: result = make_error(env, atom_efault); break; case EINVAL: result = make_error(env, atom_einval); break; case ENAMETOOLONG: result = make_error(env, atom_enametoolong); break; default: result = make_error(env, MKI(env, res)); break; } return result; } /* ---------------------------------------------------------------------- * 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(). */ static ERL_NIF_TERM nif_getnameinfo(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { ERL_NIF_TERM result, eSockAddr; ERL_NIF_TERM eFlags; int flags = 0; // Just in case... SockAddress sa; SOCKLEN_T saLen = 0; // Just in case... NDBG( ("nif_getnameinfo -> entry (%d)\r\n", argc) ); if (argc != 2) return enif_make_badarg(env); eSockAddr = argv[0]; eFlags = argv[1]; NDBG( ("nif_getnameinfo -> " "\r\n SockAddr: %T" "\r\n Flags: %T" "\r\n", eSockAddr, eFlags) ); 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); result = ngetnameinfo(env, &sa, saLen, flags); NDBG( ("nif_getnameinfo -> done when result: " "\r\n %T\r\n", result) ); return result; } /* 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); NDBG( ("ngetnameinfo -> res: %d\r\n", res) ); 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, eHostName, eServName; //, eHints; char* hostName; char* servName; // struct addrinfo* hints; NDBG( ("nif_getaddrinfo -> entry (%d)\r\n", argc) ); if (argc != 3) { return enif_make_badarg(env); } eHostName = argv[0]; eServName = argv[1]; // eHints = argv[2]; NDBG( ("nif_getaddrinfo -> " "\r\n ehost: %T" "\r\n eservice: %T" "\r\n ehints: %T" "\r\n", argv[0], argv[1], 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); */ NDBG( ("nif_getaddrinfo -> done when result: " "\r\n %T\r\n", result) ); return result; } static ERL_NIF_TERM ngetaddrinfo(ErlNifEnv* env, char* host, char* serv) { ERL_NIF_TERM result; struct addrinfo* addrInfoP; int res; NDBG( ("ngetaddrinfo -> entry with" "\r\n host: %s" "\r\n serv: %s" "\r\n", ((host == NULL) ? "NULL" : host), ((serv == NULL) ? "NULL" : serv)) ); res = getaddrinfo(host, serv, NULL, &addrInfoP); NDBG( ("ngetaddrinfo -> res: %d\r\n", res) ); switch (res) { case 0: { ERL_NIF_TERM addrInfo = encode_address_infos(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_SERVICE: 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, result; char ifn[IF_NAMESIZE+1]; NDBG( ("nif_if_name2index -> entry (%d)\r\n", argc) ); if (argc != 1) { return enif_make_badarg(env); } eifn = argv[0]; NDBG( ("nif_if_name2index -> " "\r\n Ifn: %T" "\r\n", argv[0]) ); if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn))) return make_error2(env, atom_einval); result = nif_name2index(env, ifn); NDBG( ("nif_if_name2index -> done when result: %T\r\n", result) ); return result; } static ERL_NIF_TERM nif_name2index(ErlNifEnv* env, char* ifn) { unsigned int idx; NDBG( ("nif_name2index -> entry with ifn: %s\r\n", ifn) ); idx = if_nametoindex(ifn); NDBG( ("nif_name2index -> idx: %d\r\n", idx) ); if (idx == 0) return make_error2(env, get_errno()); else return make_ok2(env, MKI(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[]) { ERL_NIF_TERM result; unsigned int idx; NDBG( ("nif_if_index2name -> entry (%d)\r\n", argc) ); if ((argc != 1) || !GET_UINT(env, argv[0], &idx)) { return enif_make_badarg(env); } NDBG( ("nif_index2name -> " "\r\n Idx: %T" "\r\n", argv[0]) ); result = nif_index2name(env, idx); NDBG( ("nif_if_index2name -> done when result: %T\r\n", result) ); return result; } 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_error(env, atom_enxio); } 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[]) { ERL_NIF_TERM result; NDBG( ("nif_if_names -> entry (%d)\r\n", argc) ); if (argc != 0) { return enif_make_badarg(env); } result = nif_names(env); NDBG( ("nif_if_names -> done when result: %T\r\n", result) ); return result; } static ERL_NIF_TERM nif_names(ErlNifEnv* env) { ERL_NIF_TERM result; struct if_nameindex* ifs = if_nameindex(); NDBG( ("nif_names -> ifs: 0x%lX\r\n", ifs) ); 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); NDBG( ("nif_names -> len: %d\r\n", len) ); 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; BOOLEAN_T done = FALSE; while (!done) { NDBG( ("nif_names_length -> %d: " "\r\n if_index: %d" "\r\n if_name: 0x%lX" "\r\n", len, p[len].if_index, p[len].if_name) ); if ((p[len].if_index == 0) && (p[len].if_name == NULL)) done = TRUE; else 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->in4.sin_family = AF_INET; saP->in4.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->in4.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 static ERL_NIF_TERM encode_in_sockaddr(ErlNifEnv* env, struct sockaddr* addrP, SOCKLEN_T addrLen) { SockAddress* sockAddrP = (SockAddress*) addrP; ERL_NIF_TERM sockAddr; switch (sockAddrP->sa.sa_family) { case AF_INET: sockAddr = encode_in4_sockaddr(env, &sockAddrP->in4, addrLen); break; #if defined(HAVE_IN6) && defined(AF_INET6) case AF_INET6: sockAddr = encode_in6_sockaddr(env, &sockAddrP->in6, addrLen); break; #endif #ifdef HAVE_SYS_UN_H case AF_UNIX: sockAddr = encode_un_sockaddr(env, &sockAddrP->un, addrLen); break; #endif default: sockAddr = atom_undefined; break; } return sockAddr; } /* Encode an IPv4 socket address; in4_sockaddr */ static ERL_NIF_TERM encode_in4_sockaddr(ErlNifEnv* env, struct sockaddr_in* addrP, SOCKLEN_T addrLen) { ERL_NIF_TERM sockAddr; short port; ERL_NIF_TERM ePort, eAddr; if (addrLen >= sizeof(struct sockaddr_in)) { unsigned int i; ERL_NIF_TERM at[4]; unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); unsigned char* a = (unsigned char*) &addrP->sin_addr; /* The port */ port = ntohs(addrP->sin_port); ePort = MKI(env, port); /* The address */ for (i = 0; i < atLen; i++) { at[i] = MKI(env, a[i]); } eAddr = MKTA(env, at, atLen); // eAddr = MKT4(env, at[0], at[1], at[2], at[3]); /* And finally construct the in4_sockaddr record */ sockAddr = make_in4_sockaddr(env, ePort, eAddr); } else { sockAddr = atom_undefined; } return sockAddr; } /* Encode an IPv6 socket address; in6_sockaddr */ #if defined(HAVE_IN6) && defined(AF_INET6) static ERL_NIF_TERM encode_in6_sockaddr(ErlNifEnv* env, struct sockaddr_in6* addrP, SOCKLEN_T addrLen) { ERL_NIF_TERM sockAddr; short port; ERL_NIF_TERM ePort, eAddr, eFlowInfo, eScopeId; if (addrLen >= sizeof(struct sockaddr_in6)) { unsigned int i; ERL_NIF_TERM at[8]; unsigned int atLen = sizeof(at) / sizeof(ERL_NIF_TERM); unsigned char* a = (unsigned char*) &addrP->sin6_addr; /* The port */ port = ntohs(addrP->sin6_port); ePort = MKI(env, port); /* The address */ for (i = 0; i < atLen; i++) { at[i] = MKI(env, get_int16(a + i*2)); } eAddr = MKTA(env, at, atLen); /* The flowInfo */ eFlowInfo = MKI(env, addrP->sin6_flowinfo); /* The scopeId */ eScopeId = MKI(env, addrP->sin6_scope_id); /* And finally construct the in6_sockaddr record */ sockAddr = make_in6_sockaddr(env, ePort, eAddr, eFlowInfo, eScopeId); } else { sockAddr = atom_undefined; } return sockAddr; } #endif /* Encode an Unix Domain socket address: string() */ #ifdef HAVE_SYS_UN_H static ERL_NIF_TERM encode_un_sockaddr(ErlNifEnv* env, struct sockaddr_un* addrP, SOCKLEN_T addrLen) { ERL_NIF_TERM sockAddr; size_t n, m; if (addrLen >= offsetof(struct sockaddr_un, sun_path)) { n = addrLen - offsetof(struct sockaddr_un, sun_path); if (255 < n) { sockAddr = atom_undefined; } else { m = my_strnlen(addrP->sun_path, n); #ifdef __linux__ /* Assume that the address is a zero terminated string, * except when the first byte is \0 i.e the string length is 0, * then use the reported length instead. * This fix handles Linux's nonportable * abstract socket address extension. */ if (m == 0) { m = n; } #endif sockAddr = MKSL(env, addrP->sun_path, m); } } else { sockAddr = atom_undefined; } return sockAddr; } #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)) { NDBG( ("decode_nameinfo_flags -> is atom\r\n") ); 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; } NDBG( ("decode_addrinfo_string -> len: %d\r\n", len) ); bufP = MALLOC(len + 1); // We shall NULL-terminate if (GET_STR(env, eString, bufP, len+1)) { NDBG( ("decode_addrinfo_string -> buf: %s\r\n", bufP) ); // bufP[len] = '\0'; *stringP = bufP; result = TRUE; } else { *stringP = NULL; result = FALSE; } } return result; } static ERL_NIF_TERM decode_bool(ErlNifEnv* env, ERL_NIF_TERM eBool, BOOLEAN_T* bool) { if (COMPARE(eBool, atom_true) == 0) { *bool = TRUE; return atom_ok; } else if (COMPARE(eBool, atom_false) == 0) { *bool = FALSE; return atom_ok; } else { return make_error(env, atom_einval); } } /* 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_infos(ErlNifEnv* env, struct addrinfo* addrInfo) { ERL_NIF_TERM result; unsigned int len = address_info_length(addrInfo); NDBG( ("encode_address_infos -> len: %d\r\n", len) ); if (len > 0) { ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); unsigned int i = 0; struct addrinfo* p = addrInfo; while (i < len) { array[i] = encode_address_info(env, p); p = p->ai_next; i++; } result = MKLA(env, array, len); } else { result = MKEL(env); } NDBG( ("encode_address_infos -> result: " "\r\n %T\r\n", result) ); 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 = 1; struct addrinfo* tmp; BOOLEAN_T done = FALSE; tmp = addrInfoP; while (!done) { if (tmp->ai_next != NULL) { len++; tmp = tmp->ai_next; } else { done = TRUE; } } 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 encode_address_info(ErlNifEnv* env, struct addrinfo* addrInfoP) { ERL_NIF_TERM result, fam, type, proto, addr; fam = encode_address_info_family(env, addrInfoP->ai_family); type = encode_address_info_type(env, addrInfoP->ai_socktype); proto = encode_address_info_proto(env, addrInfoP->ai_protocol); addr = encode_in_sockaddr(env, addrInfoP->ai_addr, addrInfoP->ai_addrlen); result = make_address_info(env, fam, type, proto, addr); return result; } /* 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 encode_address_info_family(ErlNifEnv* env, int family) { ERL_NIF_TERM efam; switch (family) { case AF_INET: efam = esock_atom_inet; break; #if defined(HAVE_IN6) && defined(AF_INET6) case AF_INET6: efam = esock_atom_inet6; break; #endif #ifdef HAVE_SYS_UN_H case AF_UNIX: efam = esock_atom_local; 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 encode_address_info_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; case SOCK_RAW: etype = atom_raw; break; case SOCK_RDM: etype = atom_rdm; break; case SOCK_SEQPACKET: etype = atom_seqpacket; 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 encode_address_info_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; #if defined(SOL_IPV6) case SOL_IPV6: eproto = atom_ipv6; break; #endif case IPPROTO_TCP: eproto = atom_tcp; break; case IPPROTO_UDP: eproto = atom_udp; break; #if defined(HAVE_SCTP) case IPPROTO_SCTP: eproto = atom_sctp; break; #endif 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 encode_address_info_addr(ErlNifEnv* env, struct sockaddr* addrP, SOCKLEN_T addrLen) { ERL_NIF_TERM port, addr, eaddr; SockAddress* p = (SockAddress*) addrP; NDBG( ("encode_address_info_addr -> entry with" "\r\n family: %d" "\r\n addrLen: %d" "\r\n", addrP->sa_family, addrLen) ); switch (addrP->sa_family) { case AF_INET: { unsigned char* a = (unsigned char*) &p->in.sin_addr; port = MKI(env, ntohs(p->in.sin_port)); addr = MKT4(env, MKI(env, a[0]), MKI(env, a[1]), MKI(env, a[2]), MKI(env, a[3])); eaddr = MKT2(env, port, addr); } break; #if defined(HAVE_IN6) && defined(AF_INET6) case AF_INET6: { unsigned char* a = (unsigned char*) &p->in6.sin6_addr; port = MKI(env, ntohs(p->in6.sin6_port)); addr = MKT8(env, MKI(env, get_int16(a)), MKI(env, get_int16(&a[ 2])), MKI(env, get_int16(&a[ 4])), MKI(env, get_int16(&a[ 6])), MKI(env, get_int16(&a[ 8])), MKI(env, get_int16(&a[10])), MKI(env, get_int16(&a[12])), MKI(env, get_int16(&a[14]))); eaddr = MKT2(env, port, addr); } break; #endif default: eaddr = atom_undefined; break; } NDBG( ("make_addrinfo_addr -> eaddr: " "\r\n %T\r\n", eaddr) ); return eaddr; } */ #if defined(HAVE_SYS_UN_H) || defined(SO_BINDTODEVICE) /* strnlen doesn't exist everywhere */ static size_t my_strnlen(const char *s, size_t maxlen) { size_t i = 0; while (i < maxlen && s[i] != '\0') i++; return i; } #endif /* Construct the IPv4 socket address record: in4_sockaddr */ static ERL_NIF_TERM make_in4_sockaddr(ErlNifEnv* env, ERL_NIF_TERM port, ERL_NIF_TERM addr) { return MKT3(env, atom_in4_sockaddr, port, addr); } /* Construct the IPv6 socket address record: in6_sockaddr */ static ERL_NIF_TERM make_in6_sockaddr(ErlNifEnv* env, ERL_NIF_TERM port, ERL_NIF_TERM addr, ERL_NIF_TERM flowInfo, ERL_NIF_TERM scopeId) { return MKT5(env, atom_in6_sockaddr, port, addr, flowInfo, scopeId); } static ERL_NIF_TERM make_address_info(ErlNifEnv* env, ERL_NIF_TERM fam, ERL_NIF_TERM sockType, ERL_NIF_TERM proto, ERL_NIF_TERM addr) { return MKT5(env, atom_address_info, fam, sockType, proto, addr); } /* 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) { NDBG( ("make_error2 -> err: %d\r\n", 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) { } */ /* ---------------------------------------------------------------------- * D e b u g F u n c t i o n s * ---------------------------------------------------------------------- */ /* * Print a debug format string *with* both a timestamp and the * the name of the *current* thread. */ static void dbg_printf( const char* format, ... ) { va_list args; char f[512 + sizeof(format)]; // This has to suffice... char stamp[30]; struct timespec ts; int res; /* * We should really include self in the printout, so we can se which process * are executing the code. But then I must change the API.... * ....something for later. */ if (!dbg_realtime(&ts)) { if (dbg_timespec2str(stamp, sizeof(stamp), &ts) != 0) { // res = enif_snprintf(f, sizeof(f), "NET [%s] %s", TSNAME(), format); res = enif_snprintf(f, sizeof(f), "NET [%s]", format); } else { // res = enif_snprintf(f, sizeof(f), "NET[%s] [%s] %s", stamp, TSNAME(), format); res = enif_snprintf(f, sizeof(f), "NET [%s] %s", stamp, format); } if (res > 0) { va_start (args, format); erts_vfprintf (stdout, f, args); // TMP: use enif_vfprintf va_end (args); fflush(stdout); } } return; } static int dbg_realtime(struct timespec* tsP) { return clock_gettime(CLOCK_REALTIME, tsP); } /* * Convert a timespec struct into a readable/printable string */ static int dbg_timespec2str(char *buf, unsigned int len, struct timespec *ts) { int ret, buflen; struct tm t; tzset(); if (localtime_r(&(ts->tv_sec), &t) == NULL) return 1; ret = strftime(buf, len, "%F %T", &t); if (ret == 0) return 2; len -= ret - 1; buflen = strlen(buf); ret = snprintf(&buf[buflen], len, ".%06ld", ts->tv_nsec/1000); if (ret >= len) return 3; return 0; } /* ---------------------------------------------------------------------- * 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}, {"nif_command", 1, nif_command, 0}, // Shall we let this be dirty? /* get/set hostname */ {"nif_gethostname", 0, nif_gethostname, 0}, /* address and name translation in protocol-independent manner */ {"nif_getnameinfo", 2, nif_getnameinfo, 0}, {"nif_getaddrinfo", 3, nif_getaddrinfo, 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) { // We should make it possible to use load_info to get default values data.debug = FALSE; // NDBG( ("on_load -> entry\r\n") ); /* +++ Misc atoms +++ */ atom_address_info = MKA(env, str_address_info); atom_dccp = MKA(env, str_dccp); atom_debug = MKA(env, str_debug); atom_dgram = MKA(env, str_dgram); atom_error = MKA(env, str_error); atom_false = MKA(env, str_false); 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_in4_sockaddr = MKA(env, str_in4_sockaddr); atom_in6_sockaddr = MKA(env, str_in6_sockaddr); atom_inet = MKA(env, str_inet); atom_inet6 = MKA(env, str_inet6); atom_ip = MKA(env, str_ip); atom_ipv6 = MKA(env, str_ipv6); 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_raw = MKA(env, str_raw); atom_rdm = MKA(env, str_rdm); atom_seqpacket = MKA(env, str_seqpacket); atom_stream = MKA(env, str_stream); atom_tcp = MKA(env, str_tcp); atom_true = MKA(env, str_true); atom_udp = MKA(env, str_udp); 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_eaddrfamily = MKA(env, str_eaddrfamily); 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_efault = MKA(env, str_efault); atom_einval = MKA(env, str_einval); // atom_eisconn = MKA(env, str_eisconn); atom_emem = MKA(env, str_emem); atom_enametoolong = MKA(env, str_enametoolong); atom_enodata = MKA(env, str_enodata); atom_enoname = MKA(env, str_enoname); // atom_enotclosing = MKA(env, str_enotclosing); // atom_enotconn = MKA(env, str_enotconn); atom_enxio = MKA(env, str_enxio); atom_eoverflow = MKA(env, str_eoverflow); atom_eservice = MKA(env, str_eservice); atom_esocktype = MKA(env, str_esocktype); atom_esystem = MKA(env, str_esystem); // 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); // NDBG( ("on_load -> done\r\n") ); return !net; } ERL_NIF_INIT(net, net_funcs, on_load, NULL, NULL, NULL)