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