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