aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/nifs/common/net_nif.c
blob: f7eeee45ac739e816d950eb7d3adc0f2b7ac1b22 (plain) (tree)

























                                                                           

                           




















                                                                    
                 







































































                                                                            
                       





































                          

                       
                        
 




                                          






                                                                               
 







                                     




                        





                                                    
 





                                                                         






                             


                                   
                                                            
 
 




                    







                                                                         
                    

 
 








                                                                         


                                                        


                                                           
 
                                                           


                                                               


                                                               













                                                                 

                                               
                                                 



                                                             


                                                  

















                                                             
 





                                                                      



                                                             


                                                   

                                                                    
                                                             
                                                                    

                                                                    
                                                              
                                                            
                                                            
                                                            
                                                             
                                                          
 





                                                      
 

                                                 


























                                                                             
                                                             
                                                      







                                                                          

                                      
                                                    


                                                  
                                               
                                             
                                                     
                                                
                                                
                                              
                                                  


                                                  



                   
                                      
                               







                                                   

 
                                     
                                   


                                   
                                
                              
                                      
                                 
                                 
                               
                                   


                                   
 

















                                                                         
             
                


                                 
                    









                                                                         









                                                
                           
 
                                         
 


                                                                               
 
                                                  
 





                                                                         

















                                                            
                                                       





                                     
                                                     


                                 
                                                         



















                                                                       
                                                            

                     
                                                            


                                                      
                                                            



                                                   
                                                        





     













                                                                         
                                                               





                                     
                                                                           













                                             
                                                                    


                  
                                                    


                
                                                    


                
                                                          


                      
                                                          


              
                                                      







                  

                                                                         







                                                                        






                                                       




                                               
 
                                                               
 
                  
                                     
                        
                        
 

                                

                                
                                        
 
                                                            

                                     
                                                    

                                     

                                                  

                                                  
                                      

                  







                                                                         



                                                     


                                    
                                        
                                   
                                        





                                                        
                                                        
 






                                                     
                                               


              
                      
                   
                                                          
              
      
 
                         
                      
                                                       
              
      
 
                     
                  
                                                   
              
      
 
                       
                    
                                                     
              
      
 
                       
                    
                                                  
              
      
 
                       
                    
                                                     
              
      
 
                         
                      
                                                       
              
      
 
                       
                    
                                                          
              
      

            
                                                          
























                                                                             
                                                              



                              
                                                               
 




                                     
                           
 

                                


                                

                                                


























                                                           

                                                  
                                      
 




                  


                                          
 
                            


                               
                                              





                                               

                                                    
                                                        
    


                  
                                                                         
                                    
                                                   


              
                           
                        
                                                         
              
      
 
                      
                   
                                                          
              
      
 
                         
                      
                                                       
              
      
 
                     
                  
                                                   
              
      
 
                       
                    
                                                     
              
      
 
                       
                    
                                                  
              
      
 
                       
                    
                                                     
              
      
 
                       
                    
                                                     
              
      
 
                        
                     
                                                      
              
      
 
                         
                      
                                                       
              
      
 
                       
                    
                                                     
              
      

            
                                                          





















                                                                         
                              

                                    
                                                                 
 




                                     

                                  


                              
                                                  
                                                        
 

                                      
                                                                             

                  







                                           

                     
                                                                     

                              
 
                                                          

                 
                                                         
         
                                                   



















                                                                         
                        

                     
                                                                 
 




                                        
                                      




                                      
                                                                             

                  













                                                    
                                           
                                                    
            
                                                   





















                                                                         

                        
                                                            
 



                                     

                            
                                                                        

                  









                                              
                                                        
 
                      
                                                          














                                                                              
                                                         
 









                                                                     
                                                                

                        
                                                                 













                                                     



                               
                                                


                                                               
 



                                                               











                                                                         











                                                           
                                                         





                                      
                                                                






















                                                                
                                                              





















































                                                                            
 
                                                          





                             
 
            
 
                                                            
 







                  




                                            
                                               
                     

                                                       
                      
                             
            
                                                        




     




                                                                 

                                                            



                                                     
                                                                
 
                  
                                                                             



                                          
                                                   


                           
 
                                       



                           
                                                    

                                      











                                                            


                                  
 



                                   
                  


                               
         












                                                          

                                                            
 
                                                  
 


                                                                    



                                                              
    



                                                                          



 
                                                            




                                                                

                                                          


                      
                                                        
                                





                
                                                            




                                                                 

                                                          


                       
                                                         
                                   





                 
                                                      




                                                                

                                                        


                        
                                                           
                                 





                  
      





                                               
 















                                                               
 


 























































                                                                            
                                           
                                                                          
 


                                                         

                                                                     
                                                         







                                                         












                                                                               







                                                                          
                                                                        
                                               
 
                                            
 
                            
                                                                
                                                         







                                                                             

                     
                                                     


                                                   
                                                
                                              
                                                      
                                                 
                                                 
                                               
                                                   


                                                   

                                     


                                                                   






                                                      
                                           
 



                                                       
/*
 * %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


#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 <time.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/ip.h>
#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>

#include "socket_dbg.h"
#include "socket_int.h"
#include "socket_util.h"


/* All platforms fail on malloc errors. */
#define FATAL_MALLOC


