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, ¤t_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