1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2018-2020. All Rights Reserved.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * %CopyrightEnd%
19  *
20  * ----------------------------------------------------------------------
21  *  Purpose : The NIF (C) part of the net interface
22  *            This is a module of miscellaneous functions.
23  *
24  *            We (try to) avoid name clashes by prefixing "all" internal
25  *            function names with enet.
26  * ----------------------------------------------------------------------
27  *
28  */
29 
30 #define STATIC_ERLANG_NIF 1
31 
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 /* If we HAVE_SCTP_H and Solaris, we need to define the following in
38  * order to get SCTP working:
39  */
40 #if (defined(HAVE_SCTP_H) && defined(__sun) && defined(__SVR4))
41 #define SOLARIS10    1
42 /* WARNING: This is not quite correct, it may also be Solaris 11! */
43 #define _XPG4_2
44 #define __EXTENSIONS__
45 #endif
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <stddef.h>
50 #include <ctype.h>
51 #include <sys/types.h>
52 #include <errno.h>
53 #include <time.h>
54 
55 #ifdef HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
58 #ifdef HAVE_SYS_UIO_H
59 #include <sys/uio.h>
60 #endif
61 
62 #ifdef HAVE_NET_IF_DL_H
63 #include <net/if_dl.h>
64 #endif
65 
66 #ifdef HAVE_IFADDRS_H
67 #include <ifaddrs.h>
68 #elif defined(__PASE__)
69 /* PASE has this, but under a different name because AIX doesn't have it. */
70 #include <as400_protos.h>
71 /*
72  * We don't redefine the function names because they're used in other
73  * contexts, but the struct is safe to rename.
74  */
75 #define ifaddrs ifaddrs_pase
76 #endif
77 
78 #ifdef HAVE_NETPACKET_PACKET_H
79 #include <netpacket/packet.h>
80 #endif
81 
82 #ifdef HAVE_SYS_UN_H
83 #include <sys/un.h>
84 #endif
85 
86 /* SENDFILE STUFF HERE IF WE NEED IT... */
87 
88 #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
89 #define __DARWIN__ 1
90 #endif
91 
92 
93 #ifdef __WIN32__
94 #define STRNCASECMP               strncasecmp
95 #define INCL_WINSOCK_API_TYPEDEFS 1
96 
97 #ifndef WINDOWS_H_INCLUDES_WINSOCK2_H
98 #include <winsock2.h>
99 #endif
100 #include <windows.h>
101 #include <Ws2tcpip.h>   /* NEED VC 6.0 or higher */
102 
103 /* Visual studio 2008+: NTDDI_VERSION needs to be set for iphlpapi.h
104  * to define the right structures. It needs to be set to WINXP (or LONGHORN)
105  * for IPV6 to work and it's set lower by default, so we need to change it.
106  */
107 #ifdef HAVE_SDKDDKVER_H
108 #  include <sdkddkver.h>
109 #  ifdef NTDDI_VERSION
110 #    undef NTDDI_VERSION
111 #  endif
112 #  define NTDDI_VERSION NTDDI_WINXP
113 #endif
114 #include <iphlpapi.h>
115 
116 #undef WANT_NONBLOCKING
117 #include "sys.h"
118 
119 #else /* !__WIN32__ */
120 
121 #include <sys/time.h>
122 #ifdef NETDB_H_NEEDS_IN_H
123 #include <netinet/in.h>
124 #endif
125 #include <netdb.h>
126 
127 #include <sys/socket.h>
128 #include <netinet/in.h>
129 
130 #ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H
131 #include <rpc/types.h>
132 #endif
133 
134 #include <netinet/ip.h>
135 #include <netinet/tcp.h>
136 #include <netinet/udp.h>
137 #include <arpa/inet.h>
138 
139 #include <sys/param.h>
140 #ifdef HAVE_ARPA_NAMESER_H
141 #include <arpa/nameser.h>
142 #endif
143 
144 #ifdef HAVE_SYS_SOCKIO_H
145 #include <sys/sockio.h>
146 #endif
147 
148 #ifdef HAVE_SYS_IOCTL_H
149 #include <sys/ioctl.h>
150 #endif
151 
152 #include <net/if.h>
153 
154 #ifdef HAVE_SCHED_H
155 #include <sched.h>
156 #endif
157 
158 #ifdef HAVE_SETNS_H
159 #include <setns.h>
160 #endif
161 
162 #define HAVE_UDP
163 
164 #ifndef WANT_NONBLOCKING
165 #define WANT_NONBLOCKING
166 #endif
167 #include "sys.h"
168 
169 #endif
170 
171 #include <erl_nif.h>
172 
173 #include "socket_dbg.h"
174 #include "socket_int.h"
175 #include "socket_tarray.h"
176 #include "socket_util.h"
177 
178 
179 /* All platforms fail on malloc errors. */
180 #define FATAL_MALLOC
181 
182 
183 #ifdef __WIN32__
184 #define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__))
185 #else
186 #define net_gethostname(__buf__, __bufSz__) gethostname((__buf__), (__bufSz__))
187 #endif // __WIN32__
188 
189 
190 
191 /* *** Misc macros and defines *** */
192 
193 #ifdef __WIN32__
194 #define get_errno() WSAGetLastError()
195 #else
196 #define get_errno() errno
197 #endif
198 
199 
200 #define HOSTNAME_LEN 256
201 #define SERVICE_LEN  256
202 
203 
204 /* MAXHOSTNAMELEN could be 64 or 255 depending
205  * on the platform. Instead, use INET_MAXHOSTNAMELEN
206  * which is always 255 across all platforms
207  */
208 #define NET_MAXHOSTNAMELEN 255
209 
210 
211 /* =================================================================== *
212  *                                                                     *
213  *                        Various enif macros                          *
214  *                                                                     *
215  * =================================================================== */
216 
217 
218 #ifdef HAVE_SOCKLEN_T
219 #  define SOCKLEN_T socklen_t
220 #else
221 #  define SOCKLEN_T size_t
222 #endif
223 
224 /* Debug stuff... */
225 #define NET_NIF_DEBUG_DEFAULT FALSE
226 
227 #define NDBG( proto ) ESOCK_DBG_PRINTF( data.debug , proto )
228 
229 
230 typedef struct {
231     BOOLEAN_T debug;
232 } NetData;
233 
234 
235 
236 /* =================================================================== *
237  *                                                                     *
238  *                           Static data                               *
239  *                                                                     *
240  * =================================================================== */
241 
242 
243 static NetData data;
244 
245 
246 
247 /* ----------------------------------------------------------------------
248  *  F o r w a r d s
249  * ----------------------------------------------------------------------
250  */
251 
252 /* THIS IS JUST TEMPORARY */
253 extern char* erl_errno_id(int error);
254 
255 /* All the nif "callback" functions for the net API has
256  * the exact same API:
257  *
258  * nif_<funcname>(ErlNifEnv*         env,
259  *                int                argc,
260  *                const ERL_NIF_TERM argv[]);
261  *
262  * So, to simplify, use some macro magic to define those.
263  *
264  * These are the functions making up the "official" API.
265  */
266 
267 #define ENET_NIF_FUNCS                  \
268     ENET_NIF_FUNC_DEF(info);            \
269     ENET_NIF_FUNC_DEF(command);         \
270     ENET_NIF_FUNC_DEF(gethostname);     \
271     ENET_NIF_FUNC_DEF(getnameinfo);     \
272     ENET_NIF_FUNC_DEF(getaddrinfo);     \
273     ENET_NIF_FUNC_DEF(getifaddrs);      \
274     ENET_NIF_FUNC_DEF(if_name2index);   \
275     ENET_NIF_FUNC_DEF(if_index2name);   \
276     ENET_NIF_FUNC_DEF(if_names);
277 
278 #define ENET_NIF_FUNC_DEF(F)                              \
279     static ERL_NIF_TERM nif_##F(ErlNifEnv*         env,    \
280                                 int                argc,   \
281                                 const ERL_NIF_TERM argv[]);
282 ENET_NIF_FUNCS
283 #undef ENET_NIF_FUNC_DEF
284 
285 
286 /* And here comes the functions that does the actual work (for the most part) */
287 static ERL_NIF_TERM enet_command(ErlNifEnv*   env,
288                                  ERL_NIF_TERM cmd);
289 #if defined(HAVE_GETHOSTNAME)
290 static ERL_NIF_TERM enet_gethostname(ErlNifEnv* env);
291 #endif
292 
293 #if defined(HAVE_GETNAMEINFO)
294 static ERL_NIF_TERM enet_getnameinfo(ErlNifEnv*          env,
295                                      const ESockAddress* saP,
296                                      SOCKLEN_T           saLen,
297                                      int                 flags);
298 #endif
299 
300 #if defined(HAVE_GETADDRINFO)
301 static ERL_NIF_TERM enet_getaddrinfo(ErlNifEnv* env,
302                                      char*      host,
303                                      char*      serv);
304 #endif
305 
306 #if defined(HAVE_GETIFADDRS) || defined(__PASE__)
307 static ERL_NIF_TERM enet_getifaddrs(ErlNifEnv* env,
308                                     char*      netns);
309 #endif
310 
311 #if defined(HAVE_IF_NAMETOINDEX)
312 static ERL_NIF_TERM enet_if_name2index(ErlNifEnv* env,
313                                        char*      ifn);
314 #endif
315 
316 #if defined(HAVE_IF_INDEXTONAME)
317 static ERL_NIF_TERM enet_if_index2name(ErlNifEnv*   env,
318                                        unsigned int id);
319 #endif
320 
321 #if defined(HAVE_IF_NAMEINDEX) && defined(HAVE_IF_FREENAMEINDEX)
322 static ERL_NIF_TERM enet_if_names(ErlNifEnv* env);
323 static unsigned int enet_if_names_length(struct if_nameindex* p);
324 #endif
325 
326 /*
327 static void net_dtor(ErlNifEnv* env, void* obj);
328 static void net_stop(ErlNifEnv* env,
329                      void*      obj,
330                      int        fd,
331                      int        is_direct_call);
332 static void net_down(ErlNifEnv*           env,
333                      void*                obj,
334                      const ErlNifPid*     pid,
335                      const ErlNifMonitor* mon);
336 */
337 
338 static ERL_NIF_TERM enet_getifaddrs(ErlNifEnv* env,
339                                     char*      netns);
340 static ERL_NIF_TERM enet_getifaddrs_process(ErlNifEnv*      env,
341                                             struct ifaddrs* ifap);
342 static unsigned int enet_getifaddrs_length(struct ifaddrs* ifap);
343 static BOOLEAN_T encode_ifaddrs(ErlNifEnv*      env,
344                                 struct ifaddrs* ifap,
345                                 ERL_NIF_TERM*   eifa);
346 static ERL_NIF_TERM encode_ifaddrs_name(ErlNifEnv* env,
347                                         char*      name);
348 static ERL_NIF_TERM encode_ifaddrs_flags(ErlNifEnv*   env,
349                                          unsigned int flags);
350 static ERL_NIF_TERM encode_ifaddrs_addr(ErlNifEnv*       env,
351                                         struct sockaddr* sa);
352 static char* make_ifaddrs(ErlNifEnv*    env,
353                           ERL_NIF_TERM  name,
354                           ERL_NIF_TERM  flags,
355                           ERL_NIF_TERM  addr,
356                           ERL_NIF_TERM  netmask,
357                           ERL_NIF_TERM  ifu_key,
358                           ERL_NIF_TERM  ifu_value,
359                           ERL_NIF_TERM  data,
360                           ERL_NIF_TERM* ifAddrs);
361 #ifdef HAVE_SETNS
362 static BOOLEAN_T enet_getifaddrs_netns(ErlNifEnv*   env,
363                                        ERL_NIF_TERM map,
364                                        char**       netns);
365 static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err);
366 static BOOLEAN_T restore_network_namespace(int ns, int* err);
367 #endif
368 static BOOLEAN_T decode_nameinfo_flags(ErlNifEnv*         env,
369                                        const ERL_NIF_TERM eflags,
370                                        int*               flags);
371 static BOOLEAN_T decode_nameinfo_flags_list(ErlNifEnv*         env,
372                                             const ERL_NIF_TERM eflags,
373                                             int*               flags);
374 static
375 BOOLEAN_T decode_addrinfo_string(ErlNifEnv*         env,
376                                  const ERL_NIF_TERM eString,
377                                  char**             stringP);
378 static ERL_NIF_TERM decode_bool(ErlNifEnv*   env,
379                                 ERL_NIF_TERM ebool,
380                                 BOOLEAN_T*   ibool);
381 static ERL_NIF_TERM encode_address_infos(ErlNifEnv*       env,
382                                          struct addrinfo* addrInfo);
383 static ERL_NIF_TERM encode_address_info(ErlNifEnv*       env,
384                                         struct addrinfo* addrInfoP);
385 static unsigned int address_info_length(struct addrinfo* addrInfoP);
386 
387 static ERL_NIF_TERM encode_address_info_family(ErlNifEnv* env,
388                                          int        family);
389 static ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env,
390                                        int        socktype);
391 static ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env,
392                                         int        proto);
393 
394 static char* make_address_info(ErlNifEnv*    env,
395                                ERL_NIF_TERM  fam,
396                                ERL_NIF_TERM  sockType,
397                                ERL_NIF_TERM  proto,
398                                ERL_NIF_TERM  addr,
399                                ERL_NIF_TERM* ai);
400 
401 static BOOLEAN_T extract_debug(ErlNifEnv*   env,
402                                ERL_NIF_TERM map);
403 static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
404 
405 
406 #if HAVE_IN6
407 #  if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY
408 #    if HAVE_DECL_IN6ADDR_ANY_INIT
409 static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
410 #    else
411 static const struct in6_addr in6addr_any =
412     { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
413 #    endif /* HAVE_IN6ADDR_ANY_INIT */
414 #  endif /* ! HAVE_DECL_IN6ADDR_ANY */
415 
416 #  if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK
417 #    if HAVE_DECL_IN6ADDR_LOOPBACK_INIT
418 static const struct in6_addr in6addr_loopback =
419     { { IN6ADDR_LOOPBACK_INIT } };
420 #    else
421 static const struct in6_addr in6addr_loopback =
422     { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
423 #    endif /* HAVE_IN6ADDR_LOOPBACk_INIT */
424 #  endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */
425 #endif /* HAVE_IN6 */
426 
427 
428 
429 /* *** Local atoms ***
430  *
431  * These have been deprecated:
432  *     LOCAL_ATOM_DECL(idna_allow_unassigned);     Should really have been idn_...
433  *     LOCAL_ATOM_DECL(idna_use_std3_ascii_rules); Should really have been idn_...
434  */
435 
436 
437 #define LOCAL_ATOMS                             \
438     LOCAL_ATOM_DECL(address_info);              \
439     LOCAL_ATOM_DECL(automedia);                 \
440     LOCAL_ATOM_DECL(broadaddr);                 \
441     LOCAL_ATOM_DECL(broadcast);                 \
442     LOCAL_ATOM_DECL(debug);                     \
443     LOCAL_ATOM_DECL(dstaddr);                   \
444     LOCAL_ATOM_DECL(dynamic);                   \
445     LOCAL_ATOM_DECL(host);                      \
446     LOCAL_ATOM_DECL(idn);                       \
447     LOCAL_ATOM_DECL(master);                    \
448     LOCAL_ATOM_DECL(multicast);                 \
449     LOCAL_ATOM_DECL(name);                      \
450     LOCAL_ATOM_DECL(namereqd);                  \
451     LOCAL_ATOM_DECL(name_info);                 \
452     LOCAL_ATOM_DECL(netmask);                   \
453     LOCAL_ATOM_DECL(noarp);                     \
454     LOCAL_ATOM_DECL(nofqdn);                    \
455     LOCAL_ATOM_DECL(notrailers);                \
456     LOCAL_ATOM_DECL(numerichost);               \
457     LOCAL_ATOM_DECL(numericserv);               \
458     LOCAL_ATOM_DECL(pointopoint);               \
459     LOCAL_ATOM_DECL(portsel);                   \
460     LOCAL_ATOM_DECL(promisc);                   \
461     LOCAL_ATOM_DECL(running);                   \
462     LOCAL_ATOM_DECL(service);                   \
463     LOCAL_ATOM_DECL(slave);                     \
464     LOCAL_ATOM_DECL(up);
465 
466 #define LOCAL_ERROR_REASON_ATOMS               \
467     LOCAL_ATOM_DECL(eaddrfamily);              \
468     LOCAL_ATOM_DECL(ebadflags);                \
469     LOCAL_ATOM_DECL(efail);                    \
470     LOCAL_ATOM_DECL(efamily);                  \
471     LOCAL_ATOM_DECL(efault);                   \
472     LOCAL_ATOM_DECL(emem);                     \
473     LOCAL_ATOM_DECL(enametoolong);             \
474     LOCAL_ATOM_DECL(enodata);                  \
475     LOCAL_ATOM_DECL(enoname);                  \
476     LOCAL_ATOM_DECL(enxio);                    \
477     LOCAL_ATOM_DECL(eoverflow);                \
478     LOCAL_ATOM_DECL(eservice);                 \
479     LOCAL_ATOM_DECL(esocktype);                \
480     LOCAL_ATOM_DECL(esystem);
481 
482 #define LOCAL_ATOM_DECL(A) static ERL_NIF_TERM atom_##A
483 LOCAL_ATOMS
484 LOCAL_ERROR_REASON_ATOMS
485 #undef LOCAL_ATOM_DECL
486 
487 
488 /* *** net *** */
489 static ErlNifResourceType*    net;
490 static ErlNifResourceTypeInit netInit = {
491     NULL, // net_dtor,
492     NULL, // net_stop,
493     NULL  // (ErlNifResourceDown*) net_down
494 };
495 
496 
497 
498 /* ----------------------------------------------------------------------
499  *  N I F   F u n c t i o n s
500  * ----------------------------------------------------------------------
501  *
502  * Utility and admin functions:
503  * ----------------------------
504  * nif_info/0
505  * nif_command/1
506  *
507  * The "proper" net functions:
508  * ------------------------------
509  * nif_gethostname/0
510  * nif_getnameinfo/2
511  * nif_getaddrinfo/3
512  * nif_getifaddrs/1
513  * nif_if_name2index/1
514  * nif_if_index2name/1
515  * nif_if_names/0
516  *
517  */
518 
519 
520 /* ----------------------------------------------------------------------
521  * nif_info
522  *
523  * Description:
524  * This is currently just a placeholder...
525  */
526 static
nif_info(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])527 ERL_NIF_TERM nif_info(ErlNifEnv*         env,
528                       int                argc,
529                       const ERL_NIF_TERM argv[])
530 {
531 #if defined(__WIN32__)
532     return enif_raise_exception(env, MKA(env, "notsup"));
533 #else
534     ERL_NIF_TERM info, tmp;
535 
536     NDBG( ("NET", "info -> entry\r\n") );
537 
538     tmp  = enif_make_new_map(env);
539     if (!enif_make_map_put(env, tmp, atom_debug, BOOL2ATOM(data.debug), &info))
540         info = tmp;
541 
542     NDBG( ("NET", "info -> done: %T\r\n", info) );
543 
544     return info;
545 #endif
546 }
547 
548 
549 
550 /* ----------------------------------------------------------------------
551  * nif_command
552  *
553  * Description:
554  * This is a general purpose utility function.
555  *
556  * Arguments:
557  * Command - This is a general purpose command, of any type.
558  *           Currently, the only supported command is:
559  *
560  *                  {debug, boolean()}
561  */
562 static
nif_command(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])563 ERL_NIF_TERM nif_command(ErlNifEnv*         env,
564                          int                argc,
565                          const ERL_NIF_TERM argv[])
566 {
567 #if defined(__WIN32__)
568     return enif_raise_exception(env, MKA(env, "notsup"));
569 #else
570     ERL_NIF_TERM ecmd, result;
571 
572     NDBG( ("NET", "command -> entry (%d)\r\n", argc) );
573 
574     if (argc != 1)
575         return enif_make_badarg(env);
576 
577     ecmd = argv[0];
578 
579     NDBG( ("NET", "command -> ecmd: %T\r\n", ecmd) );
580 
581     result = enet_command(env, ecmd);
582 
583     NDBG( ("NET", "command -> result: %T\r\n", result) );
584 
585     return result;
586 #endif
587 }
588 
589 
590 
591 /*
592  * The command can, in principle, be anything, though currently we only
593  * support a debug command.
594  */
595 #if !defined(__WIN32__)
596 static
enet_command(ErlNifEnv * env,ERL_NIF_TERM cmd)597 ERL_NIF_TERM enet_command(ErlNifEnv*   env,
598                           ERL_NIF_TERM cmd)
599 {
600     const ERL_NIF_TERM* t;
601     int                 tsz;
602 
603     if (IS_TUPLE(env, cmd)) {
604         /* Could be the debug tuple */
605         if (!GET_TUPLE(env, cmd, &tsz, &t))
606             return esock_make_error(env, esock_atom_einval);
607 
608         if (tsz != 2)
609             return esock_make_error(env, esock_atom_einval);
610 
611         /* First element should be the atom 'debug' */
612         if (COMPARE(t[0], atom_debug) != 0)
613             return esock_make_error(env, esock_atom_einval);
614 
615         return decode_bool(env, t[1], &data.debug);
616 
617     } else {
618         return esock_make_error(env, esock_atom_einval);
619     }
620 
621 }
622 #endif
623 
624 
625 
626 /* ----------------------------------------------------------------------
627  * nif_gethostname
628  *
629  * Description:
630  * Access the hostname of the current processor.
631  *
632  */
633 static
nif_gethostname(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])634 ERL_NIF_TERM nif_gethostname(ErlNifEnv*         env,
635                              int                argc,
636                              const ERL_NIF_TERM argv[])
637 {
638 #if defined(__WIN32__)
639     return enif_raise_exception(env, MKA(env, "notsup"));
640 #elif defined(HAVE_GETHOSTNAME)
641     ERL_NIF_TERM result;
642 
643     NDBG( ("NET", "nif_gethostname -> entry (%d)\r\n", argc) );
644 
645     if (argc != 0)
646         return enif_make_badarg(env);
647 
648     result = enet_gethostname(env);
649 
650     NDBG( ("NET", "nif_gethostname -> done when result: %T\r\n", result) );
651 
652     return result;
653 #else
654     return esock_make_error(env, esock_atom_enotsup);
655 #endif
656 }
657 
658 
659 #if !defined(__WIN32__) && defined(HAVE_GETHOSTNAME)
660 static
enet_gethostname(ErlNifEnv * env)661 ERL_NIF_TERM enet_gethostname(ErlNifEnv* env)
662 {
663     ERL_NIF_TERM result;
664     char         buf[NET_MAXHOSTNAMELEN + 1];
665     int          res;
666 
667     res = net_gethostname(buf, sizeof(buf));
668 
669     NDBG( ("NET", "enet_gethostname -> gethostname res: %d\r\n", res) );
670 
671     switch (res) {
672     case 0:
673         result = esock_make_ok2(env, MKS(env, buf));
674         break;
675 
676     case EFAULT:
677         result = esock_make_error(env, atom_efault);
678         break;
679 
680     case EINVAL:
681         result = esock_make_error(env, esock_atom_einval);
682         break;
683 
684     case ENAMETOOLONG:
685         result = esock_make_error(env, atom_enametoolong);
686         break;
687 
688     default:
689         result = esock_make_error(env, MKI(env, res));
690         break;
691     }
692 
693     return result;
694 }
695 #endif
696 
697 
698 
699 
700 /* ----------------------------------------------------------------------
701  * nif_getnameinfo
702  *
703  * Description:
704  * Address-to-name translation in protocol-independent manner.
705  *
706  * Arguments:
707  * SockAddr - Socket Address (address and port)
708  * Flags    - The flags argument modifies the behavior of getnameinfo().
709  */
710 
711 static
nif_getnameinfo(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])712 ERL_NIF_TERM nif_getnameinfo(ErlNifEnv*         env,
713                              int                argc,
714                              const ERL_NIF_TERM argv[])
715 {
716 #if defined(__WIN32__)
717     return enif_raise_exception(env, MKA(env, "notsup"));
718 #elif defined(HAVE_GETNAMEINFO)
719     ERL_NIF_TERM result;
720     ERL_NIF_TERM eSockAddr, eFlags;
721     int          flags = 0; // Just in case...
722     ESockAddress sa;
723     SOCKLEN_T    saLen = 0; // Just in case...
724     char*        xres;
725 
726     NDBG( ("NET", "nif_getnameinfo -> entry (%d)\r\n", argc) );
727 
728     if (argc != 2)
729         return enif_make_badarg(env);
730     eSockAddr = argv[0];
731     eFlags    = argv[1];
732 
733     NDBG( ("NET",
734            "nif_getnameinfo -> "
735            "\r\n   SockAddr: %T"
736            "\r\n   Flags:    %T"
737            "\r\n", eSockAddr, eFlags) );
738 
739     if ((xres = esock_decode_sockaddr(env, eSockAddr, &sa, &saLen)) != NULL) {
740         NDBG( ("NET", "nif_getnameinfo -> failed decode sockaddr: %s\r\n", xres) );
741         return esock_make_error_str(env, xres);
742     }
743 
744     NDBG( ("NET", "nif_getnameinfo -> (try) decode flags\r\n") );
745 
746     if (!decode_nameinfo_flags(env, eFlags, &flags))
747         return enif_make_badarg(env);
748 
749     result = enet_getnameinfo(env, &sa, saLen, flags);
750 
751     NDBG( ("NET",
752            "nif_getnameinfo -> done when result: "
753            "\r\n   %T\r\n", result) );
754 
755     return result;
756 #else
757     return esock_make_error(env, esock_atom_enotsup);
758 #endif
759 }
760 
761 
762 
763 /* Given the provided sock(et) address (and flags), retreive the host and
764  * service info.
765  */
766 #if !defined(__WIN32__) && defined(HAVE_GETNAMEINFO)
767 static
enet_getnameinfo(ErlNifEnv * env,const ESockAddress * saP,SOCKLEN_T saLen,int flags)768 ERL_NIF_TERM enet_getnameinfo(ErlNifEnv*          env,
769                               const ESockAddress* saP,
770                               SOCKLEN_T           saLen,
771                               int                 flags)
772 {
773     ERL_NIF_TERM result;
774     char         host[HOSTNAME_LEN];
775     SOCKLEN_T    hostLen = sizeof(host);
776     char         serv[SERVICE_LEN];
777     SOCKLEN_T    servLen = sizeof(serv);
778 
779     int res = getnameinfo((struct sockaddr*) saP, saLen,
780                           host, hostLen,
781                           serv, servLen,
782                           flags);
783 
784     NDBG( ("NET", "enet_getnameinfo -> res: %d\r\n", res) );
785 
786     switch (res) {
787     case 0:
788         {
789             ERL_NIF_TERM keys[] = {atom_host,      atom_service};
790             ERL_NIF_TERM vals[] = {MKS(env, host), MKS(env, serv)};
791             ERL_NIF_TERM info;
792             unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM);
793             unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM);
794 
795             ESOCK_ASSERT( (numKeys == numVals) );
796 
797             if (!MKMA(env, keys, vals, numKeys, &info))
798                 return enif_make_badarg(env);
799 
800             result = esock_make_ok2(env, info);
801         }
802         break;
803 
804 #if defined(EAI_AGAIN)
805     case EAI_AGAIN:
806         result = esock_make_error(env, esock_atom_eagain);
807         break;
808 #endif
809 
810 #if defined(EAI_BADFLAGS)
811     case EAI_BADFLAGS:
812         result = esock_make_error(env, atom_ebadflags);
813         break;
814 #endif
815 
816 #if defined(EAI_FAIL)
817     case EAI_FAIL:
818         result = esock_make_error(env, atom_efail);
819         break;
820 #endif
821 
822 #if defined(EAI_FAMILY)
823     case EAI_FAMILY:
824         result = esock_make_error(env, atom_efamily);
825         break;
826 #endif
827 
828 #if defined(EAI_MEMORY)
829     case EAI_MEMORY:
830         result = esock_make_error(env, atom_emem);
831         break;
832 #endif
833 
834 #if defined(EAI_NONAME)
835     case EAI_NONAME:
836         result = esock_make_error(env, atom_enoname);
837         break;
838 #endif
839 
840 #if defined(EAI_OVERFLOW)
841     case EAI_OVERFLOW:
842         result = esock_make_error(env, atom_eoverflow);
843         break;
844 #endif
845 
846 #if defined(EAI_SYSTEM)
847     case EAI_SYSTEM:
848         result = esock_make_error_errno(env, get_errno());
849         break;
850 #endif
851 
852     default:
853         result = esock_make_error(env, esock_atom_einval);
854         break;
855     }
856 
857     return result;
858 }
859 #endif
860 
861 
862 
863 /* ----------------------------------------------------------------------
864  * nif_getaddrinfo
865  *
866  * Description:
867  * Network address and service translation.
868  *
869  * Arguments:
870  * Host    - Host name (either a string or the atom undefined)
871  * Service - Service name (either a string or the atom undefined)
872  * Hints   - Hints for the lookup (address info record) (currently *ignored*)
873  */
874 
875 static
nif_getaddrinfo(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])876 ERL_NIF_TERM nif_getaddrinfo(ErlNifEnv*         env,
877                              int                argc,
878                              const ERL_NIF_TERM argv[])
879 {
880 #if defined(__WIN32__)
881     return enif_raise_exception(env, MKA(env, "notsup"));
882 #elif defined(HAVE_GETADDRINFO)
883     ERL_NIF_TERM     result, eHostName, eServName; //, eHints;
884     char*            hostName;
885     char*            servName;
886     // struct addrinfo* hints;
887 
888     NDBG( ("NET", "nif_getaddrinfo -> entry (%d)\r\n", argc) );
889 
890     if (argc != 3) {
891         return enif_make_badarg(env);
892     }
893     eHostName = argv[0];
894     eServName = argv[1];
895     // eHints    = argv[2];
896 
897     NDBG( ("NET",
898            "nif_getaddrinfo -> "
899            "\r\n   ehost:    %T"
900            "\r\n   eservice: %T"
901            "\r\n   ehints:   %T"
902            "\r\n", argv[0], argv[1], argv[2]) );
903 
904     if (!decode_addrinfo_string(env, eHostName, &hostName))
905         return enif_make_badarg(env);
906 
907     if (!decode_addrinfo_string(env, eServName, &servName))
908         return enif_make_badarg(env);
909 
910     /*
911       if (decode_addrinfo_hints(env, eHints, &hints))
912       return enif_make_badarg(env);
913     */
914 
915     if ((hostName == NULL) && (servName == NULL))
916         return enif_make_badarg(env);
917 
918     result = enet_getaddrinfo(env, hostName, servName);
919 
920     if (hostName != NULL)
921         FREE(hostName);
922 
923     if (servName != NULL)
924         FREE(servName);
925 
926     /*
927       if (hints != NULL)
928       FREE(hints);
929     */
930 
931     NDBG( ("NET",
932            "nif_getaddrinfo -> done when result: "
933            "\r\n   %T\r\n", result) );
934 
935     return result;
936 #else
937     return esock_make_error(env, esock_atom_enotsup);
938 #endif
939 }
940 
941 
942 #if !defined(__WIN32__) && defined(HAVE_GETADDRINFO)
943 static
enet_getaddrinfo(ErlNifEnv * env,char * host,char * serv)944 ERL_NIF_TERM enet_getaddrinfo(ErlNifEnv* env,
945                               char*      host,
946                               char*      serv)
947 {
948     ERL_NIF_TERM     result;
949     struct addrinfo* addrInfoP;
950     int              res;
951 
952     NDBG( ("NET", "enet_getaddrinfo -> entry with"
953            "\r\n   host: %s"
954            "\r\n   serv: %s"
955            "\r\n",
956            ((host == NULL) ? "NULL" : host),
957            ((serv == NULL) ? "NULL" : serv)) );
958 
959     res = getaddrinfo(host, serv, NULL, &addrInfoP);
960 
961     NDBG( ("NET", "enet_getaddrinfo -> res: %d\r\n", res) );
962 
963     switch (res) {
964     case 0:
965         {
966             ERL_NIF_TERM addrInfo = encode_address_infos(env, addrInfoP);
967             freeaddrinfo(addrInfoP);
968             result = esock_make_ok2(env, addrInfo);
969         }
970         break;
971 
972 #if defined(EAI_ADDRFAMILY)
973     case EAI_ADDRFAMILY:
974         result = esock_make_error(env, atom_eaddrfamily);
975         break;
976 #endif
977 
978 #if defined(EAI_AGAIN)
979     case EAI_AGAIN:
980         result = esock_make_error(env, esock_atom_eagain);
981         break;
982 #endif
983 
984 #if defined(EAI_BADFLAGS)
985     case EAI_BADFLAGS:
986         result = esock_make_error(env, atom_ebadflags);
987         break;
988 #endif
989 
990 #if defined(EAI_FAIL)
991     case EAI_FAIL:
992         result = esock_make_error(env, atom_efail);
993         break;
994 #endif
995 
996 #if defined(EAI_FAMILY)
997     case EAI_FAMILY:
998         result = esock_make_error(env, atom_efamily);
999         break;
1000 #endif
1001 
1002 #if defined(EAI_MEMORY)
1003     case EAI_MEMORY:
1004         result = esock_make_error(env, atom_emem);
1005         break;
1006 #endif
1007 
1008 #if defined(EAI_NODATA)
1009     case EAI_NODATA:
1010         result = esock_make_error(env, atom_enodata);
1011         break;
1012 #endif
1013 
1014 #if defined(EAI_NONAME)
1015     case EAI_NONAME:
1016         result = esock_make_error(env, atom_enoname);
1017         break;
1018 #endif
1019 
1020 #if defined(EAI_SERVICE)
1021     case EAI_SERVICE:
1022         result = esock_make_error(env, atom_eservice);
1023         break;
1024 #endif
1025 
1026 #if defined(EAI_SOCKTYPE)
1027     case EAI_SOCKTYPE:
1028         result = esock_make_error(env, atom_esocktype);
1029         break;
1030 #endif
1031 
1032 #if defined(EAI_SYSTEM)
1033     case EAI_SYSTEM:
1034         result = esock_make_error(env, atom_esystem);
1035         break;
1036 #endif
1037 
1038     default:
1039         result = esock_make_error(env, esock_atom_einval);
1040         break;
1041     }
1042 
1043     return result;
1044 }
1045 #endif
1046 
1047 
1048 
1049 /* ----------------------------------------------------------------------
1050  * nif_getifaddrs
1051  *
1052  * Description:
1053  * Get interface addresses
1054  *
1055  * Arguments:
1056  * Extra - A way to pass 'extra' arguments.
1057  *         Currently only used for netns (name space).
1058  */
1059 
1060 static
nif_getifaddrs(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])1061 ERL_NIF_TERM nif_getifaddrs(ErlNifEnv*         env,
1062                             int                argc,
1063                             const ERL_NIF_TERM argv[])
1064 {
1065 #if defined(__WIN32__)
1066     return enif_raise_exception(env, MKA(env, "notsup"));
1067 #elif defined(HAVE_GETIFADDRS) || defined(__PASE__)
1068 #ifdef HAVE_SETNS
1069     ERL_NIF_TERM extra;
1070 #endif
1071     char*        netns;
1072     ERL_NIF_TERM result;
1073 
1074     NDBG( ("NET", "nif_getifaddrs -> entry (%d)\r\n", argc) );
1075 
1076     if ((argc != 1) ||
1077         !IS_MAP(env,  argv[0])) {
1078         return enif_make_badarg(env);
1079     }
1080 #ifdef HAVE_SETNS
1081     extra = argv[0];
1082 #endif
1083 
1084 
1085 #ifdef HAVE_SETNS
1086     /* We *currently* only support one extra option: netns */
1087     if (!enet_getifaddrs_netns(env, extra, &netns)) {
1088         NDBG( ("NET", "nif_getifaddrs -> namespace: %s\r\n", netns) );
1089         return enif_make_badarg(env);
1090     }
1091 #else
1092     netns = NULL;
1093 #endif
1094 
1095     result = enet_getifaddrs(env, netns);
1096 
1097     NDBG( ("NET",
1098            "nif_getifaddrs -> done when result: "
1099            "\r\n   %T\r\n", result) );
1100 
1101     return result;
1102 #else // HAVE_GETIFADDRS
1103     return esock_make_error(env, esock_atom_enotsup);
1104 #endif
1105 }
1106 
1107 
1108 #if defined(HAVE_GETIFADDRS) || defined(__PASE__)
1109 #ifdef HAVE_SETNS
1110 /* enet_getifaddrs_netns - extract the netns field from the 'extra' map
1111  *
1112  * Note that the 'extra' map *may* contain other options, but here we
1113  * only care about 'netns'.
1114  */
1115 static
enet_getifaddrs_netns(ErlNifEnv * env,ERL_NIF_TERM map,char ** netns)1116 BOOLEAN_T enet_getifaddrs_netns(ErlNifEnv* env, ERL_NIF_TERM map, char** netns)
1117 {
1118     size_t       sz;
1119     ERL_NIF_TERM key;
1120     ERL_NIF_TERM value;
1121     unsigned int len;
1122     char*        buf;
1123     int          written;
1124 
1125     /* Note that its acceptable that the extra map is empty */
1126     if (!enif_get_map_size(env, map, &sz) ||
1127         (sz != 1)) {
1128         *netns = NULL;
1129         return TRUE;
1130     }
1131 
1132     /* Regardless of the content of the 'extra' map, we only care about 'netns' */
1133     key = enif_make_atom(env, "netns");
1134     if (!GET_MAP_VAL(env, map, key, &value)) {
1135         *netns = NULL;
1136         return TRUE;
1137     }
1138 
1139     /* So far so good. The value should be a string, check. */
1140     if (!enif_is_list(env, value)) {
1141         *netns = NULL; // Just in case...
1142         return FALSE;
1143     }
1144 
1145     if (!enif_get_list_length(env, value, &len)) {
1146         *netns = NULL; // Just in case...
1147         return FALSE;
1148     }
1149 
1150     if ((buf = MALLOC(len+1)) == NULL) {
1151         *netns = NULL; // Just in case...
1152         return FALSE;
1153     }
1154 
1155     written = enif_get_string(env, value, buf, len+1, ERL_NIF_LATIN1);
1156     if (written == (len+1)) {
1157         *netns = buf;
1158         return TRUE;
1159     } else {
1160         *netns = NULL; // Just in case...
1161         return FALSE;
1162     }
1163 }
1164 #endif
1165 
1166 
1167 
1168 static
enet_getifaddrs(ErlNifEnv * env,char * netns)1169 ERL_NIF_TERM enet_getifaddrs(ErlNifEnv* env, char* netns)
1170 {
1171     ERL_NIF_TERM    result;
1172     struct ifaddrs* ifap;
1173     int             save_errno;
1174 #ifdef HAVE_SETNS
1175     int             current_ns = 0;
1176 #endif
1177 
1178     NDBG( ("NET", "enet_getifaddrs -> entry with"
1179            "\r\n   netns: %s"
1180            "\r\n", ((netns == NULL) ? "NULL" : netns)) );
1181 
1182 #ifdef HAVE_SETNS
1183     if ((netns != NULL) &&
1184         !change_network_namespace(netns, &current_ns, &save_errno))
1185         return esock_make_error_errno(env, save_errno);
1186 #endif
1187 
1188 #ifdef __PASE__
1189     if (0 == Qp2getifaddrs(&ifap)) {
1190 #else
1191     if (0 == getifaddrs(&ifap)) {
1192 #endif
1193         result = enet_getifaddrs_process(env, ifap);
1194 #ifdef __PASE__
1195         Qp2freeifaddrs(ifap);
1196 #else
1197         freeifaddrs(ifap);
1198 #endif
1199     } else {
1200         save_errno = get_errno();
1201 
1202         NDBG( ("NET", "enet_getifaddrs -> failed get addrs: %d", save_errno) );
1203 
1204         result = esock_make_error_errno(env, save_errno);
1205     }
1206 
1207 
1208 #ifdef HAVE_SETNS
1209     if ((netns != NULL) &&
1210         !restore_network_namespace(current_ns, &save_errno))
1211         return esock_make_error_errno(env, save_errno);
1212 
1213     if (netns != NULL)
1214         FREE(netns);
1215 #endif
1216 
1217     NDBG( ("NET", "enet_getifaddrs -> done when"
1218            "\r\n   result: %T"
1219            "\r\n", result) );
1220 
1221     return result;
1222 }
1223 
1224 
1225 static
1226 ERL_NIF_TERM enet_getifaddrs_process(ErlNifEnv* env, struct ifaddrs* ifap)
1227 {
1228     ERL_NIF_TERM result;
1229     unsigned int len = ((ifap == NULL) ? 0 : enet_getifaddrs_length(ifap));
1230 
1231     NDBG( ("NET", "enet_getifaddrs_process -> len: %d\r\n", len) );
1232 
1233     if (len > 0) {
1234         ERL_NIF_TERM*   array = MALLOC(len * sizeof(ERL_NIF_TERM));
1235         unsigned int    i     = 0;
1236         struct ifaddrs* p     = ifap;
1237 
1238         while (i < len) {
1239             ERL_NIF_TERM entry;
1240 
1241             if (!encode_ifaddrs(env, p, &entry)) {
1242                 /* If this fail, we are f*ed, so give up */
1243                 FREE(array);
1244                 return esock_make_error(env, esock_atom_einval);
1245             }
1246 
1247             NDBG( ("NET", "enet_getifaddrs_process -> entry: %T\r\n", entry) );
1248 
1249             array[i] = entry;
1250             p = p->ifa_next;
1251             i++;
1252         }
1253 
1254         NDBG( ("NET", "enet_getifaddrs_process -> all entries processed\r\n") );
1255 
1256         result = esock_make_ok2(env, MKLA(env, array, len));
1257         FREE(array);
1258 
1259     } else {
1260         result = esock_make_ok2(env, MKEL(env));
1261     }
1262 
1263     NDBG( ("NET", "enet_getifaddrs_process -> result: "
1264            "\r\n   %T\r\n", result) );
1265 
1266     return result;
1267 }
1268 
1269 
1270 
1271 /* Calculate the length of the interface adress linked list
1272  * The list is NULL-terminated, so the only way is to
1273  * iterate through the list until we find next = NULL.
1274  */
1275 static
1276 unsigned int enet_getifaddrs_length(struct ifaddrs* ifap)
1277 {
1278     unsigned int    len = 1;
1279     struct ifaddrs* tmp;
1280     BOOLEAN_T       done = FALSE;
1281 
1282     tmp = ifap;
1283 
1284     while (!done) {
1285         if (tmp->ifa_next != NULL) {
1286             len++;
1287             tmp = tmp->ifa_next;
1288         } else {
1289             done = TRUE;
1290         }
1291     }
1292 
1293     return len;
1294 }
1295 
1296 
1297 
1298 static
1299 BOOLEAN_T encode_ifaddrs(ErlNifEnv*      env,
1300                          struct ifaddrs* ifap,
1301                          ERL_NIF_TERM*   eifa)
1302 {
1303     char*        xres;
1304     ERL_NIF_TERM ename, eflags, eaddr, enetmask, eifu_key, eifu_value, edata;
1305     ERL_NIF_TERM eifAddrs;
1306 
1307     ename     = encode_ifaddrs_name(env,  ifap->ifa_name);
1308     NDBG( ("NET", "encode_ifaddrs -> name: %T\r\n", ename) );
1309     eflags    = encode_ifaddrs_flags(env, ifap->ifa_flags);
1310     NDBG( ("NET", "encode_ifaddrs -> flags: %T\r\n", eflags) );
1311     eaddr     = encode_ifaddrs_addr(env,  ifap->ifa_addr);
1312     NDBG( ("NET", "encode_ifaddrs -> addr: %T\r\n", eaddr) );
1313     enetmask  = encode_ifaddrs_addr(env,  ifap->ifa_netmask);
1314     NDBG( ("NET", "encode_ifaddrs -> netmask: %T\r\n", enetmask) );
1315     if (ifap->ifa_dstaddr && (ifap->ifa_flags & IFF_POINTOPOINT)) {
1316         eifu_key   = atom_dstaddr;
1317         eifu_value = encode_ifaddrs_addr(env, ifap->ifa_dstaddr);
1318     } else if (ifap->ifa_broadaddr && (ifap->ifa_flags & IFF_BROADCAST)) {
1319         eifu_key   = atom_broadaddr;
1320         eifu_value = encode_ifaddrs_addr(env, ifap->ifa_broadaddr);
1321     } else {
1322         eifu_key   = esock_atom_undefined;
1323         eifu_value = esock_atom_undefined;
1324     }
1325     NDBG( ("NET", "encode_ifaddrs -> ifu: "
1326             "\r\n   key: %T"
1327             "\r\n   val: %T"
1328             "\r\n", eifu_key, eifu_value) );
1329     /* Don't know how to encode this yet...
1330      * We don't even know the size...
1331      */
1332     edata = esock_atom_undefined;
1333 
1334     if ((xres = make_ifaddrs(env,
1335                              ename, eflags, eaddr, enetmask,
1336                              eifu_key, eifu_value, edata,
1337                              &eifAddrs)) == NULL) {
1338         NDBG( ("NET", "encode_ifaddrs -> encoded ifAddrs: %T\r\n", eifAddrs) );
1339         *eifa = eifAddrs;
1340 
1341         return TRUE;
1342 
1343     } else {
1344 
1345         /* *** Ouch ***
1346          *
1347          * Failed to construct the ifaddrs map => try for a minumum map
1348          *
1349          */
1350         ERL_NIF_TERM keys[]  = {atom_name};
1351         ERL_NIF_TERM vals[]  = {ename};
1352         unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM);
1353 
1354         esock_warning_msg("Failed constructing IfAddr map for %s: %s",
1355                           ifap->ifa_name, xres);
1356 
1357         if (!MKMA(env, keys, vals, numKeys, eifa)) {
1358             // We are in a bad way, give up */
1359             *eifa = esock_atom_undefined;
1360             return FALSE;
1361         } else {
1362             return TRUE;
1363         }
1364 
1365     }
1366 }
1367 
1368 
1369 
1370 static
1371 ERL_NIF_TERM encode_ifaddrs_name(ErlNifEnv* env, char* name)
1372 {
1373     return ((name == NULL) ? esock_atom_undefined : MKS(env, name));
1374 }
1375 
1376 
1377 
1378 static
1379 ERL_NIF_TERM encode_ifaddrs_flags(ErlNifEnv* env, unsigned int flags)
1380 {
1381     SocketTArray ta = TARRAY_CREATE(16);
1382     ERL_NIF_TERM eflags;
1383 
1384 #if defined(IFF_UP)
1385     if (flags & IFF_UP)
1386         TARRAY_ADD(ta, atom_up);
1387 #endif
1388 
1389 #if defined(IFF_BROADCAST)
1390     if (flags & IFF_BROADCAST)
1391         TARRAY_ADD(ta, atom_broadcast);
1392 #endif
1393 
1394 #if defined(IFF_DEBUG)
1395     if (flags & IFF_DEBUG)
1396         TARRAY_ADD(ta, atom_debug);
1397 #endif
1398 
1399 #if defined(IFF_LOOPBACK)
1400     if (flags & IFF_LOOPBACK)
1401         TARRAY_ADD(ta, esock_atom_loopback);
1402 #endif
1403 
1404 #if defined(IFF_POINTOPOINT)
1405     if (flags & IFF_POINTOPOINT)
1406         TARRAY_ADD(ta, atom_pointopoint);
1407 #endif
1408 
1409 #if defined(IFF_NOTRAILERS)
1410     if (flags & IFF_NOTRAILERS)
1411         TARRAY_ADD(ta, atom_notrailers);
1412 #endif
1413 
1414 #if defined(IFF_RUNNING)
1415     if (flags & IFF_RUNNING)
1416         TARRAY_ADD(ta, atom_running);
1417 #endif
1418 
1419 #if defined(IFF_NOARP)
1420     if (flags & IFF_NOARP)
1421         TARRAY_ADD(ta, atom_noarp);
1422 #endif
1423 
1424 #if defined(IFF_PROMISC)
1425     if (flags & IFF_PROMISC)
1426         TARRAY_ADD(ta, atom_promisc);
1427 #endif
1428 
1429 #if defined(IFF_MASTER)
1430     if (flags & IFF_MASTER)
1431         TARRAY_ADD(ta, atom_master);
1432 #endif
1433 
1434 #if defined(IFF_SLAVE)
1435     if (flags & IFF_SLAVE)
1436         TARRAY_ADD(ta, atom_slave);
1437 #endif
1438 
1439 #if defined(IFF_MULTICAST)
1440     if (flags & IFF_MULTICAST)
1441         TARRAY_ADD(ta, atom_multicast);
1442 #endif
1443 
1444 #if defined(IFF_PORTSEL)
1445     if (flags & IFF_PORTSEL)
1446         TARRAY_ADD(ta, atom_portsel);
1447 #endif
1448 
1449 #if defined(IFF_AUTOMEDIA)
1450     if (flags & IFF_AUTOMEDIA)
1451         TARRAY_ADD(ta, atom_automedia);
1452 #endif
1453 
1454 #if defined(IFF_DYNAMIC)
1455     if (flags & IFF_DYNAMIC)
1456         TARRAY_ADD(ta, atom_dynamic);
1457 #endif
1458 
1459     TARRAY_TOLIST(ta, env, &eflags);
1460 
1461     return eflags;
1462 }
1463 
1464 
1465 static
1466 ERL_NIF_TERM encode_ifaddrs_addr(ErlNifEnv* env, struct sockaddr* sa)
1467 {
1468     ERL_NIF_TERM esa;
1469 
1470     if (sa != NULL) {
1471         unsigned int sz = sizeof(ESockAddress);
1472         if (esock_encode_sockaddr(env, (ESockAddress*) sa, sz, &esa) != NULL) {
1473             NDBG( ("NET", "encode_ifaddrs_addr -> "
1474                    "encoding address field 0x%lX failed => use default\r\n", sa) );
1475             esa = esock_atom_undefined;
1476         }
1477     } else {
1478         esa = esock_atom_undefined;
1479     }
1480 
1481     return esa;
1482 }
1483 
1484 
1485 static
1486 char* make_ifaddrs(ErlNifEnv*    env,
1487                    ERL_NIF_TERM  ename,
1488                    ERL_NIF_TERM  eflags,
1489                    ERL_NIF_TERM  eaddr,
1490                    ERL_NIF_TERM  enetmask,
1491                    ERL_NIF_TERM  eifu_key,
1492                    ERL_NIF_TERM  eifu_value,
1493                    ERL_NIF_TERM  edata,
1494                    ERL_NIF_TERM* eifAddrs)
1495 {
1496     /* Several of these values can be (the atom) undefined, which
1497      * means that they should *not* be included in the result map.
1498      */
1499     ERL_NIF_TERM keys[6]; // There are only (at most) siz (6) fields...
1500     ERL_NIF_TERM vals[6];
1501     unsigned int len = sizeof(keys) / sizeof(ERL_NIF_TERM); // Just in case...
1502     unsigned int idx = 0;
1503 
1504     /* *** Name *** */
1505     NDBG( ("NET", "make_ifaddrs -> name: %T\r\n", ename) );
1506     keys[idx] = atom_name;
1507     vals[idx] = ename;
1508     idx++;
1509 
1510     /* *** Flags *** */
1511     NDBG( ("NET", "make_ifaddrs -> flags: %T\r\n", eflags) );
1512     keys[idx] = esock_atom_flags;
1513     vals[idx] = eflags;
1514     idx++;
1515 
1516     /* *** Addr (can be 'undefined' = NULL) *** */
1517     NDBG( ("NET", "make_ifaddrs -> addr: %T\r\n", eaddr) );
1518     if (COMPARE(eaddr, esock_atom_undefined) != 0) {
1519         keys[idx] = esock_atom_addr;
1520         vals[idx] = eaddr;
1521         idx++;
1522     } else {
1523         len--;
1524     }
1525 
1526     /* *** Netmask (can be 'undefined' = NULL) *** */
1527     NDBG( ("NET", "make_ifaddrs -> netmask: %T\r\n", enetmask) );
1528     if (COMPARE(enetmask, esock_atom_undefined) != 0) {
1529         keys[idx] = atom_netmask;
1530         vals[idx] = enetmask;
1531         idx++;
1532     } else {
1533         len--;
1534     }
1535 
1536     /* *** Netmask (can be 'undefined' = NULL) *** */
1537     NDBG( ("NET", "make_ifaddrs -> ifu: %T, %T\r\n", eifu_key, eifu_value) );
1538     if ((COMPARE(eifu_key, esock_atom_undefined) != 0) &&
1539         (COMPARE(eifu_value, esock_atom_undefined) != 0)) {
1540         keys[idx] = eifu_key;
1541         vals[idx] = eifu_value;
1542         idx++;
1543     } else {
1544         len--;
1545     }
1546 
1547     /* *** Data (can be 'undefined' = NULL) *** */
1548     NDBG( ("NET", "make_ifaddrs -> data: %T\r\n", edata) );
1549     if (COMPARE(edata, esock_atom_undefined) != 0) {
1550         keys[idx] = esock_atom_data;
1551         vals[idx] = edata;
1552         idx++;
1553     } else {
1554         len--;
1555     }
1556 
1557     NDBG( ("NET", "make_ifaddrs -> construct ifa with:"
1558            "\r\n   len: %d"
1559            "\r\n"
1560            ) );
1561     if (!MKMA(env, keys, vals, len, eifAddrs)) {
1562         NDBG( ("NET", "make_ifaddrs -> "
1563                "failed contructing interface adress map\r\n") );
1564         *eifAddrs = esock_atom_undefined;
1565         return ESOCK_STR_EINVAL;
1566     } else {
1567         return NULL;
1568     }
1569 }
1570 
1571 #endif // HAVE_GETIFADDRS
1572 
1573 
1574 /* ----------------------------------------------------------------------
1575  * nif_if_name2index
1576  *
1577  * Description:
1578  * Perform a Interface Name to Interface Index translation.
1579  *
1580  * Arguments:
1581  * Ifn - Interface name to be translated.
1582  */
1583 
1584 static
1585 ERL_NIF_TERM nif_if_name2index(ErlNifEnv*         env,
1586                                int                argc,
1587                                const ERL_NIF_TERM argv[])
1588 {
1589 #if defined(__WIN32__)
1590     return enif_raise_exception(env, MKA(env, "notsup"));
1591 #elif defined(HAVE_IF_NAMETOINDEX)
1592     ERL_NIF_TERM eifn, result;
1593     char         ifn[IF_NAMESIZE+1];
1594 
1595     NDBG( ("NET", "nif_if_name2index -> entry (%d)\r\n", argc) );
1596 
1597     if (argc != 1) {
1598         return enif_make_badarg(env);
1599     }
1600     eifn = argv[0];
1601 
1602     NDBG( ("NET",
1603            "nif_if_name2index -> "
1604            "\r\n   Ifn: %T"
1605            "\r\n", argv[0]) );
1606 
1607     if (0 >= GET_STR(env, eifn, ifn, sizeof(ifn)))
1608         return esock_make_error(env, esock_atom_einval);
1609 
1610     result = enet_if_name2index(env, ifn);
1611 
1612     NDBG( ("NET", "nif_if_name2index -> done when result: %T\r\n", result) );
1613 
1614     return result;
1615 #else
1616     return esock_make_error(env, esock_atom_enotsup);
1617 #endif
1618 }
1619 
1620 
1621 
1622 #if !defined(__WIN32__) && defined(HAVE_IF_NAMETOINDEX)
1623 static
1624 ERL_NIF_TERM enet_if_name2index(ErlNifEnv* env,
1625                             char*      ifn)
1626 {
1627     unsigned int idx;
1628 
1629     NDBG( ("NET", "enet_if_name2index -> entry with ifn: %s\r\n", ifn) );
1630 
1631     idx = if_nametoindex(ifn);
1632 
1633     NDBG( ("NET", "enet_if_name2index -> idx: %d\r\n", idx) );
1634 
1635     if (idx == 0) {
1636         int save_errno = get_errno();
1637         NDBG( ("NET", "nif_name2index -> failed: %d\r\n", save_errno) );
1638         return esock_make_error_errno(env, save_errno);
1639     } else {
1640          return esock_make_ok2(env, MKI(env, idx));
1641     }
1642 
1643 }
1644 #endif
1645 
1646 
1647 
1648 /* ----------------------------------------------------------------------
1649  * nif_if_index2name
1650  *
1651  * Description:
1652  * Perform a Interface Index to Interface Name translation.
1653  *
1654  * Arguments:
1655  * Idx - Interface index to be translated.
1656  */
1657 
1658 static
1659 ERL_NIF_TERM nif_if_index2name(ErlNifEnv*         env,
1660                                int                argc,
1661                                const ERL_NIF_TERM argv[])
1662 {
1663 #if defined(__WIN32__)
1664     return enif_raise_exception(env, MKA(env, "notsup"));
1665 #elif defined(HAVE_IF_INDEXTONAME)
1666     ERL_NIF_TERM result;
1667     unsigned int idx;
1668 
1669     NDBG( ("NET", "nif_if_index2name -> entry (%d)\r\n", argc) );
1670 
1671     if ((argc != 1) ||
1672         !GET_UINT(env, argv[0], &idx)) {
1673         return enif_make_badarg(env);
1674     }
1675 
1676     NDBG( ("NET", "nif_index2name -> "
1677            "\r\n   Idx: %T"
1678            "\r\n", argv[0]) );
1679 
1680     result = enet_if_index2name(env, idx);
1681 
1682     NDBG( ("NET", "nif_if_index2name -> done when result: %T\r\n", result) );
1683 
1684     return result;
1685 #else
1686     return esock_make_error(env, esock_atom_enotsup);
1687 #endif
1688 }
1689 
1690 
1691 
1692 #if !defined(__WIN32__) && defined(HAVE_IF_INDEXTONAME)
1693 static
1694 ERL_NIF_TERM enet_if_index2name(ErlNifEnv*   env,
1695                             unsigned int idx)
1696 {
1697     ERL_NIF_TERM result;
1698     char*        ifn = MALLOC(IF_NAMESIZE+1);
1699 
1700     if (ifn == NULL)
1701         return enif_make_badarg(env); // PLACEHOLDER
1702 
1703     if (NULL != if_indextoname(idx, ifn)) {
1704         result = esock_make_ok2(env, MKS(env, ifn));
1705     } else {
1706         result = esock_make_error(env, atom_enxio);
1707     }
1708 
1709     FREE(ifn);
1710 
1711     return result;
1712 }
1713 #endif
1714 
1715 
1716 
1717 /* ----------------------------------------------------------------------
1718  * nif_if_names
1719  *
1720  * Description:
1721  * Get network interface names and indexes.
1722  *
1723  */
1724 
1725 static
1726 ERL_NIF_TERM nif_if_names(ErlNifEnv*         env,
1727                           int                argc,
1728                           const ERL_NIF_TERM argv[])
1729 {
1730 #if defined(__WIN32__) || (defined(__ANDROID__) && (__ANDROID_API__ < 24))
1731     return enif_raise_exception(env, MKA(env, "notsup"));
1732 #elif defined(HAVE_IF_NAMEINDEX) && defined(HAVE_IF_FREENAMEINDEX)
1733     ERL_NIF_TERM result;
1734 
1735     NDBG( ("NET", "nif_if_names -> entry (%d)\r\n", argc) );
1736 
1737     if (argc != 0) {
1738         return enif_make_badarg(env);
1739     }
1740 
1741     result = enet_if_names(env);
1742 
1743     NDBG( ("NET", "nif_if_names -> done when result: %T\r\n", result) );
1744 
1745     return result;
1746 #else
1747     return esock_make_error(env, esock_atom_enotsup);
1748 #endif
1749 }
1750 
1751 
1752 
1753 /* if_nameindex and if_freenameindex were added in Android 7.0 Nougat. With
1754 the Android NDK Unified Headers, check that the build is targeting at least
1755 the corresponding API level 24. */
1756 /* Can we replace the ANDROID tests with the HAVE_... ? */
1757 #if !defined(__WIN32__) && !(defined(__ANDROID__) && (__ANDROID_API__ < 24))
1758 #if defined(HAVE_IF_NAMEINDEX) && defined(HAVE_IF_FREENAMEINDEX)
1759 static
1760 ERL_NIF_TERM enet_if_names(ErlNifEnv* env)
1761 {
1762     ERL_NIF_TERM         result;
1763     struct if_nameindex* ifs = if_nameindex();
1764 
1765     NDBG( ("NET", "enet_if_names -> ifs: 0x%lX\r\n", ifs) );
1766 
1767     if (ifs == NULL) {
1768         result = esock_make_error_errno(env, get_errno());
1769     } else {
1770         /*
1771          * We got some interfaces:
1772          * 1) Calculate how many - the only way is to iterate through the list
1773          *    until its end (which is indicated by an entry with index = zero
1774          *    and if_name = NULL).
1775          * 2) Allocate an ERL_NIF_TERM array of the calculated length.
1776          * 3) Iterate through the array of interfaces and for each create
1777          *    a two tuple: {Idx, If}
1778          *
1779          * Or shall we instead build a list in reverse order and then when
1780          * its done, reverse that? Check
1781          */
1782         unsigned int len = enet_if_names_length(ifs);
1783 
1784         NDBG( ("NET", "enet_if_names -> len: %d\r\n", len) );
1785 
1786         if (len > 0) {
1787             ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM));
1788             unsigned int  i;
1789 
1790             for (i = 0; i < len; i++) {
1791                 array[i] = MKT2(env,
1792                                 MKI(env, ifs[i].if_index),
1793                                 MKS(env, ifs[i].if_name));
1794             }
1795 
1796             result = esock_make_ok2(env, MKLA(env, array, len));
1797             FREE(array);
1798         } else {
1799             result = esock_make_ok2(env, enif_make_list(env, 0));
1800         }
1801     }
1802 
1803     if (ifs != NULL)
1804         if_freenameindex(ifs);
1805 
1806     return result;
1807 }
1808 
1809 
1810 static
1811 unsigned int enet_if_names_length(struct if_nameindex* p)
1812 {
1813     unsigned int len = 0;
1814     BOOLEAN_T    done =  FALSE;
1815 
1816     while (!done) {
1817 
1818         NDBG( ("NET", "enet_if_names_length -> %d: "
1819                "\r\n   if_index: %d"
1820                "\r\n   if_name:  0x%lX"
1821                "\r\n", len, p[len].if_index, p[len].if_name) );
1822 
1823         if ((p[len].if_index == 0) && (p[len].if_name == NULL))
1824             done = TRUE;
1825         else
1826             len++;
1827     }
1828 
1829     return len;
1830 }
1831 #endif // if defined(HAVE_IF_NAMEINDEX) && defined(HAVE_IF_FREENAMEINDEX)
1832 #endif // if !defined(__WIN32__) && ...
1833 
1834 
1835 
1836 /* ----------------------------------------------------------------------
1837  *  U t i l i t y   F u n c t i o n s
1838  * ----------------------------------------------------------------------
1839  */
1840 
1841 /* The erlang format for a set of flags is a list of atoms.
1842  * A special case is when there is no flags, which is
1843  * represented by the atom undefined.
1844  */
1845 #if !defined(__WIN32__)
1846 static
1847 BOOLEAN_T decode_nameinfo_flags(ErlNifEnv*         env,
1848                                 const ERL_NIF_TERM eflags,
1849                                 int*               flags)
1850 {
1851     BOOLEAN_T result;
1852 
1853     if (IS_ATOM(env, eflags)) {
1854         NDBG( ("NET", "decode_nameinfo_flags -> is atom (%T)\r\n", eflags) );
1855         if (COMPARE(eflags, esock_atom_undefined) == 0) {
1856             *flags = 0;
1857             result = TRUE;
1858         } else {
1859             result = FALSE;
1860         }
1861     } else if (IS_LIST(env, eflags)) {
1862         NDBG( ("NET", "decode_nameinfo_flags -> is list\r\n") );
1863         result = decode_nameinfo_flags_list(env, eflags, flags);
1864     } else {
1865         result = FALSE;
1866     }
1867 
1868     NDBG( ("NET", "decode_nameinfo_flags -> result: %s\r\n", B2S(result)) );
1869 
1870     return result;
1871 }
1872 
1873 
1874 static
1875 BOOLEAN_T decode_nameinfo_flags_list(ErlNifEnv*         env,
1876                                      const ERL_NIF_TERM eflags,
1877                                      int*               flags)
1878 {
1879     ERL_NIF_TERM elem, tail, list = eflags;
1880     int          tmp = 0;
1881     BOOLEAN_T    done = FALSE;
1882 
1883     while (!done) {
1884 
1885         /*
1886         NDBG( ("NET", "decode_nameinfo_flags_list -> "
1887                "get next (list) element of"
1888                "\r\n   %T\r\n", list) );
1889         */
1890 
1891         if (GET_LIST_ELEM(env, list, &elem, &tail)) {
1892 
1893             /*
1894             NDBG( ("NET", "decode_nameinfo_flags_list -> got: "
1895                    "\r\n   element: %T"
1896                    "\r\n   tail:    %T"
1897                    "\r\n", elem, tail) );
1898             */
1899 
1900             if (COMPARE(elem, atom_namereqd) == 0) {
1901                 tmp |= NI_NAMEREQD;
1902             } else if (COMPARE(elem, esock_atom_dgram) == 0) {
1903                 tmp |= NI_DGRAM;
1904             } else if (COMPARE(elem, atom_nofqdn) == 0) {
1905                 tmp |= NI_NOFQDN;
1906             } else if (COMPARE(elem, atom_numerichost) == 0) {
1907                 tmp |= NI_NUMERICHOST;
1908             } else if (COMPARE(elem, atom_numericserv) == 0) {
1909                 tmp |= NI_NUMERICSERV;
1910 
1911                 /* Starting with glibc 2.3.4: */
1912 
1913 #if defined(NI_IDN)
1914             } else if (COMPARE(elem, atom_idn) == 0) {
1915                 tmp |= NI_IDN;
1916 #endif
1917 
1918                 /*
1919                  * In later versions of gcc these have been deprecated.
1920                  * That is, they results in compiler warnings.
1921                  * And since we "don't like that", the simplest way
1922                  * to deal with this is to remove the use of them.
1923                  * We leave them here commented out as an example.
1924 
1925 #if defined(NI_IDN_ALLOW_UNASSIGNED)
1926             } else if (COMPARE(elem, atom_idna_allow_unassigned) == 0) {
1927                 tmp |= NI_IDN_ALLOW_UNASSIGNED;
1928 #endif
1929 
1930 #if defined(NI_IDN_USE_STD3_ASCII_RULES)
1931             } else if (COMPARE(elem, atom_idna_use_std3_ascii_rules) == 0) {
1932                 tmp |= NI_IDN_USE_STD3_ASCII_RULES;
1933 #endif
1934 
1935                 */
1936 
1937             } else {
1938 
1939                 NDBG( ("NET", "decode_nameinfo_flags_list -> "
1940                        "invalid flag: %T\r\n", elem) );
1941 
1942                 return FALSE;
1943             }
1944 
1945             list = tail;
1946 
1947         } else {
1948             done = TRUE;
1949         }
1950     }
1951 
1952     *flags = tmp;
1953 
1954     return TRUE;
1955 }
1956 
1957 
1958 
1959 /* Decode the address info string (hostname or service name)
1960  * The string is either the atom undefined or an actual string.
1961  */
1962 static
1963 BOOLEAN_T decode_addrinfo_string(ErlNifEnv*         env,
1964                                  const ERL_NIF_TERM eString,
1965                                  char**             stringP)
1966 {
1967     BOOLEAN_T result;
1968 
1969     if (IS_ATOM(env, eString)) {
1970 
1971         if (COMPARE(eString, esock_atom_undefined) == 0) {
1972             *stringP = NULL;
1973             result   = TRUE;
1974         } else {
1975             *stringP = NULL;
1976             result   = FALSE;
1977         }
1978 
1979     } else {
1980 
1981         result = esock_decode_string(env, eString, stringP);
1982 
1983     }
1984 
1985     return result;
1986 
1987 }
1988 
1989 
1990 
1991 static
1992 ERL_NIF_TERM decode_bool(ErlNifEnv*   env,
1993                          ERL_NIF_TERM ebool,
1994                          BOOLEAN_T*   ibool)
1995 {
1996     if (COMPARE(ebool, esock_atom_true) == 0) {
1997         *ibool = TRUE;
1998         return esock_atom_ok;
1999     } else if (COMPARE(ebool, esock_atom_false) == 0) {
2000         *ibool = FALSE;
2001         return esock_atom_ok;
2002     } else {
2003         return esock_make_error(env, esock_atom_einval);
2004     }
2005 }
2006 
2007 
2008 
2009 /* Encode the address info
2010  * The address info is a linked list och address info, which
2011  * will result in the result being a list of zero or more length.
2012  */
2013 static
2014 ERL_NIF_TERM encode_address_infos(ErlNifEnv*       env,
2015                                   struct addrinfo* addrInfo)
2016 {
2017     ERL_NIF_TERM result;
2018     unsigned int len = address_info_length(addrInfo);
2019 
2020     NDBG( ("NET", "encode_address_infos -> len: %d\r\n", len) );
2021 
2022     if (len > 0) {
2023         ERL_NIF_TERM*    array = MALLOC(len * sizeof(ERL_NIF_TERM));
2024         unsigned int     i     = 0;
2025         struct addrinfo* p     = addrInfo;
2026 
2027         while (i < len) {
2028             array[i] = encode_address_info(env, p);
2029             p = p->ai_next;
2030             i++;
2031         }
2032 
2033         result = MKLA(env, array, len);
2034         FREE(array);
2035     } else {
2036         result = MKEL(env);
2037     }
2038 
2039     NDBG( ("NET", "encode_address_infos -> result: "
2040            "\r\n   %T\r\n", result) );
2041 
2042     return result;
2043 }
2044 
2045 
2046 
2047 /* Calculate the length of the adress info linked list
2048  * The list is NULL-terminated, so the only way is to
2049  * iterate through the list until we find next = NULL.
2050  */
2051 static
2052 unsigned int address_info_length(struct addrinfo* addrInfoP)
2053 {
2054     unsigned int     len = 1;
2055     struct addrinfo* tmp;
2056     BOOLEAN_T        done = FALSE;
2057 
2058     tmp = addrInfoP;
2059 
2060     while (!done) {
2061         if (tmp->ai_next != NULL) {
2062             len++;
2063             tmp = tmp->ai_next;
2064         } else {
2065             done = TRUE;
2066         }
2067     }
2068 
2069     return len;
2070 }
2071 
2072 
2073 
2074 /* Create one (erlang) instance of the address info record
2075  * Should we have address info as a record or as a map?
2076  *
2077  * {address_info, Fam, Type, Proto, Addr}
2078  */
2079 static
2080 ERL_NIF_TERM encode_address_info(ErlNifEnv*       env,
2081                                  struct addrinfo* addrInfoP)
2082 {
2083     ERL_NIF_TERM fam, type, proto, addr, addrInfo;
2084 
2085     fam   = encode_address_info_family(env, addrInfoP->ai_family);
2086     type  = encode_address_info_type(env,   addrInfoP->ai_socktype);
2087     proto = encode_address_info_proto(env,  addrInfoP->ai_protocol);
2088     esock_encode_sockaddr(env,
2089                           (ESockAddress*) addrInfoP->ai_addr,
2090                           addrInfoP->ai_addrlen,
2091                           &addr);
2092 
2093     if (make_address_info(env, fam, type, proto, addr, &addrInfo) == NULL)
2094         return addrInfo;
2095     else
2096         return esock_atom_undefined; // We should do better...
2097 
2098 }
2099 
2100 
2101 /* Convert an "native" family to an erlang family (=domain).
2102  * Note that this is not currently exhaustive, but only supports
2103  * inet and inet6. Other values will be returned as is, that is
2104  * in the form of an integer.
2105  */
2106 static
2107 ERL_NIF_TERM encode_address_info_family(ErlNifEnv* env,
2108                                         int        family)
2109 {
2110     ERL_NIF_TERM efam;
2111 
2112     if (NULL != esock_encode_domain(env, family, &efam))
2113         efam = MKI(env, family);
2114 
2115     return efam;
2116 }
2117 
2118 
2119 
2120 /* Convert an "native" socket type to an erlang socket type.
2121  * Note that this is not currently exhaustive, but only supports
2122  * stream and dgram. Other values will be returned as is, that is
2123  * in the form of an integer.
2124  */
2125 static
2126 ERL_NIF_TERM encode_address_info_type(ErlNifEnv* env,
2127                                       int        socktype)
2128 {
2129     ERL_NIF_TERM etype;
2130 
2131     if (NULL != esock_encode_type(env, socktype, &etype))
2132         etype = MKI(env, socktype);
2133 
2134     return etype;
2135 }
2136 
2137 
2138 
2139 /* Convert an "native" protocol to an erlang protocol.
2140  * Note that this is not currently exhaustive, but only supports
2141  * tcp and udp. Other values will be returned as is, that is
2142  * in the form of an integer.
2143  */
2144 static
2145 ERL_NIF_TERM encode_address_info_proto(ErlNifEnv* env,
2146                                        int        proto)
2147 {
2148     ERL_NIF_TERM eproto;
2149 
2150     if (NULL != esock_encode_protocol(env, proto, &eproto))
2151         eproto = MKI(env, proto);
2152 
2153     return eproto;
2154 }
2155 
2156 
2157 
2158 static
2159 char* make_address_info(ErlNifEnv*    env,
2160                         ERL_NIF_TERM  fam,
2161                         ERL_NIF_TERM  sockType,
2162                         ERL_NIF_TERM  proto,
2163                         ERL_NIF_TERM  addr,
2164                         ERL_NIF_TERM* ai)
2165 {
2166     ERL_NIF_TERM keys[] = {esock_atom_family,
2167                            esock_atom_type,
2168                            esock_atom_protocol,
2169                            esock_atom_addr};
2170     ERL_NIF_TERM vals[] = {fam, sockType, proto, addr};
2171     unsigned int numKeys = sizeof(keys) / sizeof(ERL_NIF_TERM);
2172     unsigned int numVals = sizeof(vals) / sizeof(ERL_NIF_TERM);
2173 
2174     ESOCK_ASSERT( (numKeys == numVals) );
2175 
2176     if (!MKMA(env, keys, vals, numKeys, ai)) {
2177         *ai = esock_atom_undefined;
2178         return ESOCK_STR_EINVAL;
2179     } else {
2180         return NULL;
2181     }
2182 }
2183 #endif // if !defined(__WIN32__)
2184 
2185 
2186 
2187 
2188 
2189 #ifdef HAVE_SETNS
2190 /* We should really have another API, so that we can return errno... */
2191 
2192 /* *** change network namespace ***
2193  * Retreive the current namespace and set the new.
2194  * Return result and previous namespace if successfull.
2195  */
2196 #if !defined(__WIN32__)
2197 static
2198 BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err)
2199 {
2200     int save_errno;
2201     int current_ns = 0;
2202     int new_ns     = 0;
2203 
2204     NDBG( ("NET", "change_network_namespace -> entry with"
2205             "\r\n   new ns: %s", netns) );
2206 
2207     if (netns != NULL) {
2208         current_ns = open("/proc/self/ns/net", O_RDONLY);
2209         if (current_ns == -1) {
2210             *cns = current_ns;
2211             *err = get_errno();
2212             return FALSE;
2213         }
2214         new_ns = open(netns, O_RDONLY);
2215         if (new_ns == -1) {
2216             save_errno = get_errno();
2217             while (close(current_ns) == -1 &&
2218                    get_errno() == EINTR);
2219             *cns = -1;
2220             *err = save_errno;
2221             return FALSE;
2222         }
2223         if (setns(new_ns, CLONE_NEWNET) != 0) {
2224             save_errno = get_errno();
2225             while ((close(new_ns) == -1) &&
2226                    (get_errno() == EINTR));
2227             while ((close(current_ns) == -1) &&
2228                    (get_errno() == EINTR));
2229             *cns = -1;
2230             *err = save_errno;
2231             return FALSE;
2232         } else {
2233             while ((close(new_ns) == -1) &&
2234                    (get_errno() == EINTR));
2235             *cns = current_ns;
2236             *err = 0;
2237             return TRUE;
2238         }
2239     } else {
2240         *cns = -1;
2241         *err = 0;
2242         return TRUE;
2243     }
2244 }
2245 
2246 
2247 /* *** restore network namespace ***
2248  * Restore the previous namespace (see above).
2249  */
2250 static
2251 BOOLEAN_T restore_network_namespace(int ns, int* err)
2252 {
2253     int save_errno;
2254 
2255     NDBG( ("NET", "restore_network_namespace -> entry with"
2256             "\r\n   ns: %d", ns) );
2257 
2258     if (ns != -1) {
2259         if (setns(ns, CLONE_NEWNET) != 0) {
2260             /* XXX Failed to restore network namespace.
2261              * What to do? Tidy up and return an error...
2262              * Note that the thread now might still be in the namespace.
2263              * Can this even happen? Should the emulator be aborted?
2264              */
2265             save_errno = get_errno();
2266             while (close(ns) == -1 &&
2267                    get_errno() == EINTR);
2268             *err = save_errno;
2269             return FALSE;
2270         } else {
2271             while (close(ns) == -1 &&
2272                    get_errno() == EINTR);
2273             *err = 0;
2274             return TRUE;
2275         }
2276   }
2277 
2278   *err = 0;
2279   return TRUE;
2280 }
2281 #endif // if !defined(__WIN32__)
2282 #endif // ifdef HAVE_SETNS
2283 
2284 
2285 
2286 
2287 /* ----------------------------------------------------------------------
2288  *  C a l l b a c k   F u n c t i o n s
2289  * ----------------------------------------------------------------------
2290  */
2291 
2292 /* =========================================================================
2293  * net_dtor - Callback function for resource destructor
2294  *
2295  */
2296 /*
2297 static
2298 void net_dtor(ErlNifEnv* env, void* obj)
2299 {
2300 }
2301 */
2302 
2303 
2304 /* =========================================================================
2305  * net_stop - Callback function for resource stop
2306  *
2307  */
2308 /*
2309 static
2310 void net_stop(ErlNifEnv* env, void* obj, int fd, int is_direct_call)
2311 {
2312 }
2313 */
2314 
2315 
2316 
2317 
2318 /* =========================================================================
2319  * net_down - Callback function for resource down (monitored processes)
2320  *
2321  */
2322 /*
2323 static
2324 void net_down(ErlNifEnv*           env,
2325               void*                obj,
2326               const ErlNifPid*     pid,
2327               const ErlNifMonitor* mon)
2328 {
2329 }
2330 */
2331 
2332 
2333 
2334 /* ----------------------------------------------------------------------
2335  *  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
2336  * ----------------------------------------------------------------------
2337  */
2338 
2339 static
2340 ErlNifFunc net_funcs[] =
2341 {
2342     // Some utility functions
2343     {"nif_info",      0, nif_info,      0},
2344     {"nif_command",   1, nif_command,   0}, // Shall we let this be dirty?
2345 
2346     /* get/set hostname */
2347     {"nif_gethostname",         0, nif_gethostname,   0},
2348 
2349     /* address and name translation in protocol-independent manner */
2350     {"nif_getnameinfo",         2, nif_getnameinfo,   0},
2351     {"nif_getaddrinfo",         3, nif_getaddrinfo,   0},
2352 
2353     {"nif_getifaddrs",          1, nif_getifaddrs,    ERL_NIF_DIRTY_JOB_IO_BOUND},
2354 
2355     /* Network interface (name and/or index) functions */
2356     {"nif_if_name2index",       1, nif_if_name2index, 0},
2357     {"nif_if_index2name",       1, nif_if_index2name, 0},
2358     {"nif_if_names",            0, nif_if_names,      0}
2359 };
2360 
2361 
2362 #if !defined(__WIN32__)
2363 static
2364 BOOLEAN_T extract_debug(ErlNifEnv*   env,
2365                         ERL_NIF_TERM map)
2366 {
2367     /*
2368      * We need to do this here since the "proper" atom has not been
2369      * created when this function is called.
2370      */
2371     ERL_NIF_TERM debug = MKA(env, "debug");
2372 
2373     return esock_extract_bool_from_map(env, map, debug, NET_NIF_DEBUG_DEFAULT);
2374 }
2375 #endif
2376 
2377 
2378 /* =======================================================================
2379  * load_info - A map of misc info (e.g global debug)
2380  */
2381 
2382 static
2383 int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
2384 {
2385 #if !defined(__WIN32__)
2386     // We should make it possible to use load_info to get default values
2387     data.debug = extract_debug(env, load_info);
2388 
2389     NDBG( ("NET", "on_load -> entry\r\n") );
2390 #endif
2391 
2392 #define LOCAL_ATOM_DECL(A) atom_##A = MKA(env, #A)
2393 LOCAL_ATOMS
2394 LOCAL_ERROR_REASON_ATOMS
2395 #undef LOCAL_ATOM_DECL
2396 
2397     // For storing "global" things...
2398     // data.env       = enif_alloc_env(); // We should really check
2399     // data.version   = MKA(env, ERTS_VERSION);
2400     // data.buildDate = MKA(env, ERTS_BUILD_DATE);
2401 
2402     net = enif_open_resource_type_x(env,
2403                                     "net",
2404                                     &netInit,
2405                                     ERL_NIF_RT_CREATE,
2406                                     NULL);
2407 
2408 #if !defined(__WIN32__)
2409     NDBG( ("NET", "on_load -> done\r\n") );
2410 #endif
2411 
2412     return !net;
2413 }
2414 
2415 ERL_NIF_INIT(prim_net, net_funcs, on_load, NULL, NULL, NULL)
2416