#ifdef __WIN32__
#define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__))
#else
#define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__))
#endif // __WIN32__



/* *** Misc macros and defines *** */

#ifdef __WIN32__
#define get_errno() WSAGetLastError()
#else
#define get_errno() errno
#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                          *
 *                                                                     *
 * =================================================================== */


#ifdef HAVE_SOCKLEN_T
#  define SOCKLEN_T socklen_t
#else
#  define SOCKLEN_T size_t
#endif

/* Debug stuff... */
#define NET_NIF_DEBUG_DEFAULT FALSE

#define NDBG( proto ) ESOCK_DBG_PRINTF( data.debug , proto )


typedef struct {
    BOOLEAN_T debug;
} NetData;



/* =================================================================== *
 *                                                                     *
 *                           Static data                               *
 *                                                                     *
 * =================================================================== */


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_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 SocketAddress* 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_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 char* make_address_info(ErlNifEnv*    env,
                               ERL_NIF_TERM  fam,
                               ERL_NIF_TERM  sockType,
                               ERL_NIF_TERM  proto,
                               ERL_NIF_TERM  addr,
                               ERL_NIF_TERM* ai);

static BOOLEAN_T extract_debug(ErlNifEnv*   env,
                               ERL_NIF_TERM map);
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_debug[]                     = "debug";
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";

/* (special) error string constants */
static char str_eaddrfamily[]       = "eaddrfamily";
static char str_ebadflags[]         = "ebadflags";
static char str_efail[]             = "efail";
static char str_efamily[]           = "efamily";
static char str_efault[]            = "efault";
static char str_emem[]              = "emem";
static char str_enametoolong[]      = "enametoolong";
static char str_enodata[]           = "enodata";
static char str_enoname[]           = "enoname";
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";


/* *** Atoms *** */

static ERL_NIF_TERM atom_address_info;
static ERL_NIF_TERM atom_debug;
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_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_emem;
static ERL_NIF_TERM atom_enametoolong;
static ERL_NIF_TERM atom_enodata;
static ERL_NIF_TERM atom_enoname;
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;


/* *** 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_info/0
 * nif_command/1
 *
 * 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_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( ("NET", "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( ("NET", "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( ("NET", "command -> entry (%d)\r\n", argc) );

    if (argc != 1)
        return enif_make_badarg(env);

    ecmd = argv[0];

    NDBG( ("NET", "command -> ecmd: %T\r\n", ecmd) );

    result = ncommand(env, ecmd);

    NDBG( ("NET", "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 esock_make_error(env, esock_atom_einval);

        if (tsz != 2)
            return esock_make_error(env, esock_atom_einval);

        /* First element should be the atom 'debug' */
        if (COMPARE(t[0], atom_debug) != 0)
            return esock_make_error(env, esock_atom_einval);

        return decode_bool(env, t[1], &data.debug);

    } else {
        return esock_make_error(env, esock_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( ("NET", "nif_gethostname -> entry (%d)\r\n", argc) );

    if (argc != 0)
        return enif_make_badarg(env);

    result = ngethostname(env);

    NDBG( ("NET", "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( ("NET", "ngethostname -> gethostname res: %d\r\n", res) );

    switch (res) {
    case 0:
        result = esock_make_ok2(env, MKS(env, buf));
        break;

    case EFAULT:
        result = esock_make_error(env, atom_efault);
        break;

    case EINVAL:
        result = esock_make_error(env, esock_atom_einval);
        break;

    case ENAMETOOLONG:
        result = esock_make_error(env, atom_enametoolong);
        break;

    default:
        result = esock_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;
    ERL_NIF_TERM  eSockAddr, eFlags;
    int           flags = 0; // Just in case...
    SocketAddress sa;
    SOCKLEN_T     saLen = 0; // Just in case...

    NDBG( ("NET", "nif_getnameinfo -> entry (%d)\r\n", argc) );

    if (argc != 2)
        return enif_make_badarg(env);
    eSockAddr = argv[0];
    eFlags    = argv[1];

    NDBG( ("NET",
           "nif_getnameinfo -> "
           "\r\n   SockAddr: %T"
           "\r\n   Flags:    %T"
           "\r\n", eSockAddr, eFlags) );

    if (!esock_decode_sockaddr(env, eSockAddr, &sa, &saLen))
        return enif_make_badarg(env);

    if (!decode_nameinfo_flags(env, eFlags, &flags))
        return enif_make_badarg(env);

    result = ngetnameinfo(env, &sa, saLen, flags);

    NDBG( ("NET",
           "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 SocketAddress* 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( ("NET", "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 = esock_make_ok2(env, info);
        }
        break;

#if defined(EAI_AGAIN)
    case EAI_AGAIN:
        result = esock_make_error(env, esock_atom_eagain);
        break;
#endif

#if defined(EAI_BADFLAGS)
    case EAI_BADFLAGS:
        result = esock_make_error(env, atom_ebadflags);
        break;
#endif

#if defined(EAI_FAIL)
    case EAI_FAIL:
        result = esock_make_error(env, atom_efail);
        break;
#endif

#if defined(EAI_FAMILY)
    case EAI_FAMILY:
        result = esock_make_error(env, atom_efamily);
        break;
#endif

#if defined(EAI_MEMORY)
    case EAI_MEMORY:
        result = esock_make_error(env, atom_emem);
        break;
#endif

#if defined(EAI_NONAME)
    case EAI_NONAME:
        result = esock_make_error(env, atom_enoname);
        break;
#endif

#if defined(EAI_OVERFLOW)
    case EAI_OVERFLOW:
        result = esock_make_error(env, atom_eoverflow);
        break;
#endif

#if defined(EAI_SYSTEM)
    case EAI_SYSTEM:
        result = esock_make_error_errno(env, get_errno());
        break;
#endif

    default:
        result = esock_make_error(env, esock_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( ("NET", "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( ("NET",
           "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( ("NET",
           "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( ("NET", "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( ("NET", "ngetaddrinfo -> res: %d\r\n", res) );
    
    switch (res) {
    case 0:
        {
            ERL_NIF_TERM addrInfo = encode_address_infos(env, addrInfoP);
            freeaddrinfo(addrInfoP);
            result = esock_make_ok2(env, addrInfo);
        }
        break;

#if defined(EAI_ADDRFAMILY)
    case EAI_ADDRFAMILY:
        result = esock_make_error(env, atom_eaddrfamily);
        break;
#endif

#if defined(EAI_AGAIN)
    case EAI_AGAIN:
        result = esock_make_error(env, esock_atom_eagain);
        break;
#endif

#if defined(EAI_BADFLAGS)
    case EAI_BADFLAGS:
        result = esock_make_error(env, atom_ebadflags);
        break;
#endif

#if defined(EAI_FAIL)
    case EAI_FAIL:
        result = esock_make_error(env, atom_efail);
        break;
#endif

#if defined(EAI_FAMILY)
    case EAI_FAMILY:
        result = esock_make_error(env, atom_efamily);
        break;
#endif

#if defined(EAI_MEMORY)
    case EAI_MEMORY:
        result = esock_make_error(env, atom_emem);
        break;
#endif

#if defined(EAI_NODATA)
    case EAI_NODATA:
        result = esock_make_error(env, atom_enodata);
        break;
#endif

#if defined(EAI_NONAME)
    case EAI_NONAME:
        result = esock_make_error(env, atom_enoname);
        break;
#endif

#if defined(EAI_SERVICE)
    case EAI_SERVICE:
        result = esock_make_error(env, atom_eservice);
        break;
#endif

#if defined(EAI_SOCKTYPE)
    case EAI_SOCKTYPE:
        result = esock_make_error(env, atom_esocktype);
        break;
#endif

#if defined(EAI_SYSTEM)
    case EAI_SYSTEM:
        result = esock_make_error(env, atom_esystem);
        break;
#endif

    default:
        result = esock_make_error(env, esock_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( ("NET", "nif_if_name2index -> entry (%d)\r\n", argc) );

    if (argc != 1) {
        return enif_make_badarg(env);
    }
    eifn = argv[0];

    NDBG( ("NET",
           "nif_if_name2index -> "
           "\r\n   Ifn: %T"
           "\r\n", argv[0]) );

    if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn)))
        return esock_make_error(env, esock_atom_einval);

    result = nif_name2index(env, ifn);

    NDBG( ("NET", "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( ("NET", "nif_name2index -> entry with ifn: %s\r\n", ifn) );

    idx = if_nametoindex(ifn);

    NDBG( ("NET", "nif_name2index -> idx: %d\r\n", idx) );

    if (idx == 0)
         return esock_make_error_errno(env, get_errno());
     else
         return esock_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( ("NET", "nif_if_index2name -> entry (%d)\r\n", argc) );

    if ((argc != 1) ||
        !GET_UINT(env, argv[0], &idx)) {
        return enif_make_badarg(env);
    }

    NDBG( ("NET", "nif_index2name -> "
           "\r\n   Idx: %T"
           "\r\n", argv[0]) );

    result = nif_index2name(env, idx);

    NDBG( ("NET", "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 = esock_make_ok2(env, MKS(env, ifn));
    } else {
        result = esock_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( ("NET", "nif_if_names -> entry (%d)\r\n", argc) );

    if (argc != 0) {
        return enif_make_badarg(env);
    }

    result = nif_names(env);

    NDBG( ("NET", "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( ("NET", "nif_names -> ifs: 0x%lX\r\n", ifs) );

    if (ifs == NULL) {
        result = esock_make_error_errno(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( ("NET", "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 = esock_make_ok2(env, MKLA(env, array, len));
            FREE(array);
        } else {
            result = esock_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( ("NET", "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
 * ----------------------------------------------------------------------
 */

/* 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, esock_atom_undefined) == 0) {
            *flags = 0;
            result = TRUE;
        } else {
            result = FALSE;
        }
    } else if (IS_LIST(env, eflags)) {
        NDBG( ("NET", "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, esock_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, esock_atom_undefined) == 0) {
            *stringP = NULL;
            result   = TRUE;
        } else {
            *stringP = NULL;
            result   = FALSE;
        }

    } else {

        result = esock_decode_string(env, eString, stringP);

    }

    return result;

}



static
ERL_NIF_TERM decode_bool(ErlNifEnv*   env,
                         ERL_NIF_TERM eBool,
                         BOOLEAN_T*   bool)
{
    if (COMPARE(eBool, esock_atom_true) == 0) {
        *bool = TRUE;
        return esock_atom_ok;
    } else if (COMPARE(eBool, esock_atom_false) == 0) {
        *bool = FALSE;
        return esock_atom_ok;
    } else {
        return esock_make_error(env, esock_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( ("NET", "encode_address_infos -> len: %d\r\n", len) );

    if (len > 0) {
        ERL_NIF_TERM*    array = MALLOC(len * sizeof(ERL_NIF_TERM)); // LEAK?
        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( ("NET", "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 fam, type, proto, addr, addrInfo;

    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);
    esock_encode_sockaddr(env,
                          (SocketAddress*) addrInfoP->ai_addr,
                          addrInfoP->ai_addrlen,
                          &addr);
    
    if (make_address_info(env, fam, type, proto, addr, &addrInfo) == NULL)
        return addrInfo;
    else
        return esock_atom_undefined; // We should to better...

}


/* Convert an "native" family to an erlang family (=domain).
 * 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;

    if (NULL != esock_encode_domain(env, family, &efam))
        efam = MKI(env, family);

    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;

    if (NULL != esock_encode_type(env, socktype, &etype))
        etype = MKI(env, socktype);

    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;

    if (NULL != esock_encode_protocol(env, proto, &eproto))
        eproto = MKI(env, proto);

    return eproto;
}



static
char* make_address_info(ErlNifEnv*    env,
                        ERL_NIF_TERM  fam,
                        ERL_NIF_TERM  sockType,
                        ERL_NIF_TERM  proto,
                        ERL_NIF_TERM  addr,
                        ERL_NIF_TERM* ai)
{
    ERL_NIF_TERM keys[] = {esock_atom_family,
                           esock_atom_type,
                           esock_atom_protocol,
                           esock_atom_addr};
    ERL_NIF_TERM vals[] = {fam, sockType, proto, addr};
    unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM);
    unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM);
    
    ESOCK_ASSERT( (numKeys == numVals) );
    
    if (!MKMA(env, keys, vals, numKeys, ai)) {
        *ai = esock_atom_undefined;
        return ESOCK_STR_EINVAL;
    } else {
        return NULL;
    }
}



/* ----------------------------------------------------------------------
 *  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_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}
};


static
BOOLEAN_T extract_debug(ErlNifEnv*   env,
                        ERL_NIF_TERM map)
{
    /*
     * We need to do this here since the "proper" atom has not been
     * created when this function is called.
     */
    ERL_NIF_TERM debug = MKA(env, "debug");
    
    return esock_extract_bool_from_map(env, map, debug, NET_NIF_DEBUG_DEFAULT);
}


/* =======================================================================
 * 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 = extract_debug(env, load_info);

    NDBG( ("NET", "on_load -> entry\r\n") );

    /* +++ Misc atoms +++ */
    atom_address_info              = MKA(env, str_address_info);
    atom_debug                     = MKA(env, str_debug);
    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);

    /* Error codes */
    atom_eaddrfamily     = MKA(env, str_eaddrfamily);
    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_emem            = MKA(env, str_emem);
    atom_enametoolong    = MKA(env, str_enametoolong);
    atom_enodata         = MKA(env, str_enodata);
    atom_enoname         = MKA(env, str_enoname);
    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);

    // For storing "global" things...
    // data.env       = enif_alloc_env(); // We should really check
    // data.version   = MKA(env, ERTS_VERSION);
    // data.buildDate = MKA(env, ERTS_BUILD_DATE);

    net = enif_open_resource_type_x(env,
                                    "net",
                                    &netInit,
                                    ERL_NIF_RT_CREATE,
                                    NULL);

    NDBG( ("NET", "on_load -> done\r\n") );

    return !net;
}

ERL_NIF_INIT(net, net_funcs, on_load, NULL, NULL, NULL)