1 /*
2  * %CopyrightBegin%
3  *
4  * Copyright Ericsson AB 2018-2021. 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 socket interface
22  *
23  * All of the nif-functions which are part of the API has two parts.
24  * The first function is called 'nif_<something>', e.g. nif_open.
25  * This does the initial validation and argument processing and then
26  * calls the function that does the actual work. This is called
27  * 'esock_<something>'.
28  * ----------------------------------------------------------------------
29  *
30  *
31  * This is just a code snippet in case there is need of extra debugging
32  *
33  * esock_dbg_printf("DEMONP", "[%d] %s: %T\r\n",
34  *                  descP->sock, slogan,
35  *                  esock_make_monitor_term(env, &mon));
36  *
37  */
38 
39 #define STATIC_ERLANG_NIF 1
40 
41 
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45 
46 /* If we HAVE_SCTP_H and Solaris, we need to define the following in
47  * order to get SCTP working:
48  */
49 #if (defined(HAVE_SCTP_H) && defined(__sun) && defined(__SVR4))
50 #define SOLARIS10    1
51 /* WARNING: This is not quite correct, it may also be Solaris 11! */
52 #define _XPG4_2
53 #define __EXTENSIONS__
54 #endif
55 
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <stddef.h>
59 #include <ctype.h>
60 #include <sys/types.h>
61 #include <errno.h>
62 #include <stdint.h>
63 #include <limits.h>
64 
65 #ifdef HAVE_UNISTD_H
66 #include <unistd.h>
67 #endif
68 #ifdef HAVE_SYS_UIO_H
69 #include <sys/uio.h>
70 #endif
71 
72 #ifdef HAVE_NET_IF_DL_H
73 #include <net/if_dl.h>
74 #endif
75 
76 #ifdef HAVE_IFADDRS_H
77 #include <ifaddrs.h>
78 #endif
79 
80 #ifdef HAVE_NETPACKET_PACKET_H
81 #include <netpacket/packet.h>
82 #endif
83 
84 #ifdef HAVE_SENDFILE
85 #if defined(__linux__) || (defined(__sun) && defined(__SVR4))
86     #include <sys/sendfile.h>
87 #elif defined(__FreeBSD__) || defined(__DragonFly__)
88     /* Need to define __BSD_VISIBLE in order to expose prototype
89      * of sendfile in sys/socket.h
90      */
91     #define __BSD_VISIBLE 1
92 #endif
93 #endif
94 
95 #if defined(__APPLE__) && defined(__MACH__) && !defined(__DARWIN__)
96 #define __DARWIN__ 1
97 #endif
98 
99 
100 #ifdef __WIN32__
101 /* ---------------------------------------------------------------------- *
102  *                                                                        *
103  * Start of __WIN32__ section                                             *
104  *                                                                        *
105  * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
106 
107 
108 #define STRNCASECMP               strncasecmp
109 #define INCL_WINSOCK_API_TYPEDEFS 1
110 
111 #ifndef WINDOWS_H_INCLUDES_WINSOCK2_H
112 #include <winsock2.h>
113 #endif
114 #include <windows.h>
115 #include <Ws2tcpip.h>
116 
117 /* Visual studio 2008+: NTDDI_VERSION needs to be set for iphlpapi.h
118  * to define the right structures. It needs to be set to WINXP (or LONGHORN)
119  * for IPV6 to work and it's set lower by default, so we need to change it.
120  */
121 #ifdef HAVE_SDKDDKVER_H
122 #  include <sdkddkver.h>
123 #  ifdef NTDDI_VERSION
124 #    undef NTDDI_VERSION
125 #  endif
126 #  define NTDDI_VERSION NTDDI_WINXP
127 #endif
128 #include <iphlpapi.h>
129 
130 #undef WANT_NONBLOCKING
131 #include "sys.h"
132 
133 
134 /* AND HERE WE MAY HAVE A BUNCH OF DEFINES....SEE INET DRIVER.... */
135 
136 
137 /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *
138  *                                                                        *
139  * End of __WIN32__ section                                               *
140  *                                                                        *
141  * ---------------------------------------------------------------------- */
142 #else /* #ifdef __WIN32__ */
143 /* ---------------------------------------------------------------------- *
144  *                                                                        *
145  * Start of non-__WIN32__ section a.k.a UNIX section                      *
146  *                                                                        *
147  * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
148 
149 
150 #include <sys/time.h>
151 #ifdef NETDB_H_NEEDS_IN_H
152 #include <netinet/in.h>
153 #endif
154 #include <netdb.h>
155 
156 #include <sys/socket.h>
157 #include <netinet/in.h>
158 
159 #ifdef DEF_INADDR_LOOPBACK_IN_RPC_TYPES_H
160 #include <rpc/types.h>
161 #endif
162 
163 #include <netinet/ip.h>
164 #include <netinet/tcp.h>
165 #include <netinet/udp.h>
166 #include <arpa/inet.h>
167 
168 #include <sys/param.h>
169 #ifdef HAVE_ARPA_NAMESER_H
170 #include <arpa/nameser.h>
171 #endif
172 
173 #ifdef HAVE_SYS_SOCKIO_H
174 #include <sys/sockio.h>
175 #endif
176 
177 #ifdef HAVE_SYS_IOCTL_H
178 #include <sys/ioctl.h>
179 #endif
180 
181 #include <net/if.h>
182 
183 #ifdef HAVE_SCHED_H
184 #include <sched.h>
185 #endif
186 
187 #ifdef HAVE_SETNS_H
188 #include <setns.h>
189 #endif
190 #ifdef HAVE_LINUX_ERRQUEUE_H
191 #include <linux/types.h>     /* On some (I assume) "old" linux,    *
192                               * for example SLES 10 SP1, this is   *
193                               * not (explicitly) included by the   *
194                               * errqueue file. And for some reason *
195                               * configure does not detect this.    *
196                               * So, to simplify, we include here.  */
197 #include <linux/errqueue.h>
198 #include <linux/icmp.h>
199 #include <linux/icmpv6.h>
200 #endif
201 
202 #define HAVE_UDP
203 
204 /* SCTP support -- currently for UNIX platforms only: */
205 #undef HAVE_SCTP
206 #if defined(HAVE_SCTP_H)
207 
208 #include <netinet/sctp.h>
209 
210 /* SCTP Socket API Draft from version 11 on specifies that netinet/sctp.h must
211    explicitly define HAVE_SCTP in case when SCTP is supported,  but Solaris 10
212    still apparently uses Draft 10, and does not define that symbol, so we have
213    to define it explicitly:
214 */
215 #ifndef     HAVE_SCTP
216 #    define HAVE_SCTP
217 #endif
218 
219 /* These changed in draft 11, so SOLARIS10 uses the old MSG_* */
220 #if ! HAVE_DECL_SCTP_UNORDERED
221 #     define    SCTP_UNORDERED  MSG_UNORDERED
222 #endif
223 #if ! HAVE_DECL_SCTP_ADDR_OVER
224 #     define    SCTP_ADDR_OVER  MSG_ADDR_OVER
225 #endif
226 #if ! HAVE_DECL_SCTP_ABORT
227 #     define    SCTP_ABORT      MSG_ABORT
228 #endif
229 #if ! HAVE_DECL_SCTP_EOF
230 #     define    SCTP_EOF        MSG_EOF
231 #endif
232 
233 /* More Solaris 10 fixes: */
234 #if ! HAVE_DECL_SCTP_CLOSED && HAVE_DECL_SCTPS_IDLE
235 #    define SCTP_CLOSED SCTPS_IDLE
236 #    undef HAVE_DECL_SCTP_CLOSED
237 #    define HAVE_DECL_SCTP_CLOSED 1
238 #endif
239 #if ! HAVE_DECL_SCTP_BOUND && HAVE_DECL_SCTPS_BOUND
240 #    define SCTP_BOUND SCTPS_BOUND
241 #    undef HAVE_DECL_SCTP_BOUND
242 #    define HAVE_DECL_SCTP_BOUND 1
243 #endif
244 #if ! HAVE_DECL_SCTP_LISTEN && HAVE_DECL_SCTPS_LISTEN
245 #    define SCTP_LISTEN SCTPS_LISTEN
246 #    undef HAVE_DECL_SCTP_LISTEN
247 #    define HAVE_DECL_SCTP_LISTEN 1
248 #endif
249 #if ! HAVE_DECL_SCTP_COOKIE_WAIT && HAVE_DECL_SCTPS_COOKIE_WAIT
250 #    define SCTP_COOKIE_WAIT SCTPS_COOKIE_WAIT
251 #    undef HAVE_DECL_SCTP_COOKIE_WAIT
252 #    define HAVE_DECL_SCTP_COOKIE_WAIT 1
253 #endif
254 #if ! HAVE_DECL_SCTP_COOKIE_ECHOED && HAVE_DECL_SCTPS_COOKIE_ECHOED
255 #    define SCTP_COOKIE_ECHOED SCTPS_COOKIE_ECHOED
256 #    undef HAVE_DECL_SCTP_COOKIE_ECHOED
257 #    define HAVE_DECL_SCTP_COOKIE_ECHOED 1
258 #endif
259 #if ! HAVE_DECL_SCTP_ESTABLISHED && HAVE_DECL_SCTPS_ESTABLISHED
260 #    define SCTP_ESTABLISHED SCTPS_ESTABLISHED
261 #    undef HAVE_DECL_SCTP_ESTABLISHED
262 #    define HAVE_DECL_SCTP_ESTABLISHED 1
263 #endif
264 #if ! HAVE_DECL_SCTP_SHUTDOWN_PENDING && HAVE_DECL_SCTPS_SHUTDOWN_PENDING
265 #    define SCTP_SHUTDOWN_PENDING SCTPS_SHUTDOWN_PENDING
266 #    undef HAVE_DECL_SCTP_SHUTDOWN_PENDING
267 #    define HAVE_DECL_SCTP_SHUTDOWN_PENDING 1
268 #endif
269 #if ! HAVE_DECL_SCTP_SHUTDOWN_SENT && HAVE_DECL_SCTPS_SHUTDOWN_SENT
270 #    define SCTP_SHUTDOWN_SENT SCTPS_SHUTDOWN_SENT
271 #    undef HAVE_DECL_SCTP_SHUTDOWN_SENT
272 #    define HAVE_DECL_SCTP_SHUTDOWN_SENT 1
273 #endif
274 #if ! HAVE_DECL_SCTP_SHUTDOWN_RECEIVED && HAVE_DECL_SCTPS_SHUTDOWN_RECEIVED
275 #    define SCTP_SHUTDOWN_RECEIVED SCTPS_SHUTDOWN_RECEIVED
276 #    undef HAVE_DECL_SCTP_SHUTDOWN_RECEIVED
277 #    define HAVE_DECL_SCTP_SHUTDOWN_RECEIVED 1
278 #endif
279 #if ! HAVE_DECL_SCTP_SHUTDOWN_ACK_SENT && HAVE_DECL_SCTPS_SHUTDOWN_ACK_SENT
280 #    define SCTP_SHUTDOWN_ACK_SENT SCTPS_SHUTDOWN_ACK_SENT
281 #    undef HAVE_DECL_SCTP_SHUTDOWN_ACK_SENT
282 #    define HAVE_DECL_SCTP_SHUTDOWN_ACK_SENT 1
283 #endif
284 /* New spelling in lksctp 2.6.22 or maybe even earlier:
285  *  adaption -> adaptation
286  */
287 #if !defined(SCTP_ADAPTATION_LAYER) && defined (SCTP_ADAPTION_LAYER)
288 #     define SCTP_ADAPTATION_LAYER       SCTP_ADAPTION_LAYER
289 #     define SCTP_ADAPTATION_INDICATION  SCTP_ADAPTION_INDICATION
290 #     define sctp_adaptation_event       sctp_adaption_event
291 #     define sctp_setadaptation          sctp_setadaption
292 #     define sn_adaptation_event         sn_adaption_event
293 #     define sai_adaptation_ind          sai_adaption_ind
294 #     define ssb_adaptation_ind          ssb_adaption_ind
295 #     define sctp_adaptation_layer_event sctp_adaption_layer_event
296 #endif
297 
298 /*
299  * We *may* need this stuff later when we *fully* implement support for SCTP
300  *
301 
302 #if defined(__GNUC__) && defined(HAVE_SCTP_BINDX)
303 static typeof(sctp_bindx) *esock_sctp_bindx = NULL;
304 #else
305 static int (*esock_sctp_bindx)
306 	(int sd, struct sockaddr *addrs, int addrcnt, int flags) = NULL;
307 #endif
308 
309 #if defined(__GNUC__) && defined(HAVE_SCTP_PEELOFF)
310 static typeof(sctp_peeloff) *esock_sctp_peeloff = NULL;
311 #else
312 static int (*esock_sctp_peeloff)
313         (int sd, sctp_assoc_t assoc_id) = NULL;
314 #endif
315 
316 #if defined(__GNUC__) && defined(HAVE_SCTP_GETLADDRS)
317 static typeof(sctp_getladdrs) *esock_sctp_getladdrs = NULL;
318 #else
319 static int (*esock_sctp_getladdrs)
320         (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL;
321 #endif
322 
323 #if defined(__GNUC__) && defined(HAVE_SCTP_FREELADDRS)
324 static typeof(sctp_freeladdrs) *esock_sctp_freeladdrs = NULL;
325 #else
326 static void (*esock_sctp_freeladdrs)(struct sockaddr *addrs) = NULL;
327 #endif
328 
329 #if defined(__GNUC__) && defined(HAVE_SCTP_GETPADDRS)
330 static typeof(sctp_getpaddrs) *esock_sctp_getpaddrs = NULL;
331 #else
332 static int (*esock_sctp_getpaddrs)
333         (int sd, sctp_assoc_t assoc_id, struct sockaddr **ss) = NULL;
334 #endif
335 
336 #if defined(__GNUC__) && defined(HAVE_SCTP_FREEPADDRS)
337 static typeof(sctp_freepaddrs) *esock_sctp_freepaddrs = NULL;
338 #else
339 static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL;
340 #endif
341 
342 */
343 
344 #endif /* #if defined(HAVE_SCTP_H) */
345 
346 
347 #ifndef WANT_NONBLOCKING
348 #define WANT_NONBLOCKING
349 #endif
350 #include "sys.h"
351 
352 /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *
353  *                                                                        *
354  * End of non-__WIN32__ section a.k.a UNIX section                        *
355  *                                                                        *
356  * ---------------------------------------------------------------------- */
357 #endif /* #ifdef __WIN32__  #else */
358 
359 
360 
361 #include <erl_nif.h>
362 
363 #include "socket_dbg.h"
364 #include "socket_tarray.h"
365 #include "socket_int.h"
366 #include "socket_util.h"
367 #include "prim_file_nif_dyncall.h"
368 
369 #if defined(ERTS_INLINE)
370 #  define ESOCK_INLINE ERTS_INLINE
371 #else
372 #  if defined(__GNUC__)
373 #    define ESOCK_INLINE __inline__
374 #  elif defined(__WIN32__)
375 #    define ESOCK_INLINE __inline
376 #  else
377 #    define ESOCK_INLINE
378 #  endif
379 #endif
380 
381 
382 #if defined(SOL_IPV6) || defined(IPPROTO_IPV6)
383 #define HAVE_IPV6
384 #endif
385 
386 /* Debug stuff... */
387 #define ESOCK_GLOBAL_DEBUG_DEFAULT FALSE
388 #define ESOCK_DEBUG_DEFAULT        FALSE
389 
390 /* Counters and stuff (Don't know where to send this stuff anyway) */
391 #define ESOCK_NIF_IOW_DEFAULT FALSE
392 
393 
394 #ifdef __WIN32__
395 
396 //#define INVALID_HANDLE        from Windows header file
397 //typedef void   *HANDLE        from Windows header file
398 //#define INVALID_SOCKET        from Windows header file
399 //typedef void   *SOCKET        from Windows header file
400 #define INVALID_EVENT NULL
401 
402 #else
403 
404 #define INVALID_HANDLE (-1)
405 typedef int HANDLE;
406 #define INVALID_SOCKET (-1)
407 typedef int SOCKET; /* A subset of HANDLE */
408 #define INVALID_EVENT INVALID_HANDLE
409 
410 #endif
411 
412 
413 /* ==============================================================================
414  * The ESOCK_IS_ERROR macro below is used for portability reasons.
415  * While POSIX specifies that errors from socket-related system calls
416  * should be indicated with a -1 return value, some users have experienced
417  * non-Windows OS kernels that return negative values other than -1.
418  * While one can argue that such kernels are technically broken, comparing
419  * against values less than 0 covers their out-of-spec return values without
420  * imposing incorrect semantics on systems that manage to correctly return -1
421  * for errors, thus increasing Erlang's portability.
422  */
423 #ifdef __WIN32__
424 #define ESOCK_IS_ERROR(val) ((val) == INVALID_SOCKET)
425 #else
426 #define ESOCK_IS_ERROR(val) ((val) < 0)
427 #endif
428 
429 
430 /* *** Misc macros and defines *** */
431 
432 /* This macro exist on some (linux) platforms */
433 #if !defined(IPTOS_TOS_MASK)
434 #define IPTOS_TOS_MASK     0x1E
435 #endif
436 #if !defined(IPTOS_TOS)
437 #define IPTOS_TOS(tos)          ((tos)&IPTOS_TOS_MASK)
438 #endif
439 
440 
441 #if defined(TCP_CA_NAME_MAX)
442 #define ESOCK_OPT_TCP_CONGESTION_NAME_MAX TCP_CA_NAME_MAX
443 #else
444 /* This is really excessive, but just in case... */
445 #define ESOCK_OPT_TCP_CONGESTION_NAME_MAX 256
446 #endif
447 
448 
449 #if defined(TCP_CONGESTION) || defined(SO_BINDTODEVICE)
450 #define USE_GETOPT_STR_OPT
451 #define USE_SETOPT_STR_OPT
452 #endif
453 
454 
455 
456 /* *** Socket state defs *** */
457 
458 #define ESOCK_STATE_BOUND        0x0001 /* readState */
459 #define ESOCK_STATE_LISTENING    0x0002 /* readState */
460 #define ESOCK_STATE_ACCEPTING    0x0004 /* readState */
461 #define ESOCK_STATE_CONNECTING   0x0010 /* writeState */
462 #define ESOCK_STATE_CONNECTED    0x0020 /* writeState */
463 
464 /* This is set in either readState or writeState
465  * so it has to be read from both.
466  * Means that the socket has been used in select,
467  * so select_stop is required. */
468 #define ESOCK_STATE_SELECTED     0x0100 /* readState or writeState */
469 
470 /* These are set in both readState and writeState
471  * so they can be read from either. */
472 #define ESOCK_STATE_CLOSING      0x0200 /* readState and writeState */
473 
474 #define ESOCK_STATE_CLOSED       0x0400 /* readState and writeState */
475 //
476 #define ESOCK_STATE_DTOR         0x8000
477 
478 #define IS_CLOSED(st)                           \
479     (((st) & ESOCK_STATE_CLOSED) != 0)
480 
481 #define IS_CLOSING(st)                          \
482     (((st) & ESOCK_STATE_CLOSING) != 0)
483 
484 #define IS_OPEN(st)                                             \
485     (((st) & (ESOCK_STATE_CLOSED | ESOCK_STATE_CLOSING)) == 0)
486 
487 #define IS_SELECTED(d)                                                  \
488     ((((d)->readState | (d)->writeState) & ESOCK_STATE_SELECTED) != 0)
489 
490 
491 #define ESOCK_GET_RESOURCE(ENV, REF, RES) \
492     enif_get_resource((ENV), (REF), esocks, (RES))
493 
494 #define ESOCK_RECV_BUFFER_COUNT_DEFAULT     0
495 #define ESOCK_RECV_BUFFER_SIZE_DEFAULT      8192
496 #define ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024
497 #define ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT 1024
498 
499 #define ESOCK_DESC_PATTERN_CREATED 0x03030303
500 #define ESOCK_DESC_PATTERN_DTOR    0xC0C0C0C0
501 
502 /*
503 typedef union {
504     struct {
505         // 0 = not open, 1 = open
506         unsigned int open:1;
507         // 0 = not conn, 1 = connecting, 2 = connected
508         unsigned int connect:2;
509         // unsigned int connecting:1;
510         // unsigned int connected:1;
511         // 0 = not listen, 1 = listening, 2 = accepting
512         unsigned int listen:2;
513         // unsigned int listening:1;
514         // unsigned int accepting:1;
515         / * Room for more... * /
516     } flags;
517     unsigned int field; // Make it easy to reset all flags...
518 } SocketState;
519 */
520 
521 /*----------------------------------------------------------------------------
522  * Interface constants.
523  *
524  * The set of elements should be the same as for the type
525  * msg_flag() in socket.erl.
526  */
527 
528 static const struct msg_flag {
529     int flag;
530     ERL_NIF_TERM *name;
531 } msg_flags[] = {
532 
533     {
534 #ifdef MSG_CMSG_CLOEXEC
535         MSG_CMSG_CLOEXEC,
536 #else
537         0,
538 #endif
539         &esock_atom_cmsg_cloexec},
540 
541     {
542 #ifdef MSG_CONFIRM
543         MSG_CONFIRM,
544 #else
545         0,
546 #endif
547         &esock_atom_confirm},
548 
549     {
550 #ifdef MSG_CTRUNC
551         MSG_CTRUNC,
552 #else
553         0,
554 #endif
555         &esock_atom_ctrunc},
556 
557     {
558 #ifdef MSG_DONTROUTE
559         MSG_DONTROUTE,
560 #else
561         0,
562 #endif
563         &esock_atom_dontroute},
564 
565     {
566 #ifdef MSG_EOR
567         MSG_EOR,
568 #else
569         0,
570 #endif
571         &esock_atom_eor},
572 
573     {
574 #ifdef MSG_ERRQUEUE
575         MSG_ERRQUEUE,
576 #else
577         0,
578 #endif
579         &esock_atom_errqueue},
580 
581     {
582 #ifdef MSG_MORE
583         MSG_MORE,
584 #else
585         0,
586 #endif
587         &esock_atom_more},
588 
589     {
590 #ifdef MSG_NOSIGNAL
591         MSG_NOSIGNAL,
592 #else
593         0,
594 #endif
595         &esock_atom_nosignal},
596 
597     {
598 #ifdef MSG_OOB
599         MSG_OOB,
600 #else
601         0,
602 #endif
603         &esock_atom_oob},
604 
605     {
606 #ifdef MSG_PEEK
607         MSG_PEEK,
608 #else
609         0,
610 #endif
611         &esock_atom_peek},
612 
613     {
614 #ifdef MSG_TRUNC
615         MSG_TRUNC,
616 #else
617         0,
618 #endif
619         &esock_atom_trunc}
620 };
621 
622 
623 
624 /* level 'otp' options */
625 #define ESOCK_OPT_OTP_DEBUG        1001
626 #define ESOCK_OPT_OTP_IOW          1002
627 #define ESOCK_OPT_OTP_CTRL_PROC    1003
628 #define ESOCK_OPT_OTP_RCVBUF       1004
629 //#define ESOCK_OPT_OTP_SNDBUF       1005
630 #define ESOCK_OPT_OTP_RCVCTRLBUF   1006
631 #define ESOCK_OPT_OTP_SNDCTRLBUF   1007
632 #define ESOCK_OPT_OTP_FD           1008
633 #define ESOCK_OPT_OTP_META         1009
634 #define ESOCK_OPT_OTP_USE_REGISTRY 1010
635 /**/
636 #define ESOCK_OPT_OTP_DOMAIN       1999 // INTERNAL AND ONLY GET
637 #if 0
638 #define ESOCK_OPT_OTP_TYPE         1998 // INTERNAL AND ONLY GET
639 #define ESOCK_OPT_OTP_PROTOCOL     1997 // INTERNAL AND ONLY GET
640 #define ESOCK_OPT_OTP_DTP          1996 // INTERNAL AND ONLY GET
641 #endif
642 
643 
644 /*--------------------------------------------------------------------------*/
645 
646 
647 /* We should *eventually* use this instead of hard-coding the size (to 1) */
648 #define ESOCK_RECVMSG_IOVEC_SZ 1
649 
650 
651 /* =================================================================== *
652  *                                                                     *
653  *                        Various esockmacros                          *
654  *                                                                     *
655  * =================================================================== */
656 
657 /* Global socket debug */
658 #define SGDBG( proto )            ESOCK_DBG_PRINTF( data.dbg , proto )
659 /* Socket specific debug */
660 #define SSDBG( __D__ , proto )    ESOCK_DBG_PRINTF( (__D__)->dbg , proto )
661 #define SSDBG2( __DBG__ , proto ) ESOCK_DBG_PRINTF( (__DBG__) , proto )
662 
663 #define ESOCK_CNT_INC( __E__, __D__, SF, ACNT, CNT, INC)                \
664     do {                                                                \
665         if (cnt_inc((CNT), (INC))) {					\
666 	  esock_send_wrap_msg((__E__), (__D__), (SF), (ACNT));		\
667 	}								\
668     } while (0)
669 
670 
671 /* =================================================================== *
672  *                                                                     *
673  *                    Basic socket operations                          *
674  *                                                                     *
675  * =================================================================== */
676 
677 #ifdef __WIN32__
678 /* ---------------------------------------------------------------------- *
679  *                                                                        *
680  * Start of __WIN32__ section                                             *
681  *                                                                        *
682  * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
683 
684 /* *** Windows macros *** */
685 
686 #define sock_accept(s, addr, len) \
687     make_noninheritable_handle(accept((s), (addr), (len)))
688 #define sock_bind(s, addr, len)        bind((s), (addr), (len))
689 #define sock_close(s)                  closesocket((s))
690 #define sock_close_event(e)            WSACloseEvent(e)
691 #define sock_connect(s, addr, len)     connect((s), (addr), (len))
692 #define sock_create_event(s)           WSACreateEvent()
693 #define sock_errno()                   WSAGetLastError()
694 #define sock_getopt(s,l,o,v,ln)        getsockopt((s),(l),(o),(v),(ln))
695 #define sock_htons(x)                  htons((x))
696 #define sock_htonl(x)                  htonl((x))
697 #define sock_listen(s, b)              listen((s), (b))
698 #define sock_name(s, addr, len)        getsockname((s), (addr), (len))
699 #define sock_ntohs(x)                  ntohs((x))
700 #define sock_open(domain, type, proto)                             \
701     make_noninheritable_handle(socket((domain), (type), (proto)))
702 #define sock_peer(s, addr, len)    getpeername((s), (addr), (len))
703 #define sock_recv(s,buf,len,flag)  recv((s),(buf),(len),(flag))
704 #define sock_recvfrom(s,buf,blen,flag,addr,alen) \
705     recvfrom((s),(buf),(blen),(flag),(addr),(alen))
706 #define sock_send(s,buf,len,flag)      send((s),(buf),(len),(flag))
707 #define sock_sendto(s,buf,blen,flag,addr,alen) \
708     sendto((s),(buf),(blen),(flag),(addr),(alen))
709 #define sock_setopt(s,l,o,v,ln)        setsockopt((s),(l),(o),(v),(ln))
710 #define sock_shutdown(s, how)          shutdown((s), (how))
711 
712 
713 #define SET_BLOCKING(s)            ioctlsocket(s, FIONBIO, &zero_value)
714 #define SET_NONBLOCKING(s)         ioctlsocket(s, FIONBIO, &one_value)
715 static unsigned long zero_value = 0;
716 static unsigned long one_value  = 1;
717 
718 
719 /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *
720  *                                                                        *
721  * End of __WIN32__ section                                               *
722  *                                                                        *
723  * ---------------------------------------------------------------------- */
724 #else /* #ifdef __WIN32__ */
725 /* ---------------------------------------------------------------------- *
726  *                                                                        *
727  * Start of non-__WIN32__ section a.k.a UNIX section                      *
728  *                                                                        *
729  * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
730 
731 
732 #ifdef HAS_ACCEPT4
733 // We have to figure out what the flags are...
734 #define sock_accept(s, addr, len)       accept4((s), (addr), (len), (SOCK_CLOEXEC))
735 #else
736 #define sock_accept(s, addr, len)       accept((s), (addr), (len))
737 #endif
738 #define sock_bind(s, addr, len)         bind((s), (addr), (len))
739 #define sock_close(s)                   close((s))
740 #define sock_close_event(e)             /* do nothing */
741 #define sock_connect(s, addr, len)      connect((s), (addr), (len))
742 #define sock_create_event(s)            (s) /* return file descriptor */
743 #define sock_errno()                    errno
744 #define sock_getopt(s,t,n,v,l)          getsockopt((s),(t),(n),(v),(l))
745 #define sock_htons(x)                   htons((x))
746 #define sock_htonl(x)                   htonl((x))
747 #define sock_listen(s, b)               listen((s), (b))
748 #define sock_name(s, addr, len)         getsockname((s), (addr), (len))
749 #define sock_ntohs(x)                   ntohs((x))
750 #define sock_open(domain, type, proto)  socket((domain), (type), (proto))
751 #define sock_peer(s, addr, len)         getpeername((s), (addr), (len))
752 #define sock_recv(s,buf,len,flag)       recv((s),(buf),(len),(flag))
753 #define sock_recvfrom(s,buf,blen,flag,addr,alen) \
754     recvfrom((s),(buf),(blen),(flag),(addr),(alen))
755 #define sock_recvmsg(s,msghdr,flag)     recvmsg((s),(msghdr),(flag))
756 #define sock_send(s,buf,len,flag)       send((s), (buf), (len), (flag))
757 #define sock_sendmsg(s,msghdr,flag)     sendmsg((s),(msghdr),(flag))
758 #define sock_sendto(s,buf,blen,flag,addr,alen) \
759                 sendto((s),(buf),(blen),(flag),(addr),(alen))
760 #define sock_setopt(s,l,o,v,ln)         setsockopt((s),(l),(o),(v),(ln))
761 #define sock_shutdown(s, how)           shutdown((s), (how))
762 
763 
764 /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *
765  *                                                                        *
766  * End of non-__WIN32__ section a.k.a UNIX section                        *
767  *                                                                        *
768  * ---------------------------------------------------------------------- */
769 #endif /* #ifdef __WIN32__  #else */
770 
771 
772 /* We can use the IPv4 def for this since the beginning
773  * is the same for INET and INET6 */
774 #define which_address_port(sap)		     \
775   ((((sap)->in4.sin_family == AF_INET) ||  \
776     ((sap)->in4.sin_family == AF_INET6)) ? \
777    ((sap)->in4.sin_port) : -1)
778 
779 
780 typedef struct {
781     ErlNifMonitor mon;
782     BOOLEAN_T     isActive;
783 } ESockMonitor;
784 
785 typedef struct {
786     ErlNifPid    pid; // PID of the requesting process
787     ESockMonitor mon; // Monitor to the requesting process
788 
789     /* We need an environment for the copy of the ref we store here.
790      * We will also use this environment for any messages we send
791      * (with the ref in it). Such as the select message (used in the
792      * select call) or the abort message.
793      */
794     ErlNifEnv*   env;
795     ERL_NIF_TERM ref; // The (unique) reference (ID) of the request
796 } ESockRequestor;
797 
798 typedef struct{
799     // Holding the socket level 'otp' option 'meta' term
800     ErlNifEnv* env;
801     ERL_NIF_TERM ref;
802 } ESockMeta;
803 
804 typedef struct esock_request_queue_element {
805     struct esock_request_queue_element* nextP;
806     ESockRequestor                      data;
807 } ESockRequestQueueElement;
808 
809 typedef struct {
810     ESockRequestQueueElement* first;
811     ESockRequestQueueElement* last;
812 } ESockRequestQueue;
813 
814 
815 /*** The point of this is primarily testing ***/
816 /*
817 #if defined(ESOCK_COUNTER_SIZE)
818 #undef ESOCK_COUNTER_SIZE
819 // #define ESOCK_COUNTER_SIZE 16
820 // #define ESOCK_COUNTER_SIZE 24
821 // #define ESOCK_COUNTER_SIZE 32
822 // #define ESOCK_COUNTER_SIZE 48
823 // #define ESOCK_COUNTER_SIZE 64
824 
825 #endif
826 */
827 
828 #if ESOCK_COUNTER_SIZE == 16
829 
830 typedef Uint16                   ESockCounter;
831 #define ESOCK_COUNTER_MAX        ((ESockCounter) 0xFFFF)
832 #define MKCNT(ENV, CNT)          MKUI((ENV), (CNT))
833 #define MKCT(ENV, TAG, CNT)      MKT2((ENV), (TAG), MKCNT((CNT)))
834 #define ESOCK_COUNTER_FORMAT_STR "%u"
835 
836 #elif ESOCK_COUNTER_SIZE == 24
837 
838 typedef Uint32                   ESockCounter;
839 #define ESOCK_COUNTER_MAX        ((ESockCounter) 0xFFFFFF)
840 #define MKCNT(ENV, CNT)          MKUI((ENV), (CNT))
841 #define MKCT(ENV, TAG, CNT)      MKT2((ENV), (TAG), MKCNT((ENV), (CNT)))
842 #define ESOCK_COUNTER_FORMAT_STR "%lu"
843 
844 #elif ESOCK_COUNTER_SIZE == 32
845 
846 typedef Uint32 ESockCounter;
847 #define ESOCK_COUNTER_MAX        (~((ESockCounter) 0))
848 #define MKCNT(ENV, CNT)          MKUI((ENV), (CNT))
849 #define MKCT(ENV, TAG, CNT)      MKT2((ENV), (TAG), MKCNT((ENV), (CNT)))
850 #define ESOCK_COUNTER_FORMAT_STR "%lu"
851 
852 #elif ESOCK_COUNTER_SIZE == 48
853 
854 typedef Uint64                   ESockCounter;
855 #define ESOCK_COUNTER_MAX        ((ESockCounter) 0xFFFFFFFFFFFF)
856 #define MKCNT(ENV, CNT)          MKUI64((ENV), (CNT))
857 #define MKCT(ENV, TAG, CNT)      MKT2((ENV), (TAG), MKCNT((ENV), (CNT)))
858 #define ESOCK_COUNTER_FORMAT_STR "%llu"
859 
860 #elif ESOCK_COUNTER_SIZE == 64
861 
862 typedef Uint64                   ESockCounter;
863 #define ESOCK_COUNTER_MAX        (~((ESockCounter) 0))
864 #define MKCNT(ENV, CNT)          MKUI64((ENV), (CNT))
865 #define MKCT(ENV, TAG, CNT)      MKT2((ENV), (TAG), MKCNT((ENV), (CNT)))
866 #define ESOCK_COUNTER_FORMAT_STR "%llu"
867 
868 #else
869 
870 #error "Invalid counter size"
871 
872 #endif
873 
874 // static const ESockCounter esock_counter_max = ESOCK_COUNTER_MAX;
875 
876 #ifdef HAVE_SENDFILE
877 
878 typedef struct {
879     ESockCounter       cnt;     // Calls to OS sendfile()
880     ESockCounter       byteCnt; // Bytes sent with sendfile
881     ESockCounter       fails;   // Failed sendfile operations
882     ESockCounter       max;     // Largest sendfile operation
883     ESockCounter       maxCnt;  // Counter for ="=
884     ESockCounter       pkg;     // Sendfile chunks
885     ESockCounter       pkgMax;  // Largest sendfile chunk
886     ESockCounter       tries;   // Started sendfile operations
887     ESockCounter       waits;   // Select's during sendfile
888 } ESockSendfileCounters;
889 static ESockSendfileCounters initESockSendfileCounters =
890     {0, 0, 0, 0, 0, 0, 0, 0, 0};
891 
892 #endif
893 
894 
895 typedef struct {
896     /*
897      * +++ This is a way to, possibly, detect memory overrides "and stuff" +++
898      *
899      * We have two patterns. One is set when the descriptor is created
900      * (allocated) and one is set when the descriptor is dtor'ed.
901      */
902     Uint32             pattern;
903 
904     /* +++ Stuff "about" the socket +++ */
905 
906     /* "Constant" - set when socket is created and never changed */
907     int                domain;
908     int                type;
909     int                protocol;
910 
911     /* The state is partly for debugging, decisions are made often
912      * based on other variables.  The state is divided in
913      * a readState half and a writeState half that can be
914      * OR:ed together to create the complete state.
915      * The halves are locked by their corresponding lock.
916      */
917 
918     /* +++ Write stuff +++ */
919     ErlNifMutex*       writeMtx;
920     /**/
921     unsigned int       writeState; // For debugging
922     ESockRequestor     currentWriter;
923     ESockRequestor*    currentWriterP; // NULL or &currentWriter
924     ESockRequestQueue  writersQ;
925     ESockCounter       writePkgCnt;
926     ESockCounter       writePkgMax;
927     ESockCounter       writePkgMaxCnt;
928     ESockCounter       writeByteCnt;
929     ESockCounter       writeTries;
930     ESockCounter       writeWaits;
931     ESockCounter       writeFails;
932 #ifdef HAVE_SENDFILE
933     HANDLE                 sendfileHandle;
934     ESockSendfileCounters* sendfileCountersP;
935 #endif
936     /* +++ Connector +++ */
937     ESockRequestor     connector;
938     ESockRequestor*    connectorP; // NULL or &connector
939     /* +++ Config stuff +++ */
940     size_t             wCtrlSz; // Write control buffer size
941     ESockMeta          meta;    // Level 'otp' option 'meta' term
942 
943     /* +++ Read stuff +++ */
944     ErlNifMutex*       readMtx;
945     /**/
946     unsigned int       readState; // For debugging
947     ESockRequestor     currentReader;
948     ESockRequestor*    currentReaderP; // NULL or &currentReader
949     ESockRequestQueue  readersQ;
950     ErlNifBinary       rbuffer;      // DO WE NEED THIS
951     Uint32             readCapacity; // DO WE NEED THIS
952     ESockCounter       readPkgCnt;
953     ESockCounter       readPkgMax;
954     ESockCounter       readPkgMaxCnt;
955     ESockCounter       readByteCnt;
956     ESockCounter       readTries;
957     ESockCounter       readWaits;
958     ESockCounter       readFails;
959     /* +++ Accept stuff +++ */
960     ESockRequestor     currentAcceptor;
961     ESockRequestor*    currentAcceptorP; // NULL or &currentAcceptor
962     ESockRequestQueue  acceptorsQ;
963     ESockCounter       accSuccess;
964     ESockCounter       accTries;
965     ESockCounter       accWaits;
966     ESockCounter       accFails;
967     /* +++ Config stuff +++ */
968     size_t             rBufSz;  // Read buffer size (when data length = 0)
969     /* rNum and rNumCnt are used (together with rBufSz) when calling the recv
970      * function with the Length argument set to 0 (zero).
971      * If rNum is 0 (zero), then rNumCnt is not used and only *one* read will
972      * be done. Also, when get'ing the value of the option (rcvbuf) with
973      * getopt, the value will be reported as an integer. If the rNum has a
974      * value greater then 0 (zero), then it will instead be reported as {N, BufSz}.
975      */
976     unsigned int       rNum;    // recv: Number of reads using rBufSz
977     unsigned int       rNumCnt; // recv: Current number of reads (so far)
978     size_t             rCtrlSz; // Read control buffer size
979 
980     /* Locked by readMtx and writeMtx combined for writing,
981      * which means only one of them is required for reading
982      */
983     /* +++ Close stuff +++ */
984     ErlNifPid          closerPid;
985     ESockMonitor       closerMon;
986     ErlNifEnv*         closeEnv;
987     ERL_NIF_TERM       closeRef;
988     /* +++ Inform On (counter) Wrap +++ */
989     BOOLEAN_T          iow;
990     /* +++ Controller (owner) process +++ */
991     ErlNifPid          ctrlPid;
992     ESockMonitor       ctrlMon;
993     /* +++ The actual socket +++ */
994     SOCKET             sock;
995     ErlNifEvent        event;
996     SOCKET             origFD; // A 'socket' created from this FD
997     BOOLEAN_T          closeOnClose; // Have we dup'ed or not
998     /* +++ The dbg flag for SSDBG +++ */
999     BOOLEAN_T          dbg;
1000     BOOLEAN_T          useReg;
1001 
1002     /* Lock order: readMtx, writeMtx, cntMtx
1003      */
1004 } ESockDescriptor;
1005 
1006 
1007 /* Global stuff.
1008  */
1009 typedef struct {
1010     /* These are for debugging, testing and the like */
1011     // ERL_NIF_TERM version;
1012     // ERL_NIF_TERM buildDate;
1013 
1014     /* XXX Should be locked but too awkward and small gain */
1015     BOOLEAN_T    dbg;
1016     BOOLEAN_T    useReg;
1017 
1018     /* Registry stuff */
1019     ErlNifPid    regPid; /* Constant - not locked */
1020 
1021     /* IOV_MAX. Constant - not locked */
1022     int          iov_max;
1023 
1024     /* XXX
1025      * Should be locked but too awkward for no gain since it is not used yet
1026      */
1027     BOOLEAN_T    iow; // Where do we send this? Subscription?
1028 
1029     ErlNifMutex* protocolsMtx;
1030 
1031     ErlNifMutex* cntMtx; /* Locks the below */
1032     /* Its extreme overkill to have these counters be 64-bit,
1033      * but since the other counters are, it's much simpler to
1034      * let these be 64-bit also.
1035      */
1036     ESockCounter numSockets;
1037     ESockCounter numTypeStreams;
1038     ESockCounter numTypeDGrams;
1039     ESockCounter numTypeSeqPkgs;
1040     ESockCounter numDomainInet;
1041     ESockCounter numDomainInet6;
1042     ESockCounter numDomainLocal;
1043     ESockCounter numProtoIP;
1044     ESockCounter numProtoTCP;
1045     ESockCounter numProtoUDP;
1046     ESockCounter numProtoSCTP;
1047     //
1048     BOOLEAN_T    sockDbg;
1049 } ESockData;
1050 
1051 
1052 /* ----------------------------------------------------------------------
1053  *  F o r w a r d s
1054  * ----------------------------------------------------------------------
1055  */
1056 
1057 
1058 extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */
1059 
1060 
1061 /* All the nif "callback" functions for the socket API has
1062  * the exact same API:
1063  *
1064  * nif_<funcname>(ErlNifEnv*         env,
1065  *                int                argc,
1066  *                const ERL_NIF_TERM argv[]);
1067  *
1068  * So, to simplify, use some macro magic to define those.
1069  *
1070  * These are the functions making up the "official" API.
1071  * Basically, these functions does some preliminary checks and argument
1072  * extractions and then call the functions called 'n<funcname>', which
1073  * does the actual work. Except for the info function.
1074  *
1075  * nif_info
1076  * nif_command
1077  * nif_supports
1078  * nif_open
1079  * nif_bind
1080  * nif_connect
1081  * nif_listen
1082  * nif_accept
1083  * nif_send
1084  * nif_sendto
1085  * nif_sendmsg
1086  * nif_sendfile
1087  * nif_recv
1088  * nif_recvfrom
1089  * nif_recvmsg
1090  * nif_close
1091  * nif_shutdown
1092  * nif_setopt
1093  * nif_getopt
1094  * nif_sockname
1095  * nif_peername
1096  * nif_finalize_close
1097  * nif_cancel
1098  */
1099 
1100 #define ESOCK_NIF_FUNCS                             \
1101     ESOCK_NIF_FUNC_DEF(info);                       \
1102     ESOCK_NIF_FUNC_DEF(command);                    \
1103     ESOCK_NIF_FUNC_DEF(supports);                   \
1104     ESOCK_NIF_FUNC_DEF(open);                       \
1105     ESOCK_NIF_FUNC_DEF(bind);                       \
1106     ESOCK_NIF_FUNC_DEF(connect);                    \
1107     ESOCK_NIF_FUNC_DEF(listen);                     \
1108     ESOCK_NIF_FUNC_DEF(accept);                     \
1109     ESOCK_NIF_FUNC_DEF(send);                       \
1110     ESOCK_NIF_FUNC_DEF(sendto);                     \
1111     ESOCK_NIF_FUNC_DEF(sendmsg);                    \
1112     ESOCK_NIF_FUNC_DEF(recv);                       \
1113     ESOCK_NIF_FUNC_DEF(recvfrom);                   \
1114     ESOCK_NIF_FUNC_DEF(recvmsg);                    \
1115     ESOCK_NIF_FUNC_DEF(close);                      \
1116     ESOCK_NIF_FUNC_DEF(shutdown);                   \
1117     ESOCK_NIF_FUNC_DEF(setopt);                     \
1118     ESOCK_NIF_FUNC_DEF(getopt);                     \
1119     ESOCK_NIF_FUNC_DEF(sockname);                   \
1120     ESOCK_NIF_FUNC_DEF(peername);                   \
1121     ESOCK_NIF_FUNC_DEF(finalize_close);             \
1122     ESOCK_NIF_FUNC_DEF(cancel);
1123 
1124 #define ESOCK_NIF_FUNC_DEF(F)                              \
1125     static ERL_NIF_TERM nif_##F(ErlNifEnv*         env,    \
1126                                 int                argc,   \
1127                                 const ERL_NIF_TERM argv[]);
1128 ESOCK_NIF_FUNCS
1129 #undef ESOCK_NIF_FUNC_DEF
1130 
1131 
1132 #ifndef __WIN32__
1133 /* ---------------------------------------------------------------------- *
1134  *                                                                        *
1135  *                                                                        *
1136  * Start of non-__WIN32__ section a.k.a UNIX section                      *
1137  *                                                                        *
1138  *                                                                        *
1139  * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */
1140 
1141 /* And here comes the functions that does the actual work (for the most part) */
1142 
1143 static ERL_NIF_TERM esock_command(ErlNifEnv*   env,
1144                                   ERL_NIF_TERM command,
1145                                   ERL_NIF_TERM cdata);
1146 static ERL_NIF_TERM esock_command_debug(ErlNifEnv*   env,
1147                                         ERL_NIF_TERM cdata);
1148 static ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv*   env,
1149                                                ERL_NIF_TERM cdata);
1150 static ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv*   env,
1151                                                       ERL_NIF_TERM cdata);
1152 
1153 static ERL_NIF_TERM esock_global_info(ErlNifEnv* env);
1154 static ERL_NIF_TERM esock_socket_info(ErlNifEnv*       env,
1155                                       ESockDescriptor* descP);
1156 static ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv*       env,
1157                                              ESockDescriptor* descP);
1158 static ERL_NIF_TERM esock_socket_info_type(ErlNifEnv*       env,
1159                                            ESockDescriptor* descP);
1160 static ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv*       env,
1161                                                ESockDescriptor* descP);
1162 static ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv*       env,
1163                                             ESockDescriptor* descP);
1164 static ERL_NIF_TERM esock_socket_info_state(ErlNifEnv*   env,
1165 					    unsigned int state);
1166 #define ESOCK_SOCKET_INFO_REQ_FUNCS              \
1167     ESOCK_SOCKET_INFO_REQ_FUNC_DEF(readers);     \
1168     ESOCK_SOCKET_INFO_REQ_FUNC_DEF(writers);     \
1169     ESOCK_SOCKET_INFO_REQ_FUNC_DEF(acceptors);
1170 
1171 #define ESOCK_SOCKET_INFO_REQ_FUNC_DEF(F)                               \
1172     static ERL_NIF_TERM esock_socket_info_##F(ErlNifEnv*         env,   \
1173                                               ESockDescriptor*   descP);
1174 ESOCK_SOCKET_INFO_REQ_FUNCS
1175 #undef ESOCK_SOCKET_INFO_REQ_FUNC_DEF
1176 
1177 static ERL_NIF_TERM socket_info_reqs(ErlNifEnv*         env,
1178                                      ESockDescriptor*   descP,
1179                                      ESockRequestor*    currentRequestorP,
1180                                      ESockRequestQueue* q);
1181 
1182 static ERL_NIF_TERM esock_supports_0(ErlNifEnv* env);
1183 static ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key);
1184 
1185 static ERL_NIF_TERM esock_supports_msg_flags(ErlNifEnv* env);
1186 static ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env);
1187 static ERL_NIF_TERM esock_supports_options(ErlNifEnv* env);
1188 
1189 static ERL_NIF_TERM esock_open2(ErlNifEnv*   env,
1190                                 int          fd,
1191                                 ERL_NIF_TERM eextra);
1192 static BOOLEAN_T esock_open2_todup(ErlNifEnv*   env,
1193                                    ERL_NIF_TERM eextra);
1194 static BOOLEAN_T esock_open2_get_domain(ErlNifEnv*   env,
1195                                         ERL_NIF_TERM eopts,
1196                                         int*         domain);
1197 static BOOLEAN_T esock_open2_get_type(ErlNifEnv*   env,
1198                                       ERL_NIF_TERM eopt,
1199                                       int*         type);
1200 static ERL_NIF_TERM esock_open4(ErlNifEnv*   env,
1201                                 int          domain,
1202                                 int          type,
1203                                 int          protocol,
1204                                 ERL_NIF_TERM eopts);
1205 static BOOLEAN_T esock_open_is_debug(ErlNifEnv*   env,
1206                                      ERL_NIF_TERM eextra,
1207                                      BOOLEAN_T dflt);
1208 static BOOLEAN_T esock_open_use_registry(ErlNifEnv*   env,
1209                                          ERL_NIF_TERM eextra,
1210                                          BOOLEAN_T dflt);
1211 static BOOLEAN_T esock_open_which_domain(SOCKET sock,   int* domain);
1212 static BOOLEAN_T esock_open_which_type(SOCKET sock,     int* type);
1213 static BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto);
1214 
1215 static ERL_NIF_TERM esock_bind(ErlNifEnv*       env,
1216                                ESockDescriptor* descP,
1217                                ESockAddress*    sockAddrP,
1218                                SOCKLEN_T        addrLen);
1219 static ERL_NIF_TERM esock_connect(ErlNifEnv*       env,
1220                                   ESockDescriptor* descP,
1221                                   ERL_NIF_TERM     sockRef,
1222                                   ERL_NIF_TERM     connRef,
1223                                   ESockAddress*    addrP,
1224                                   SOCKLEN_T        addrLen);
1225 static ERL_NIF_TERM esock_listen(ErlNifEnv*       env,
1226                                  ESockDescriptor* descP,
1227                                  int              backlog);
1228 static ERL_NIF_TERM esock_accept(ErlNifEnv*       env,
1229                                  ESockDescriptor* descP,
1230                                  ERL_NIF_TERM     sockRef,
1231                                  ERL_NIF_TERM     ref);
1232 static ERL_NIF_TERM esock_accept_listening_error(ErlNifEnv*       env,
1233                                                  ESockDescriptor* descP,
1234                                                  ERL_NIF_TERM     sockRef,
1235                                                  ERL_NIF_TERM     accRef,
1236                                                  ErlNifPid        caller,
1237                                                  int              save_errno);
1238 static ERL_NIF_TERM esock_accept_listening_accept(ErlNifEnv*       env,
1239                                                   ESockDescriptor* descP,
1240                                                   ERL_NIF_TERM     sockRef,
1241                                                   SOCKET           accSock,
1242                                                   ErlNifPid        caller);
1243 static ERL_NIF_TERM esock_accept_accepting_current(ErlNifEnv*       env,
1244                                                    ESockDescriptor* descP,
1245                                                    ERL_NIF_TERM     sockRef,
1246                                                    ERL_NIF_TERM     ref);
1247 static ERL_NIF_TERM
1248 esock_accept_accepting_current_accept(ErlNifEnv*       env,
1249                                       ESockDescriptor* descP,
1250                                       ERL_NIF_TERM     sockRef,
1251                                       SOCKET           accSock);
1252 static ERL_NIF_TERM esock_accept_accepting_current_error(ErlNifEnv*       env,
1253                                                          ESockDescriptor* descP,
1254                                                          ERL_NIF_TERM     sockRef,
1255                                                          ERL_NIF_TERM     opRef,
1256                                                          int              save_errno);
1257 static ERL_NIF_TERM esock_accept_accepting_other(ErlNifEnv*       env,
1258 						 ESockDescriptor* descP,
1259 						 ERL_NIF_TERM     ref,
1260 						 ErlNifPid        caller);
1261 static ERL_NIF_TERM esock_accept_busy_retry(ErlNifEnv*       env,
1262                                             ESockDescriptor* descP,
1263                                             ERL_NIF_TERM     sockRef,
1264                                             ERL_NIF_TERM     accRef,
1265                                             ErlNifPid*       pidP);
1266 static BOOLEAN_T esock_accept_accepted(ErlNifEnv*       env,
1267                                        ESockDescriptor* descP,
1268                                        ERL_NIF_TERM     sockRef,
1269                                        SOCKET           accSock,
1270                                        ErlNifPid        pid,
1271                                        ERL_NIF_TERM*    result);
1272 static ERL_NIF_TERM esock_send(ErlNifEnv*       env,
1273                                ESockDescriptor* descP,
1274                                ERL_NIF_TERM     sockRef,
1275                                ERL_NIF_TERM     sendRef,
1276                                ErlNifBinary*    dataP,
1277                                int              flags);
1278 static ERL_NIF_TERM esock_sendto(ErlNifEnv*       env,
1279                                  ESockDescriptor* descP,
1280                                  ERL_NIF_TERM     sockRef,
1281                                  ERL_NIF_TERM     sendRef,
1282                                  ErlNifBinary*    dataP,
1283                                  int              flags,
1284                                  ESockAddress*    toAddrP,
1285                                  SOCKLEN_T        toAddrLen);
1286 static ERL_NIF_TERM esock_sendmsg(ErlNifEnv*       env,
1287                                   ESockDescriptor* descP,
1288                                   ERL_NIF_TERM     sockRef,
1289                                   ERL_NIF_TERM     sendRef,
1290                                   ERL_NIF_TERM     eMsg,
1291                                   int              flags,
1292                                   ERL_NIF_TERM     eIOV);
1293 
1294 #ifdef HAVE_SENDFILE
1295 static ERL_NIF_TERM
1296 esock_sendfile_start(ErlNifEnv       *env,
1297                      ESockDescriptor *descP,
1298                      ERL_NIF_TERM     sockRef,
1299                      ERL_NIF_TERM     sendRef,
1300                      off_t            offset,
1301                      size_t           count,
1302                      ERL_NIF_TERM     fRef);
1303 static ERL_NIF_TERM
1304 esock_sendfile_cont(ErlNifEnv       *env,
1305                     ESockDescriptor *descP,
1306                     ERL_NIF_TERM     sockRef,
1307                     ERL_NIF_TERM     sendRef,
1308                     off_t            offset,
1309                     size_t           count);
1310 static ERL_NIF_TERM
1311 esock_sendfile_deferred_close(ErlNifEnv       *env,
1312                               ESockDescriptor *descP);
1313 static int
1314 esock_sendfile(ErlNifEnv       *env,
1315                ESockDescriptor *descP,
1316                ERL_NIF_TERM     sockRef,
1317                off_t            offset,
1318                size_t          *count,
1319                int             *errP);
1320 static ERL_NIF_TERM
1321 esock_sendfile_error(ErlNifEnv             *env,
1322                      ESockDescriptor       *descP,
1323                      ERL_NIF_TERM           sockRef,
1324                      ERL_NIF_TERM reason);
1325 static ERL_NIF_TERM
1326 esock_sendfile_errno(ErlNifEnv             *env,
1327                      ESockDescriptor       *descP,
1328                      ERL_NIF_TERM           sockRef,
1329                      int                    err);
1330 static ERL_NIF_TERM
1331 esock_sendfile_ok(ErlNifEnv       *env,
1332                   ESockDescriptor *descP,
1333                   ERL_NIF_TERM     sockRef,
1334                   size_t           count);
1335 static ERL_NIF_TERM
1336 esock_sendfile_select(ErlNifEnv       *env,
1337                       ESockDescriptor *descP,
1338                       ERL_NIF_TERM     sockRef,
1339                       ERL_NIF_TERM     sendRef,
1340                       size_t           count);
1341 #endif // #ifdef HAVE_SENDFILE
1342 
1343 static ERL_NIF_TERM esock_recv(ErlNifEnv*       env,
1344                                ESockDescriptor* descP,
1345                                ERL_NIF_TERM     sendRef,
1346                                ERL_NIF_TERM     recvRef,
1347                                ssize_t          len,
1348                                int              flags);
1349 static ERL_NIF_TERM esock_recvfrom(ErlNifEnv*       env,
1350                                    ESockDescriptor* descP,
1351                                    ERL_NIF_TERM     sockRef,
1352                                    ERL_NIF_TERM     recvRef,
1353                                    ssize_t          bufSz,
1354                                    int              flags);
1355 static ERL_NIF_TERM esock_recvmsg(ErlNifEnv*       env,
1356                                   ESockDescriptor* descP,
1357                                   ERL_NIF_TERM     sockRef,
1358                                   ERL_NIF_TERM     recvRef,
1359                                   ssize_t          bufLen,
1360                                   ssize_t          ctrlLen,
1361                                   int              flags);
1362 static ERL_NIF_TERM esock_close(ErlNifEnv*       env,
1363                                 ESockDescriptor* descP);
1364 static BOOLEAN_T esock_do_stop(ErlNifEnv* env,
1365                                ESockDescriptor* descP);
1366 static ERL_NIF_TERM esock_shutdown(ErlNifEnv*       env,
1367                                    ESockDescriptor* descP,
1368                                    int              how);
1369 
1370 
1371 /* Set OTP level options */
1372 static ERL_NIF_TERM esock_setopt_otp(ErlNifEnv*       env,
1373                                      ESockDescriptor* descP,
1374                                      int              eOpt,
1375                                      ERL_NIF_TERM     eVal);
1376 /* *** esock_setopt_otp_debug        ***
1377  * *** esock_setopt_otp_iow          ***
1378  * *** esock_setopt_otp_ctrl_proc    ***
1379  * *** esock_setopt_otp_rcvbuf       ***
1380  * *** esock_setopt_otp_rcvctrlbuf   ***
1381  * *** esock_setopt_otp_sndctrlbuf   ***
1382  * *** esock_setopt_otp_meta         ***
1383  * *** esock_setopt_otp_use_registry ***
1384  */
1385 #define ESOCK_SETOPT_OTP_FUNCS               \
1386     ESOCK_SETOPT_OTP_FUNC_DEF(debug);        \
1387     ESOCK_SETOPT_OTP_FUNC_DEF(iow);          \
1388     ESOCK_SETOPT_OTP_FUNC_DEF(ctrl_proc);    \
1389     ESOCK_SETOPT_OTP_FUNC_DEF(rcvbuf);       \
1390     ESOCK_SETOPT_OTP_FUNC_DEF(rcvctrlbuf);   \
1391     ESOCK_SETOPT_OTP_FUNC_DEF(sndctrlbuf);   \
1392     ESOCK_SETOPT_OTP_FUNC_DEF(meta);	     \
1393     ESOCK_SETOPT_OTP_FUNC_DEF(use_registry);
1394 #define ESOCK_SETOPT_OTP_FUNC_DEF(F)                                 \
1395     static ERL_NIF_TERM esock_setopt_otp_##F(ErlNifEnv*       env,   \
1396                                              ESockDescriptor* descP, \
1397                                              ERL_NIF_TERM     eVal)
1398 ESOCK_SETOPT_OTP_FUNCS
1399 #undef ESOCK_SETOPT_OTP_FUNC_DEF
1400 
1401 /* Set native options */
1402 static ERL_NIF_TERM esock_setopt_native(ErlNifEnv*       env,
1403                                         ESockDescriptor* descP,
1404                                         int              level,
1405                                         int              opt,
1406                                         ERL_NIF_TERM     eVal);
1407 static ERL_NIF_TERM esock_setopt(ErlNifEnv*       env,
1408                                  ESockDescriptor* descP,
1409                                  int              level,
1410                                  int              opt,
1411                                  ERL_NIF_TERM     eVal);
1412 
1413 /* *** Handling set of socket options for level = socket *** */
1414 
1415 #if defined(SO_BINDTODEVICE)
1416 static ERL_NIF_TERM esock_setopt_so_bindtodevice(ErlNifEnv*       env,
1417                                                  ESockDescriptor* descP,
1418                                                  int              level,
1419                                                  int              opt,
1420                                                  ERL_NIF_TERM     eVal);
1421 #endif
1422 
1423 #if defined(SO_LINGER)
1424 static
1425 ERL_NIF_TERM esock_setopt_linger(ErlNifEnv*       env,
1426                                  ESockDescriptor* descP,
1427                                  int              level,
1428                                  int              opt,
1429                                  ERL_NIF_TERM     eVal);
1430 static
1431 ERL_NIF_TERM esock_getopt_linger(ErlNifEnv*       env,
1432                                  ESockDescriptor* descP,
1433                                  int              level,
1434                                  int              opt);
1435 #endif
1436 
1437 #if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
1438 static ERL_NIF_TERM esock_setopt_msfilter(ErlNifEnv*       env,
1439                                           ESockDescriptor* descP,
1440                                           int              level,
1441                                           int              opt,
1442                                           ERL_NIF_TERM     eVal);
1443 static BOOLEAN_T decode_msfilter_mode(ErlNifEnv*   env,
1444                                       ERL_NIF_TERM eVal,
1445                                       Uint32*      mode);
1446 #endif
1447 #if defined(IP_MTU_DISCOVER)
1448 static ERL_NIF_TERM esock_setopt_ip_mtu_discover(ErlNifEnv*       env,
1449                                                  ESockDescriptor* descP,
1450                                                  int              level,
1451                                                  int              opt,
1452                                                  ERL_NIF_TERM     eVal);
1453 #endif
1454 #if defined(IP_MULTICAST_IF)
1455 static ERL_NIF_TERM esock_setopt_multicast_if(ErlNifEnv*       env,
1456                                               ESockDescriptor* descP,
1457                                               int              level,
1458                                               int              opt,
1459                                               ERL_NIF_TERM     eVal);
1460 #endif
1461 
1462 #if defined(IP_TOS)
1463 static ERL_NIF_TERM esock_setopt_tos(ErlNifEnv*       env,
1464                                      ESockDescriptor* descP,
1465                                      int              level,
1466                                      int              opt,
1467                                      ERL_NIF_TERM     eVal);
1468 #endif
1469 
1470 #if defined(IP_DROP_MEMBERSHIP) || defined(IP_ADD_MEMBERSHIP)
1471 static
1472 ERL_NIF_TERM esock_setopt_in_update_membership(ErlNifEnv*       env,
1473                                                ESockDescriptor* descP,
1474                                                int              level,
1475                                                int              opt,
1476                                                ERL_NIF_TERM     eVal);
1477 #endif
1478 #if defined(IP_ADD_SOURCE_MEMBERSHIP) || defined(IP_DROP_SOURCE_MEMBERSHIP) || defined(IP_BLOCK_SOURCE) || defined(IP_UNBLOCK_SOURCE)
1479 static
1480 ERL_NIF_TERM esock_setopt_in_update_source(ErlNifEnv*       env,
1481                                            ESockDescriptor* descP,
1482                                            int              level,
1483                                            int              opt,
1484                                            ERL_NIF_TERM     eVal);
1485 #endif
1486 
1487 
1488 /* *** Handling set of socket options for level = ipv6 *** */
1489 #if defined(HAVE_IPV6)
1490 
1491 #if defined(IPV6_ADDRFORM)
1492 static ERL_NIF_TERM esock_setopt_addrform(ErlNifEnv*       env,
1493                                           ESockDescriptor* descP,
1494                                           int              level,
1495                                           int              opt,
1496                                           ERL_NIF_TERM     eVal);
1497 #endif
1498 #if defined(IPV6_MTU_DISCOVER)
1499 static ERL_NIF_TERM esock_setopt_ipv6_mtu_discover(ErlNifEnv*       env,
1500                                                    ESockDescriptor* descP,
1501                                                    int              level,
1502                                                    int              opt,
1503                                                    ERL_NIF_TERM     eVal);
1504 #endif
1505 #if defined(IPV6_MULTICAST_HOPS)
1506 static ERL_NIF_TERM esock_setopt_hops(ErlNifEnv*       env,
1507                                       ESockDescriptor* descP,
1508                                       int              level,
1509                                       int              opt,
1510                                       ERL_NIF_TERM     eVal);
1511 #endif
1512 
1513 #if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP)
1514 static ERL_NIF_TERM
1515 esock_setopt_in6_update_membership(ErlNifEnv*       env,
1516                                    ESockDescriptor* descP,
1517                                    int              level,
1518                                    int              opt,
1519                                    ERL_NIF_TERM     eVal);
1520 #endif
1521 
1522 #endif // defined(HAVE_IPV6)
1523 
1524 
1525 #if defined(TCP_CONGESTION)
1526 static ERL_NIF_TERM esock_setopt_tcp_congestion(ErlNifEnv*       env,
1527                                                 ESockDescriptor* descP,
1528                                                 int              level,
1529                                                 int              opt,
1530                                                 ERL_NIF_TERM     eVal);
1531 #endif
1532 
1533 
1534 #if defined(HAVE_SCTP)
1535 
1536 #if defined(SCTP_ASSOCINFO)
1537 static ERL_NIF_TERM esock_setopt_sctp_associnfo(ErlNifEnv*       env,
1538                                                 ESockDescriptor* descP,
1539                                                 int              level,
1540                                                 int              opt,
1541                                                 ERL_NIF_TERM     eVal);
1542 #endif
1543 #if defined(SCTP_EVENTS)
1544 static ERL_NIF_TERM esock_setopt_sctp_events(ErlNifEnv*       env,
1545                                              ESockDescriptor* descP,
1546                                              int              level,
1547                                              int              opt,
1548                                              ERL_NIF_TERM     eVal);
1549 static int esock_setopt_sctp_event(ErlNifEnv   *env,
1550                                    ERL_NIF_TERM eMap,
1551                                    ERL_NIF_TERM eKey,
1552                                    BOOLEAN_T   *failure);
1553 #endif
1554 #if defined(SCTP_INITMSG)
1555 static ERL_NIF_TERM esock_setopt_sctp_initmsg(ErlNifEnv*       env,
1556                                               ESockDescriptor* descP,
1557                                               int              level,
1558                                               int              opt,
1559                                               ERL_NIF_TERM     eVal);
1560 #endif
1561 #if defined(SCTP_RTOINFO)
1562 static ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv*       env,
1563                                               ESockDescriptor* descP,
1564                                               int              level,
1565                                               int              opt,
1566                                               ERL_NIF_TERM     eVal);
1567 #endif
1568 
1569 #endif // defined(HAVE_SCTP)
1570 
1571 
1572 static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv*       env,
1573                                      ESockDescriptor* descP,
1574                                      int              eOpt);
1575 /* *** esock_getopt_otp_debug        ***
1576  * *** esock_getopt_otp_iow          ***
1577  * *** esock_getopt_otp_ctrl_proc    ***
1578  * *** esock_getopt_otp_rcvbuf       ***
1579  * *** esock_getopt_otp_rcvctrlbuf   ***
1580  * *** esock_getopt_otp_sndctrlbuf   ***
1581  * *** esock_getopt_otp_fd           ***
1582  * *** esock_getopt_otp_meta         ***
1583  * *** esock_getopt_otp_use_registry ***
1584  * *** esock_getopt_otp_domain       ***
1585  * *** //esock_getopt_otp_type       ***
1586  * *** //esock_getopt_otp_protocol   ***
1587  * *** //esock_getopt_otp_dtp        ***
1588  */
1589 #define ESOCK_GETOPT_OTP_FUNCS               \
1590     ESOCK_GETOPT_OTP_FUNC_DEF(debug);        \
1591     ESOCK_GETOPT_OTP_FUNC_DEF(iow);          \
1592     ESOCK_GETOPT_OTP_FUNC_DEF(ctrl_proc);    \
1593     ESOCK_GETOPT_OTP_FUNC_DEF(rcvbuf);       \
1594     ESOCK_GETOPT_OTP_FUNC_DEF(rcvctrlbuf);   \
1595     ESOCK_GETOPT_OTP_FUNC_DEF(sndctrlbuf);   \
1596     ESOCK_GETOPT_OTP_FUNC_DEF(fd);           \
1597     ESOCK_GETOPT_OTP_FUNC_DEF(meta);         \
1598     ESOCK_GETOPT_OTP_FUNC_DEF(use_registry); \
1599     ESOCK_GETOPT_OTP_FUNC_DEF(domain);
1600 #if 0
1601     ESOCK_GETOPT_OTP_FUNC_DEF(type);         \
1602     ESOCK_GETOPT_OTP_FUNC_DEF(protocol);     \
1603     ESOCK_GETOPT_OTP_FUNC_DEF(dtp);
1604 #endif
1605 #define ESOCK_GETOPT_OTP_FUNC_DEF(F)                               \
1606     static ERL_NIF_TERM esock_getopt_otp_##F(ErlNifEnv*        env, \
1607                                              ESockDescriptor* descP)
1608 ESOCK_GETOPT_OTP_FUNCS
1609 #undef ESOCK_GETOPT_OTP_FUNC_DEF
1610 
1611 static ERL_NIF_TERM esock_getopt_native(ErlNifEnv*       env,
1612                                         ESockDescriptor* descP,
1613                                         int              level,
1614                                         int              opt,
1615                                         ERL_NIF_TERM     valueSpec);
1616 static ERL_NIF_TERM esock_getopt(ErlNifEnv*       env,
1617                                  ESockDescriptor* descP,
1618                                  int              level,
1619                                  int              opt);
1620 
1621 #if defined(SO_BINDTODEVICE)
1622 static ERL_NIF_TERM esock_getopt_so_bindtodevice(ErlNifEnv*       env,
1623                                                  ESockDescriptor* descP,
1624                                                  int              level,
1625                                                  int              opt);
1626 #endif
1627 #if defined(SO_DOMAIN)
1628 static ERL_NIF_TERM esock_getopt_sock_domain(ErlNifEnv*       env,
1629                                              ESockDescriptor* descP,
1630                                              int              level,
1631                                              int              opt);
1632 #endif
1633 
1634 #if defined(SO_TYPE)
1635 static
1636 ERL_NIF_TERM esock_getopt_sock_type(ErlNifEnv*       env,
1637                                     ESockDescriptor* descP,
1638                                     int              level,
1639                                     int              opt);
1640 #endif
1641 
1642 #if defined(SO_PROTOCOL)
1643 static
1644 ERL_NIF_TERM esock_getopt_sock_protocol(ErlNifEnv*       env,
1645                                         ESockDescriptor* descP,
1646                                         int              level,
1647                                         int              opt);
1648 #endif
1649 
1650 #if defined(IP_MTU_DISCOVER)
1651 static ERL_NIF_TERM esock_getopt_ip_mtu_discover(ErlNifEnv*       env,
1652                                               ESockDescriptor* descP,
1653                                               int              level,
1654                                               int              opt);
1655 #endif
1656 #if defined(IP_MULTICAST_IF)
1657 static ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv*       env,
1658                                               ESockDescriptor* descP,
1659                                               int              level,
1660                                               int              opt);
1661 #endif
1662 #if defined(IP_TOS)
1663 static ERL_NIF_TERM esock_getopt_tos(ErlNifEnv*       env,
1664                                      ESockDescriptor* descP,
1665                                      int              level,
1666                                      int              opt);
1667 #endif
1668 
1669 
1670 #if defined(HAVE_IPV6)
1671 
1672 #if defined(IPV6_MTU_DISCOVER)
1673 static ERL_NIF_TERM esock_getopt_ipv6_mtu_discover(ErlNifEnv*       env,
1674                                                    ESockDescriptor* descP,
1675                                                    int              level,
1676                                                    int              opt);
1677 #endif
1678 
1679 #endif // defined(HAVE_IPV6)
1680 
1681 #if defined(IP_PKTOPTIONS) || defined(IPV6_PKTOPTIONS)
1682 static ERL_NIF_TERM esock_getopt_pktoptions(ErlNifEnv*       env,
1683 					    ESockDescriptor* descP,
1684 					    int              level,
1685 					    int              opt);
1686 #endif
1687 
1688 #if defined(TCP_CONGESTION)
1689 static ERL_NIF_TERM esock_getopt_tcp_congestion(ErlNifEnv*       env,
1690                                                 ESockDescriptor* descP,
1691                                                 int              level,
1692                                                 int              opt);
1693 #endif
1694 
1695 
1696 #if defined(HAVE_SCTP)
1697 
1698 #if defined(SCTP_ASSOCINFO)
1699 static ERL_NIF_TERM esock_getopt_sctp_associnfo(ErlNifEnv*       env,
1700                                                 ESockDescriptor* descP,
1701                                                 int              level,
1702                                                 int              opt);
1703 #endif
1704 #if defined(SCTP_INITMSG)
1705 static ERL_NIF_TERM esock_getopt_sctp_initmsg(ErlNifEnv*       env,
1706                                               ESockDescriptor* descP,
1707                                               int              level,
1708                                               int              opt);
1709 #endif
1710 #if defined(SCTP_RTOINFO)
1711 static ERL_NIF_TERM esock_getopt_sctp_rtoinfo(ErlNifEnv*       env,
1712                                               ESockDescriptor* descP,
1713                                               int              level,
1714                                               int              opt);
1715 #endif
1716 
1717 #endif // defined(HAVE_SCTP)
1718 
1719 
1720 static ERL_NIF_TERM esock_sockname(ErlNifEnv*       env,
1721                                    ESockDescriptor* descP);
1722 static ERL_NIF_TERM esock_peername(ErlNifEnv*       env,
1723                                    ESockDescriptor* descP);
1724 static ERL_NIF_TERM esock_cancel(ErlNifEnv*       env,
1725                                  ESockDescriptor* descP,
1726                                  ERL_NIF_TERM     op,
1727                                  ERL_NIF_TERM     sockRef,
1728                                  ERL_NIF_TERM     opRef);
1729 static ERL_NIF_TERM esock_cancel_connect(ErlNifEnv*       env,
1730                                          ESockDescriptor* descP,
1731                                          ERL_NIF_TERM     opRef);
1732 static ERL_NIF_TERM esock_cancel_accept(ErlNifEnv*       env,
1733                                         ESockDescriptor* descP,
1734                                         ERL_NIF_TERM     sockRef,
1735                                         ERL_NIF_TERM     opRef);
1736 static ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv*       env,
1737                                                 ESockDescriptor* descP,
1738                                                 ERL_NIF_TERM     sockRef);
1739 static ERL_NIF_TERM esock_cancel_accept_waiting(ErlNifEnv*       env,
1740                                                 ESockDescriptor* descP,
1741                                                 ERL_NIF_TERM     opRef,
1742                                                 const ErlNifPid* selfP);
1743 static ERL_NIF_TERM esock_cancel_send(ErlNifEnv*       env,
1744                                       ESockDescriptor* descP,
1745                                       ERL_NIF_TERM     sockRef,
1746                                       ERL_NIF_TERM     opRef);
1747 static ERL_NIF_TERM esock_cancel_send_current(ErlNifEnv*       env,
1748                                               ESockDescriptor* descP,
1749                                               ERL_NIF_TERM     sockRef);
1750 static ERL_NIF_TERM esock_cancel_send_waiting(ErlNifEnv*       env,
1751                                               ESockDescriptor* descP,
1752                                               ERL_NIF_TERM     opRef,
1753                                               const ErlNifPid* selfP);
1754 static ERL_NIF_TERM esock_cancel_recv(ErlNifEnv*       env,
1755                                       ESockDescriptor* descP,
1756                                       ERL_NIF_TERM     sockRef,
1757                                       ERL_NIF_TERM     opRef);
1758 static ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv*       env,
1759                                               ESockDescriptor* descP,
1760                                               ERL_NIF_TERM     sockRef);
1761 static ERL_NIF_TERM esock_cancel_recv_waiting(ErlNifEnv*       env,
1762                                               ESockDescriptor* descP,
1763                                               ERL_NIF_TERM     opRef,
1764                                               const ErlNifPid* selfP);
1765 static ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv*       env,
1766                                              ESockDescriptor* descP,
1767                                              ERL_NIF_TERM     opRef);
1768 static ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv*       env,
1769                                               ESockDescriptor* descP,
1770                                               ERL_NIF_TERM     opRef);
1771 static ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv*       env,
1772                                              ESockDescriptor* descP,
1773                                              ERL_NIF_TERM     opRef,
1774                                              int              smode,
1775                                              int              rmode);
1776 
1777 #if defined(USE_SETOPT_STR_OPT)
1778 static ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv*       env,
1779                                          ESockDescriptor* descP,
1780                                          int              level,
1781                                          int              opt,
1782                                          int              max,
1783                                          ERL_NIF_TERM     eVal);
1784 #endif
1785 static ERL_NIF_TERM esock_setopt_bool_opt(ErlNifEnv*       env,
1786                                           ESockDescriptor* descP,
1787                                           int              level,
1788                                           int              opt,
1789                                           ERL_NIF_TERM     eVal);
1790 static ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv*       env,
1791                                          ESockDescriptor* descP,
1792                                          int              level,
1793                                          int              opt,
1794                                          ERL_NIF_TERM     eVal);
1795 #if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \
1796     && defined(ESOCK_USE_RCVSNDTIMEO)
1797 static ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv*       env,
1798                                              ESockDescriptor* descP,
1799                                              int              level,
1800                                              int              opt,
1801                                              ERL_NIF_TERM     eVal);
1802 #endif
1803 static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv*       env,
1804                                            ESockDescriptor* descP,
1805                                            int              level,
1806                                            int              opt,
1807                                            void*            optVal,
1808                                            socklen_t        optLen);
1809 
1810 #if defined(USE_GETOPT_STR_OPT)
1811 static ERL_NIF_TERM esock_getopt_str_opt(ErlNifEnv*       env,
1812                                          ESockDescriptor* descP,
1813                                          int              level,
1814                                          int              opt,
1815                                          int              max,
1816                                          BOOLEAN_T        stripNUL);
1817 #endif
1818 static ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv*       env,
1819                                           ESockDescriptor* descP,
1820                                           int              level,
1821                                           int              opt);
1822 static ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv*       env,
1823                                          ESockDescriptor* descP,
1824                                          int              level,
1825                                          int              opt);
1826 static BOOLEAN_T esock_getopt_int(SOCKET sock,
1827                                   int    level,
1828                                   int    opt,
1829                                   int*   valP);
1830 static ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv*       env,
1831                                           ESockDescriptor* descP,
1832                                           int              level,
1833                                           int              opt,
1834                                           SOCKOPTLEN_T     valueSz);
1835 static ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv*       env,
1836                                          ESockDescriptor* descP,
1837                                          int              level,
1838                                          int              opt,
1839                                          ErlNifBinary*    binP);
1840 #if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \
1841     && defined(ESOCK_USE_RCVSNDTIMEO)
1842 static ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv*       env,
1843                                              ESockDescriptor* descP,
1844                                              int              level,
1845                                              int              opt);
1846 #endif
1847 
1848 
1849 
1850 /* ------------------------------------------------------------------------
1851  * Socket option tables and handling
1852  */
1853 
1854 struct ESockOpt
1855 {
1856     int opt; // Option number
1857 
1858     // Function to set option
1859     ERL_NIF_TERM (*setopt)
1860     (ErlNifEnv *env, ESockDescriptor *descP,
1861      int level, int opt, ERL_NIF_TERM eVal);
1862 
1863     // Function to get option
1864     ERL_NIF_TERM (*getopt)
1865     (ErlNifEnv *env, ESockDescriptor *descP,
1866      int level, int opt);
1867 
1868     ERL_NIF_TERM *nameP; // Pointer to option name atom
1869 };
1870 
1871 // qsort and bsearch helper
cmpESockOpt(const void * vpa,const void * vpb)1872 static int cmpESockOpt(const void *vpa, const void *vpb) {
1873     struct ESockOpt *a, *b;
1874     a = (struct ESockOpt *) vpa;
1875     b = (struct ESockOpt *) vpb;
1876     return a->opt < b->opt ? -1 : (a->opt > b->opt ? 1 : 0);
1877 }
1878 
1879 
1880 /* SO_* options -------------------------------------------------------- */
1881 
1882 static struct ESockOpt optLevelSocket[] =
1883     {
1884         {
1885 #ifdef SO_ACCEPTCONN
1886             SO_ACCEPTCONN,
1887             esock_setopt_bool_opt, esock_getopt_bool_opt,
1888 #else
1889             0, NULL, NULL,
1890 #endif
1891             &esock_atom_acceptconn},
1892 
1893         {0, NULL, NULL, &esock_atom_acceptfilter},
1894 
1895         {
1896 #ifdef SO_BINDTODEVICE
1897             SO_BINDTODEVICE,
1898             esock_setopt_so_bindtodevice, esock_getopt_so_bindtodevice,
1899 #else
1900             0, NULL, NULL,
1901 #endif
1902             &esock_atom_bindtodevice},
1903 
1904         {
1905 #ifdef SO_BROADCAST
1906             SO_BROADCAST,
1907             esock_setopt_bool_opt, esock_getopt_bool_opt,
1908 #else
1909             0, NULL, NULL,
1910 #endif
1911             &esock_atom_broadcast},
1912 
1913         {0, NULL, NULL, &esock_atom_busy_poll},
1914 
1915         {
1916 #ifdef SO_DEBUG
1917             SO_DEBUG,
1918             esock_setopt_int_opt, esock_getopt_int_opt,
1919 #else
1920             0, NULL, NULL,
1921 #endif
1922             &esock_atom_debug},
1923 
1924         {
1925 #ifdef SO_DOMAIN
1926             SO_DOMAIN,
1927             NULL, esock_getopt_sock_domain,
1928 #else
1929             0, NULL, NULL,
1930 #endif
1931             &esock_atom_domain},
1932 
1933         {
1934 #ifdef SO_DONTROUTE
1935             SO_DONTROUTE,
1936             esock_setopt_bool_opt, esock_getopt_bool_opt,
1937 #else
1938             0, NULL, NULL,
1939 #endif
1940             &esock_atom_dontroute},
1941 
1942         {0, NULL, NULL, &esock_atom_error},
1943 
1944         {
1945 #ifdef SO_KEEPALIVE
1946             SO_KEEPALIVE,
1947             esock_setopt_bool_opt, esock_getopt_bool_opt,
1948 #else
1949             0, NULL, NULL,
1950 #endif
1951             &esock_atom_keepalive},
1952 
1953         {
1954 #ifdef SO_LINGER
1955             SO_LINGER,
1956             esock_setopt_linger, esock_getopt_linger,
1957 #else
1958             0, NULL, NULL,
1959 #endif
1960             &esock_atom_linger},
1961 
1962         {0, NULL, NULL, &esock_atom_mark},
1963 
1964         {
1965 #ifdef SO_OOBINLINE
1966             SO_OOBINLINE,
1967             esock_setopt_bool_opt, esock_getopt_bool_opt,
1968 #else
1969             0, NULL, NULL,
1970 #endif
1971             &esock_atom_oobinline},
1972 
1973         {
1974 #ifdef SO_PASSCRED
1975             SO_PASSCRED,
1976             esock_setopt_bool_opt, esock_getopt_bool_opt,
1977 #else
1978             0, NULL, NULL,
1979 #endif
1980             &esock_atom_passcred},
1981 
1982         {
1983 #ifdef SO_PEEK_OFF
1984             SO_PEEK_OFF,
1985             esock_setopt_int_opt, esock_getopt_int_opt,
1986 #else
1987             0, NULL, NULL,
1988 #endif
1989             &esock_atom_peek_off},
1990 
1991         {0, NULL, NULL, &esock_atom_peercred},
1992 
1993         {
1994 #ifdef SO_PRIORITY
1995             SO_PRIORITY,
1996             esock_setopt_int_opt, esock_getopt_int_opt,
1997 #else
1998             0, NULL, NULL,
1999 #endif
2000             &esock_atom_priority},
2001 
2002         {
2003 #ifdef SO_PROTOCOL
2004             SO_PROTOCOL,
2005             NULL, esock_getopt_sock_protocol,
2006 #else
2007             0, NULL, NULL,
2008 #endif
2009             &esock_atom_protocol},
2010 
2011         {
2012 #ifdef SO_RCVBUF
2013             SO_RCVBUF,
2014             esock_setopt_int_opt, esock_getopt_int_opt,
2015 #else
2016             0, NULL, NULL,
2017 #endif
2018             &esock_atom_rcvbuf},
2019 
2020         {0, NULL, NULL, &esock_atom_rcvbufforce},
2021 
2022         {
2023 #ifdef SO_RCVLOWAT
2024             SO_RCVLOWAT,
2025             esock_setopt_int_opt, esock_getopt_int_opt,
2026 #else
2027             0, NULL, NULL,
2028 #endif
2029             &esock_atom_rcvlowat},
2030 
2031         {
2032 #if defined(SO_RCVTIMEO) && defined(ESOCK_USE_RCVSNDTIMEO)
2033             SO_RCVTIMEO,
2034             esock_setopt_timeval_opt, esock_getopt_timeval_opt,
2035 #else
2036             0, NULL, NULL,
2037 #endif
2038             &esock_atom_rcvtimeo},
2039 
2040         {
2041 #ifdef SO_REUSEADDR
2042             SO_REUSEADDR,
2043             esock_setopt_bool_opt, esock_getopt_bool_opt,
2044 #else
2045             0, NULL, NULL,
2046 #endif
2047             &esock_atom_reuseaddr},
2048 
2049         {
2050 #ifdef SO_REUSEPORT
2051             SO_REUSEPORT,
2052             esock_setopt_bool_opt, esock_getopt_bool_opt,
2053 #else
2054             0, NULL, NULL,
2055 #endif
2056             &esock_atom_reuseport},
2057 
2058         {0, NULL, NULL, &esock_atom_rxq_ovfl},
2059         {0, NULL, NULL, &esock_atom_setfib},
2060 
2061         {
2062 #ifdef SO_SNDBUF
2063             SO_SNDBUF,
2064             esock_setopt_int_opt, esock_getopt_int_opt,
2065 #else
2066             0, NULL, NULL,
2067 #endif
2068             &esock_atom_sndbuf},
2069 
2070         {0, NULL, NULL, &esock_atom_sndbufforce},
2071 
2072         {
2073 #ifdef SO_SNDLOWAT
2074             SO_SNDLOWAT,
2075             esock_setopt_int_opt, esock_getopt_int_opt,
2076 #else
2077             0, NULL, NULL,
2078 #endif
2079             &esock_atom_sndlowat},
2080 
2081         {
2082 #if defined(SO_SNDTIMEO) && defined(ESOCK_USE_RCVSNDTIMEO)
2083             SO_SNDTIMEO,
2084             esock_setopt_timeval_opt, esock_getopt_timeval_opt,
2085 #else
2086             0, NULL, NULL,
2087 #endif
2088             &esock_atom_sndtimeo},
2089 
2090         {
2091 #ifdef SO_TIMESTAMP
2092             SO_TIMESTAMP,
2093             esock_setopt_bool_opt, esock_getopt_bool_opt,
2094 #else
2095             0, NULL, NULL,
2096 #endif
2097             &esock_atom_timestamp},
2098 
2099         {
2100 #ifdef SO_TYPE
2101             SO_TYPE,
2102             NULL, esock_getopt_sock_type,
2103 #else
2104             0, NULL, NULL,
2105 #endif
2106             &esock_atom_type}
2107     };
2108 
2109 
2110 /* IP_* options -------------------------------------------------------- */
2111 
2112 static struct ESockOpt optLevelIP[] =
2113     {
2114         {
2115 #ifdef IP_ADD_MEMBERSHIP
2116             IP_ADD_MEMBERSHIP,
2117             esock_setopt_in_update_membership, NULL,
2118 #else
2119             0, NULL, NULL,
2120 #endif
2121             &esock_atom_add_membership},
2122 
2123         {
2124 #ifdef IP_ADD_SOURCE_MEMBERSHIP
2125             IP_ADD_SOURCE_MEMBERSHIP,
2126             esock_setopt_in_update_source, NULL,
2127 #else
2128             0, NULL, NULL,
2129 #endif
2130             &esock_atom_add_source_membership},
2131 
2132         {
2133 #ifdef IP_BLOCK_SOURCE
2134             IP_BLOCK_SOURCE,
2135             esock_setopt_in_update_source, NULL,
2136 #else
2137             0, NULL, NULL,
2138 #endif
2139             &esock_atom_block_source},
2140 
2141         {0, NULL, NULL, &esock_atom_dontfrag},
2142 
2143         {
2144 #ifdef IP_DROP_MEMBERSHIP
2145             IP_DROP_MEMBERSHIP,
2146             esock_setopt_in_update_membership, NULL,
2147 #else
2148             0, NULL, NULL,
2149 #endif
2150             &esock_atom_drop_membership},
2151 
2152         {
2153 #ifdef IP_DROP_SOURCE_MEMBERSHIP
2154             IP_DROP_SOURCE_MEMBERSHIP,
2155             esock_setopt_in_update_source, NULL,
2156 #else
2157             0, NULL, NULL,
2158 #endif
2159             &esock_atom_drop_source_membership},
2160 
2161         {
2162 #ifdef IP_FREEBIND
2163             IP_FREEBIND,
2164             esock_setopt_bool_opt, esock_getopt_bool_opt,
2165 #else
2166             0, NULL, NULL,
2167 #endif
2168             &esock_atom_freebind},
2169 
2170         {
2171 #ifdef IP_HDRINCL
2172             IP_HDRINCL,
2173             esock_setopt_bool_opt, esock_getopt_bool_opt,
2174 #else
2175             0, NULL, NULL,
2176 #endif
2177             &esock_atom_hdrincl},
2178 
2179         {
2180 #ifdef IP_MINTTL
2181             IP_MINTTL,
2182             esock_setopt_int_opt, esock_getopt_int_opt,
2183 #else
2184             0, NULL, NULL,
2185 #endif
2186             &esock_atom_minttl},
2187 
2188         {
2189 #if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
2190             IP_MSFILTER,
2191             esock_setopt_msfilter, NULL,
2192 #else
2193             0, NULL, NULL,
2194 #endif
2195             &esock_atom_msfilter},
2196 
2197         {
2198 #ifdef IP_MTU
2199             IP_MTU,
2200             NULL, esock_getopt_int_opt,
2201 #else
2202             0, NULL, NULL,
2203 #endif
2204             &esock_atom_mtu},
2205 
2206         {
2207 #ifdef IP_MTU_DISCOVER
2208             IP_MTU_DISCOVER,
2209             esock_setopt_ip_mtu_discover, esock_getopt_ip_mtu_discover,
2210 #else
2211             0, NULL, NULL,
2212 #endif
2213             &esock_atom_mtu_discover},
2214 
2215         {
2216 #ifdef IP_MULTICAST_ALL
2217             IP_MULTICAST_ALL,
2218             esock_setopt_bool_opt, esock_getopt_bool_opt,
2219 #else
2220             0, NULL, NULL,
2221 #endif
2222             &esock_atom_multicast_all},
2223 
2224         {
2225 #ifdef IP_MULTICAST_IF
2226             IP_MULTICAST_IF,
2227             esock_setopt_multicast_if, esock_getopt_multicast_if,
2228 #else
2229             0, NULL, NULL,
2230 #endif
2231             &esock_atom_multicast_if},
2232 
2233         {
2234 #ifdef IP_MULTICAST_LOOP
2235             IP_MULTICAST_LOOP,
2236             esock_setopt_bool_opt, esock_getopt_bool_opt,
2237 #else
2238             0, NULL, NULL,
2239 #endif
2240             &esock_atom_multicast_loop},
2241 
2242         {
2243 #ifdef IP_MULTICAST_TTL
2244             IP_MULTICAST_TTL,
2245             esock_setopt_int_opt, esock_getopt_int_opt,
2246 #else
2247             0, NULL, NULL,
2248 #endif
2249             &esock_atom_multicast_ttl},
2250 
2251         {
2252 #ifdef IP_NODEFRAG
2253             IP_NODEFRAG,
2254             esock_setopt_bool_opt, esock_getopt_bool_opt,
2255 #else
2256             0, NULL, NULL,
2257 #endif
2258             &esock_atom_nodefrag},
2259 
2260         {0, NULL, NULL, &esock_atom_options},
2261 
2262         {
2263 #ifdef IP_PKTINFO
2264             IP_PKTINFO,
2265             esock_setopt_bool_opt, esock_getopt_bool_opt,
2266 #else
2267             0, NULL, NULL,
2268 #endif
2269             &esock_atom_pktinfo},
2270 
2271         {
2272 #ifdef IP_PKTOPTIONS
2273             IP_PKTOPTIONS,
2274             NULL, esock_getopt_pktoptions,
2275 #else
2276             0, NULL, NULL,
2277 #endif
2278             &esock_atom_pktoptions},
2279 
2280         {
2281 #ifdef IP_RECVDSTADDR
2282             IP_RECVDSTADDR,
2283             esock_setopt_bool_opt, esock_getopt_bool_opt,
2284 #else
2285             0, NULL, NULL,
2286 #endif
2287             &esock_atom_recvdstaddr},
2288 
2289         {
2290 #ifdef IP_RECVERR
2291             IP_RECVERR,
2292             esock_setopt_bool_opt, esock_getopt_bool_opt,
2293 #else
2294             0, NULL, NULL,
2295 #endif
2296             &esock_atom_recverr},
2297 
2298         {
2299 #ifdef IP_RECVIF
2300             IP_RECVIF,
2301             esock_setopt_bool_opt, esock_getopt_bool_opt,
2302 #else
2303             0, NULL, NULL,
2304 #endif
2305             &esock_atom_recvif},
2306 
2307         {
2308 #ifdef IP_RECVOPTS
2309             IP_RECVOPTS,
2310             esock_setopt_bool_opt, esock_getopt_bool_opt,
2311 #else
2312             0, NULL, NULL,
2313 #endif
2314             &esock_atom_recvopts},
2315 
2316         {
2317 #ifdef IP_RECVORIGDSTADDR
2318             IP_RECVORIGDSTADDR,
2319             esock_setopt_bool_opt, esock_getopt_bool_opt,
2320 #else
2321             0, NULL, NULL,
2322 #endif
2323             &esock_atom_recvorigdstaddr},
2324 
2325         {
2326 #ifdef IP_RECVTOS
2327             IP_RECVTOS,
2328             esock_setopt_bool_opt, esock_getopt_bool_opt,
2329 #else
2330             0, NULL, NULL,
2331 #endif
2332             &esock_atom_recvtos},
2333 
2334         {
2335 #ifdef IP_RECVTTL
2336             IP_RECVTTL,
2337             esock_setopt_bool_opt, esock_getopt_bool_opt,
2338 #else
2339             0, NULL, NULL,
2340 #endif
2341             &esock_atom_recvttl},
2342 
2343         {
2344 #ifdef IP_RETOPTS
2345             IP_RETOPTS,
2346             esock_setopt_bool_opt, esock_getopt_bool_opt,
2347 #else
2348             0, NULL, NULL,
2349 #endif
2350             &esock_atom_retopts},
2351 
2352         {
2353 #ifdef IP_ROUTER_ALERT
2354             IP_ROUTER_ALERT,
2355             esock_setopt_int_opt, esock_getopt_int_opt,
2356 #else
2357             0, NULL, NULL,
2358 #endif
2359             &esock_atom_router_alert},
2360 
2361         {
2362 #ifdef IP_SENDSRCADDR
2363             IP_SENDSRCADDR,
2364             esock_setopt_bool_opt, esock_getopt_bool_opt,
2365 #else
2366             0, NULL, NULL,
2367 #endif
2368             &esock_atom_sendsrcaddr},
2369 
2370         {
2371 #ifdef IP_TOS
2372             IP_TOS,
2373             esock_setopt_tos, esock_getopt_tos,
2374 #else
2375             0, NULL, NULL,
2376 #endif
2377             &esock_atom_tos},
2378 
2379         {
2380 #ifdef IP_TRANSPARENT
2381             IP_TRANSPARENT,
2382             esock_setopt_bool_opt, esock_getopt_bool_opt,
2383 #else
2384             0, NULL, NULL,
2385 #endif
2386             &esock_atom_transparent},
2387 
2388         {
2389 #ifdef IP_TTL
2390             IP_TTL,
2391             esock_setopt_int_opt, esock_getopt_int_opt,
2392 #else
2393             0, NULL, NULL,
2394 #endif
2395             &esock_atom_ttl},
2396 
2397         {
2398 #ifdef IP_UNBLOCK_SOURCE
2399             IP_UNBLOCK_SOURCE,
2400             esock_setopt_in_update_source, NULL,
2401 #else
2402             0, NULL, NULL,
2403 #endif
2404             &esock_atom_unblock_source}
2405 
2406     };
2407 
2408 /* IPV6_* options ------------------------------------------------------ */
2409 
2410 #ifdef HAVE_IPV6
2411 static struct ESockOpt optLevelIPV6[] =
2412     {
2413 
2414         {
2415 #ifdef IPV6_ADDRFORM
2416             IPV6_ADDRFORM,
2417             esock_setopt_addrform, NULL,
2418 #else
2419             0, NULL, NULL,
2420 #endif
2421             &esock_atom_addrform},
2422 
2423         {
2424 #ifdef IPV6_ADD_MEMBERSHIP
2425             IPV6_ADD_MEMBERSHIP,
2426             esock_setopt_in6_update_membership, NULL,
2427 #else
2428             0, NULL, NULL,
2429 #endif
2430             &esock_atom_add_membership},
2431 
2432         {
2433 #ifdef IPV6_AUTHHDR
2434             IPV6_AUTHHDR,
2435             esock_setopt_bool_opt, esock_getopt_bool_opt,
2436 #else
2437             0, NULL, NULL,
2438 #endif
2439             &esock_atom_authhdr},
2440 
2441         {0, NULL, NULL, &esock_atom_auth_level},
2442         {0, NULL, NULL, &esock_atom_checksum},
2443 
2444         {
2445 #ifdef IPV6_DROP_MEMBERSHIP
2446             IPV6_DROP_MEMBERSHIP,
2447             esock_setopt_in6_update_membership, NULL,
2448 #else
2449             0, NULL, NULL,
2450 #endif
2451             &esock_atom_drop_membership},
2452 
2453         {
2454 #if defined(IPV6_DSTOPTS)
2455             IPV6_DSTOPTS,
2456             esock_setopt_bool_opt, esock_getopt_bool_opt,
2457 #else
2458             0, NULL, NULL,
2459 #endif
2460             &esock_atom_dstopts},
2461 
2462         {0, NULL, NULL, &esock_atom_esp_network_level},
2463         {0, NULL, NULL, &esock_atom_esp_trans_level},
2464         {0, NULL, NULL, &esock_atom_faith},
2465 
2466         {
2467 #ifdef IPV6_FLOWINFO
2468             IPV6_FLOWINFO,
2469             esock_setopt_bool_opt, esock_getopt_bool_opt,
2470 #else
2471             0, NULL, NULL,
2472 #endif
2473             &esock_atom_flowinfo},
2474 
2475         {
2476 #ifdef IPV6_HOPLIMIT
2477             IPV6_HOPLIMIT,
2478             esock_setopt_bool_opt, esock_getopt_bool_opt,
2479 #else
2480             0, NULL, NULL,
2481 #endif
2482             &esock_atom_hoplimit},
2483 
2484         {
2485 #ifdef IPV6_HOPOPTS
2486             IPV6_HOPOPTS,
2487             esock_setopt_bool_opt, esock_getopt_bool_opt,
2488 #else
2489             0, NULL, NULL,
2490 #endif
2491             &esock_atom_hopopts},
2492 
2493         {0, NULL, NULL, &esock_atom_ipcomp_level},
2494         {0, NULL, NULL, &esock_atom_join_group},
2495         {0, NULL, NULL, &esock_atom_leave_group},
2496 
2497         {
2498 #ifdef IPV6_MTU
2499             IPV6_MTU,
2500             esock_setopt_int_opt, esock_getopt_int_opt,
2501 #else
2502             0, NULL, NULL,
2503 #endif
2504             &esock_atom_mtu},
2505 
2506         {
2507 #ifdef IPV6_MTU_DISCOVER
2508             IPV6_MTU_DISCOVER,
2509             esock_setopt_ipv6_mtu_discover, esock_getopt_ipv6_mtu_discover,
2510 #else
2511             0, NULL, NULL,
2512 #endif
2513             &esock_atom_mtu_discover},
2514 
2515         {
2516 #ifdef IPV6_MULTICAST_HOPS
2517             IPV6_MULTICAST_HOPS,
2518             esock_setopt_hops, esock_getopt_int_opt,
2519 #else
2520             0, NULL, NULL,
2521 #endif
2522             &esock_atom_multicast_hops},
2523 
2524         {
2525 #ifdef IPV6_MULTICAST_IF
2526             IPV6_MULTICAST_IF,
2527             esock_setopt_int_opt, esock_getopt_int_opt,
2528 #else
2529             0, NULL, NULL,
2530 #endif
2531             &esock_atom_multicast_if},
2532 
2533         {
2534 #ifdef IPV6_MULTICAST_LOOP
2535             IPV6_MULTICAST_LOOP,
2536             esock_setopt_bool_opt, esock_getopt_bool_opt,
2537 #else
2538             0, NULL, NULL,
2539 #endif
2540             &esock_atom_multicast_loop},
2541 
2542         {0, NULL, NULL, &esock_atom_portrange},
2543 
2544         {
2545 #ifdef IPV6_PKTOPTIONS
2546             IPV6_PKTOPTIONS,
2547             NULL, esock_getopt_pktoptions,
2548 #else
2549             0, NULL, NULL,
2550 #endif
2551             &esock_atom_pktoptions},
2552 
2553         {
2554 #ifdef IPV6_RECVERR
2555             IPV6_RECVERR,
2556             esock_setopt_bool_opt, esock_getopt_bool_opt,
2557 #else
2558             0, NULL, NULL,
2559 #endif
2560             &esock_atom_recverr},
2561 
2562         {
2563 #ifdef IPV6_RECVHOPLIMIT
2564             IPV6_RECVHOPLIMIT,
2565             esock_setopt_bool_opt, esock_getopt_bool_opt,
2566 #else
2567             0, NULL, NULL,
2568 #endif
2569             &esock_atom_recvhoplimit},
2570 
2571         {
2572 #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO)
2573 #if defined(IPV6_RECVPKTINFO)
2574                  IPV6_RECVPKTINFO,
2575 #else
2576                  IPV6_PKTINFO,
2577 #endif
2578                  esock_setopt_bool_opt, esock_getopt_bool_opt,
2579 #else
2580             0, NULL, NULL,
2581 #endif
2582             &esock_atom_recvpktinfo},
2583 
2584         {
2585 #ifdef IPV6_RECVTCLASS
2586             IPV6_RECVTCLASS,
2587             esock_setopt_bool_opt, esock_getopt_bool_opt,
2588 #else
2589             0, NULL, NULL,
2590 #endif
2591             &esock_atom_recvtclass},
2592 
2593         {
2594 #ifdef IPV6_ROUTER_ALERT
2595             IPV6_ROUTER_ALERT,
2596             esock_setopt_int_opt, esock_getopt_int_opt,
2597 #else
2598             0, NULL, NULL,
2599 #endif
2600             &esock_atom_router_alert},
2601 
2602         {
2603 #ifdef IPV6_RTHDR
2604             IPV6_RTHDR,
2605             esock_setopt_bool_opt, esock_getopt_bool_opt,
2606 #else
2607             0, NULL, NULL,
2608 #endif
2609             &esock_atom_rthdr},
2610 
2611         {
2612 #ifdef IPV6_TCLASS
2613             IPV6_TCLASS,
2614             esock_setopt_int_opt, esock_getopt_int_opt,
2615 #else
2616             0, NULL, NULL,
2617 #endif
2618             &esock_atom_tclass},
2619 
2620         {
2621 #ifdef IPV6_UNICAST_HOPS
2622             IPV6_UNICAST_HOPS,
2623             esock_setopt_hops, esock_getopt_int_opt,
2624 #else
2625             0, NULL, NULL,
2626 #endif
2627             &esock_atom_unicast_hops},
2628 
2629         {0, NULL, NULL, &esock_atom_use_min_mtu},
2630 
2631         {
2632 #ifdef IPV6_V6ONLY
2633             IPV6_V6ONLY,
2634             esock_setopt_bool_opt, esock_getopt_bool_opt,
2635 #else
2636             0, NULL, NULL,
2637 #endif
2638             &esock_atom_v6only}
2639 
2640         };
2641 #endif // #ifdef HAVE_IPV6
2642 
2643 
2644 /* SCTP_* options ------------------------------------------------------ */
2645 
2646 #ifdef HAVE_SCTP
2647 static struct ESockOpt optLevelSCTP[] =
2648     {
2649 
2650         {0, NULL, NULL, &esock_atom_adaption_layer},
2651 
2652         {
2653 #ifdef SCTP_ASSOCINFO
2654             SCTP_ASSOCINFO,
2655             esock_setopt_sctp_associnfo, esock_getopt_sctp_associnfo,
2656 #else
2657             0, NULL, NULL,
2658 #endif
2659             &esock_atom_associnfo},
2660 
2661         {0, NULL, NULL, &esock_atom_auth_active_key},
2662         {0, NULL, NULL, &esock_atom_auth_chunk},
2663         {0, NULL, NULL, &esock_atom_auth_delete_key},
2664         {0, NULL, NULL, &esock_atom_auth_key},
2665 
2666         {
2667 #ifdef SCTP_AUTOCLOSE
2668             SCTP_AUTOCLOSE,
2669             esock_setopt_int_opt, esock_getopt_int_opt,
2670 #else
2671             0, NULL, NULL,
2672 #endif
2673             &esock_atom_autoclose},
2674 
2675         {0, NULL, NULL, &esock_atom_context},
2676         {0, NULL, NULL, &esock_atom_default_send_params},
2677         {0, NULL, NULL, &esock_atom_delayed_ack_time},
2678 
2679         {
2680 #ifdef SCTP_DISABLE_FRAGMENTS
2681             SCTP_DISABLE_FRAGMENTS,
2682             esock_setopt_bool_opt, esock_getopt_bool_opt,
2683 #else
2684             0, NULL, NULL,
2685 #endif
2686             &esock_atom_disable_fragments},
2687 
2688         {0, NULL, NULL, &esock_atom_hmac_ident},
2689 
2690         {
2691 #ifdef SCTP_EVENTS
2692             SCTP_EVENTS,
2693             esock_setopt_sctp_events, NULL,
2694 #else
2695             0, NULL, NULL,
2696 #endif
2697             &esock_atom_events},
2698 
2699         {0, NULL, NULL, &esock_atom_explicit_eor},
2700         {0, NULL, NULL, &esock_atom_fragment_interleave},
2701         {0, NULL, NULL, &esock_atom_get_peer_addr_info},
2702 
2703         {
2704 #ifdef SCTP_INITMSG
2705             SCTP_INITMSG,
2706             esock_setopt_sctp_initmsg, esock_getopt_sctp_initmsg,
2707 #else
2708             0, NULL, NULL,
2709 #endif
2710             &esock_atom_initmsg},
2711 
2712         {0, NULL, NULL, &esock_atom_i_want_mapped_v4_addr},
2713         {0, NULL, NULL, &esock_atom_local_auth_chunks},
2714 
2715         {
2716 #ifdef SCTP_MAXSEG
2717             SCTP_MAXSEG,
2718             esock_setopt_int_opt, esock_getopt_int_opt,
2719 #else
2720             0, NULL, NULL,
2721 #endif
2722             &esock_atom_maxseg},
2723 
2724         {0, NULL, NULL, &esock_atom_maxburst},
2725 
2726         {
2727 #ifdef SCTP_NODELAY
2728             SCTP_NODELAY,
2729             esock_setopt_bool_opt, esock_getopt_bool_opt,
2730 #else
2731             0, NULL, NULL,
2732 #endif
2733             &esock_atom_nodelay},
2734 
2735         {0, NULL, NULL, &esock_atom_partial_delivery_point},
2736         {0, NULL, NULL, &esock_atom_peer_addr_params},
2737         {0, NULL, NULL, &esock_atom_peer_auth_chunks},
2738         {0, NULL, NULL, &esock_atom_primary_addr},
2739         {0, NULL, NULL, &esock_atom_reset_streams},
2740 
2741         {
2742 #ifdef SCTP_RTOINFO
2743             SCTP_RTOINFO,
2744             esock_setopt_sctp_rtoinfo, esock_getopt_sctp_rtoinfo,
2745 #else
2746             0, NULL, NULL,
2747 #endif
2748             &esock_atom_rtoinfo},
2749 
2750         {0, NULL, NULL, &esock_atom_set_peer_primary_addr},
2751         {0, NULL, NULL, &esock_atom_status},
2752         {0, NULL, NULL, &esock_atom_use_ext_recvinfo}
2753 
2754     };
2755 #endif // #ifdef HAVE_SCTP
2756 
2757 /* TCP_* options ------------------------------------------------------- */
2758 
2759 static struct ESockOpt optLevelTCP[] =
2760     {
2761 
2762         {
2763 #ifdef TCP_CONGESTION
2764             TCP_CONGESTION,
2765             esock_setopt_tcp_congestion, esock_getopt_tcp_congestion,
2766 #else
2767             0, NULL, NULL,
2768 #endif
2769             &esock_atom_congestion},
2770 
2771         {
2772 #ifdef TCP_CORK
2773             TCP_CORK,
2774             esock_setopt_bool_opt, esock_getopt_bool_opt,
2775 #else
2776             0, NULL, NULL,
2777 #endif
2778             &esock_atom_cork},
2779 
2780         {0, NULL, NULL, &esock_atom_info},
2781         {0, NULL, NULL, &esock_atom_keepcnt},
2782         {0, NULL, NULL, &esock_atom_keepidle},
2783         {0, NULL, NULL, &esock_atom_keepintvl},
2784 
2785         {
2786 #ifdef TCP_MAXSEG
2787             TCP_MAXSEG,
2788             esock_setopt_int_opt, esock_getopt_int_opt,
2789 #else
2790             0, NULL, NULL,
2791 #endif
2792             &esock_atom_maxseg},
2793 
2794         {0, NULL, NULL, &esock_atom_md5sig},
2795 
2796         {
2797 #ifdef TCP_NODELAY
2798             TCP_NODELAY,
2799             esock_setopt_bool_opt, esock_getopt_bool_opt,
2800 #else
2801             0, NULL, NULL,
2802 #endif
2803             &esock_atom_nodelay},
2804 
2805         {0, NULL, NULL, &esock_atom_noopt},
2806         {0, NULL, NULL, &esock_atom_nopush},
2807         {0, NULL, NULL, &esock_atom_syncnt},
2808         {0, NULL, NULL, &esock_atom_user_timeout}
2809 
2810     };
2811 
2812 
2813 /* UDP_* options ------------------------------------------------------- */
2814 
2815 static struct ESockOpt optLevelUDP[] =
2816     {
2817 
2818         {
2819 #ifdef UDP_CORK
2820             UDP_CORK,
2821             esock_setopt_bool_opt, esock_getopt_bool_opt,
2822 #else
2823             0, NULL, NULL,
2824 #endif
2825             &esock_atom_cork}
2826 
2827     };
2828 
2829 /* Option levels table ------------------------------------------------- */
2830 
2831 struct ESockOptLevel
2832 {
2833     int level; // Level number
2834 
2835     size_t num; // Number of options
2836 
2837     struct ESockOpt *opts; // Options table
2838 };
2839 
2840 // qsort and bsearch helper
cmpESockOptLevel(const void * vpa,const void * vpb)2841 static int cmpESockOptLevel(const void *vpa, const void *vpb) {
2842     struct ESockOptLevel *a, *b;
2843     a = (struct ESockOptLevel*) vpa;
2844     b = (struct ESockOptLevel*) vpb;
2845     return a->level < b->level ? -1 : (a->level > b->level ? 1 : 0);
2846 }
2847 
2848 #define OPT_LEVEL(Level, Opts) {(Level), NUM(Opts), (Opts)}
2849 
2850 /* Table --------------------------------------------------------------- */
2851 
2852 static struct ESockOptLevel optLevels[] =
2853     {
2854         OPT_LEVEL(SOL_SOCKET, optLevelSocket),
2855 
2856 #ifdef SOL_IP
2857         OPT_LEVEL(SOL_IP, optLevelIP),
2858 #else
2859         OPT_LEVEL(IPPROTO_IP, optLevelIP),
2860 #endif
2861 
2862 #ifdef HAVE_IPV6
2863 #ifdef SOL_IPV6
2864         OPT_LEVEL(SOL_IPV6, optLevelIPV6),
2865 #else
2866         OPT_LEVEL(IPPROTO_IPV6, optLevelIPV6),
2867 #endif
2868 #endif // #ifdef HAVE_IPV6
2869 
2870 #ifdef HAVE_SCTP
2871         OPT_LEVEL(IPPROTO_SCTP, optLevelSCTP),
2872 #endif // #ifdef HAVE_SCTP
2873 
2874         OPT_LEVEL(IPPROTO_UDP, optLevelUDP),
2875         OPT_LEVEL(IPPROTO_TCP, optLevelTCP)
2876     };
2877 
2878 #undef OPT_LEVEL
2879 
2880 /* Tables init (sorting) ----------------------------------------------- */
2881 
2882 #define ESOCK_SORT_TABLE(Array, Cmp)                            \
2883     qsort((Array), NUM(Array), sizeof(*(Array)), (Cmp))
2884 
initOpts(void)2885 static void initOpts(void) {
2886     ESOCK_SORT_TABLE(optLevelSocket, cmpESockOpt);
2887     ESOCK_SORT_TABLE(optLevelIP, cmpESockOpt);
2888 #ifdef HAVE_IPV6
2889     ESOCK_SORT_TABLE(optLevelIPV6, cmpESockOpt);
2890 #endif
2891 #ifdef HAVE_SCTP
2892     ESOCK_SORT_TABLE(optLevelSCTP, cmpESockOpt);
2893 #endif
2894     ESOCK_SORT_TABLE(optLevelTCP, cmpESockOpt);
2895     ESOCK_SORT_TABLE(optLevelUDP, cmpESockOpt);
2896     ESOCK_SORT_TABLE(optLevels, cmpESockOptLevel);
2897 }
2898 
2899 /* Option lookup in tables --------------------------------------------- */
2900 
lookupOpt(int level,int opt)2901 static struct ESockOpt *lookupOpt(int level, int opt) {
2902     struct ESockOptLevel levelKey, *levelP;
2903     struct ESockOpt optKey;
2904 
2905     sys_memzero((char *) &levelKey, sizeof(levelKey));
2906     levelKey.level = level;
2907     levelP =
2908         bsearch(&levelKey, optLevels, NUM(optLevels), sizeof(*optLevels),
2909                 cmpESockOptLevel);
2910     if (levelP == NULL)
2911         return NULL;
2912 
2913     sys_memzero((char *) &optKey, sizeof(optKey));
2914     optKey.opt = opt;
2915     return
2916         bsearch(&optKey, levelP->opts, levelP->num, sizeof(*levelP->opts),
2917                 cmpESockOpt);
2918 }
2919 
2920 /* --------------------------------------------------------------------- */
2921 
2922 
2923 
2924 static BOOLEAN_T send_check_writer(ErlNifEnv*       env,
2925                                    ESockDescriptor* descP,
2926                                    ERL_NIF_TERM     ref,
2927                                    ERL_NIF_TERM*    checkResult);
2928 static ERL_NIF_TERM send_check_result(ErlNifEnv*       env,
2929                                       ESockDescriptor* descP,
2930                                       ssize_t          send_result,
2931                                       ssize_t          dataSize,
2932                                       BOOLEAN_T        dataInTail,
2933                                       ERL_NIF_TERM     sockRef,
2934                                       ERL_NIF_TERM     sendRef);
2935 static ERL_NIF_TERM send_check_ok(ErlNifEnv*       env,
2936                                   ESockDescriptor* descP,
2937                                   ssize_t          written,
2938                                   ERL_NIF_TERM     sockRef);
2939 static ERL_NIF_TERM send_check_fail(ErlNifEnv*       env,
2940                                     ESockDescriptor* descP,
2941                                     int              saveErrno,
2942                                     ERL_NIF_TERM     sockRef);
2943 static void send_error_waiting_writers(ErlNifEnv*       env,
2944                                        ESockDescriptor* descP,
2945                                        ERL_NIF_TERM     sockRef,
2946                                        ERL_NIF_TERM     reason);
2947 static ERL_NIF_TERM send_check_retry(ErlNifEnv*       env,
2948                                      ESockDescriptor* descP,
2949                                      ssize_t          written,
2950                                      ERL_NIF_TERM     sockRef,
2951                                      ERL_NIF_TERM     sendRef);
2952 static BOOLEAN_T recv_check_reader(ErlNifEnv*       env,
2953                                    ESockDescriptor* descP,
2954                                    ERL_NIF_TERM     ref,
2955                                    ERL_NIF_TERM*    checkResult);
2956 static void recv_init_current_reader(ErlNifEnv*       env,
2957                                      ESockDescriptor* descP,
2958                                      ERL_NIF_TERM     ref);
2959 static void recv_update_current_reader(ErlNifEnv*       env,
2960                                        ESockDescriptor* descP,
2961                                        ERL_NIF_TERM     sockRef);
2962 static void recv_error_current_reader(ErlNifEnv*       env,
2963                                       ESockDescriptor* descP,
2964                                       ERL_NIF_TERM     sockRef,
2965                                       ERL_NIF_TERM     reason);
2966 static ERL_NIF_TERM recv_check_result(ErlNifEnv*       env,
2967                                       ESockDescriptor* descP,
2968                                       ssize_t          read,
2969                                       ssize_t          toRead,
2970                                       int              saveErrno,
2971                                       ErlNifBinary*    bufP,
2972                                       ERL_NIF_TERM     sockRef,
2973                                       ERL_NIF_TERM     recvRef);
2974 static ERL_NIF_TERM recv_check_full(ErlNifEnv*       env,
2975                                     ESockDescriptor* descP,
2976                                     ssize_t          read,
2977                                     ssize_t          toRead,
2978                                     ErlNifBinary*    bufP,
2979                                     ERL_NIF_TERM     sockRef,
2980                                     ERL_NIF_TERM     recvRef);
2981 static ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv*       env,
2982                                                ESockDescriptor* descP,
2983                                                ssize_t          read,
2984                                                ErlNifBinary*    bufP,
2985                                                ERL_NIF_TERM     sockRef,
2986                                                ERL_NIF_TERM     recvRef);
2987 static ERL_NIF_TERM recv_check_full_done(ErlNifEnv*       env,
2988                                          ESockDescriptor* descP,
2989                                          ssize_t          read,
2990                                          ErlNifBinary*    bufP,
2991                                          ERL_NIF_TERM     sockRef);
2992 static ERL_NIF_TERM recv_check_fail(ErlNifEnv*       env,
2993                                     ESockDescriptor* descP,
2994                                     int              saveErrno,
2995                                     ErlNifBinary*    buf1P,
2996                                     ErlNifBinary*    buf2P,
2997                                     ERL_NIF_TERM     sockRef,
2998                                     ERL_NIF_TERM     recvRef);
2999 static ERL_NIF_TERM recv_check_fail_econnreset(ErlNifEnv*       env,
3000                                                ESockDescriptor* descP,
3001                                                ERL_NIF_TERM     sockRef,
3002                                                ERL_NIF_TERM     recvRef);
3003 static ERL_NIF_TERM recv_check_partial(ErlNifEnv*       env,
3004                                        ESockDescriptor* descP,
3005                                        ssize_t          read,
3006                                        ssize_t          toRead,
3007                                        ErlNifBinary*    bufP,
3008                                        ERL_NIF_TERM     sockRef,
3009                                        ERL_NIF_TERM     recvRef);
3010 static ERL_NIF_TERM recv_check_partial_done(ErlNifEnv*       env,
3011                                             ESockDescriptor* descP,
3012                                             ssize_t          read,
3013                                             ErlNifBinary*    bufP,
3014                                             ERL_NIF_TERM     sockRef);
3015 static ERL_NIF_TERM recv_check_partial_part(ErlNifEnv*       env,
3016                                             ESockDescriptor* descP,
3017                                             ssize_t          read,
3018                                             ErlNifBinary*    bufP,
3019                                             ERL_NIF_TERM     sockRef,
3020                                             ERL_NIF_TERM     recvRef);
3021 static ERL_NIF_TERM recv_check_retry(ErlNifEnv*       env,
3022                                      ESockDescriptor* descP,
3023                                      ERL_NIF_TERM     sockRef,
3024                                      ERL_NIF_TERM     recvRef);
3025 static ERL_NIF_TERM recv_check_fail_gen(ErlNifEnv*       env,
3026                                         ESockDescriptor* descP,
3027                                         int              saveErrno,
3028                                         ERL_NIF_TERM     sockRef);
3029 static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv*       env,
3030                                           ESockDescriptor* descP,
3031                                           ssize_t          read,
3032                                           int              saveErrno,
3033                                           ErlNifBinary*    bufP,
3034                                           ESockAddress*    fromAddrP,
3035                                           SOCKLEN_T        fromAddrLen,
3036                                           ERL_NIF_TERM     sockRef,
3037                                           ERL_NIF_TERM     recvRef);
3038 static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv*       env,
3039                                          ESockDescriptor* descP,
3040                                          ssize_t          read,
3041                                          int              saveErrno,
3042                                          struct msghdr*   msgHdrP,
3043                                          ErlNifBinary*    dataBufP,
3044                                          ErlNifBinary*    ctrlBufP,
3045                                          ERL_NIF_TERM     sockRef,
3046                                          ERL_NIF_TERM     recvRef);
3047 static ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv*       env,
3048                                       ESockDescriptor* descP,
3049                                       ssize_t          read,
3050                                       struct msghdr*   msgHdrP,
3051                                       ErlNifBinary*    dataBufP,
3052                                       ErlNifBinary*    ctrlBufP,
3053                                       ERL_NIF_TERM     sockRef);
3054 
3055 static ERL_NIF_TERM esock_finalize_close(ErlNifEnv*       env,
3056                                          ESockDescriptor* descP);
3057 static int esock_close_socket(ErlNifEnv*       env,
3058                               ESockDescriptor* descP,
3059                               BOOLEAN_T        unlock);
3060 
3061 static void encode_msg(ErlNifEnv*       env,
3062                        ESockDescriptor* descP,
3063                        ssize_t          read,
3064                        struct msghdr*   msgHdrP,
3065                        ErlNifBinary*    dataBufP,
3066                        ErlNifBinary*    ctrlBufP,
3067                        ERL_NIF_TERM*    eSockAddr);
3068 static void encode_cmsgs(ErlNifEnv*       env,
3069                          ESockDescriptor* descP,
3070                          ErlNifBinary*    cmsgBinP,
3071                          struct msghdr*   msgHdrP,
3072                          ERL_NIF_TERM*    eCMsg);
3073 static BOOLEAN_T encode_cmsg(ErlNifEnv*     env,
3074                              int            level,
3075                              int            type,
3076                              unsigned char* dataP,
3077                              size_t         dataLen,
3078                              ERL_NIF_TERM*  eType,
3079                              ERL_NIF_TERM*  eData);
3080 static BOOLEAN_T decode_cmsghdrs(ErlNifEnv*       env,
3081                                  ESockDescriptor* descP,
3082                                  ERL_NIF_TERM     eCMsg,
3083                                  char*            cmsgHdrBufP,
3084                                  size_t           cmsgHdrBufLen,
3085                                  size_t*          cmsgHdrBufUsed);
3086 static BOOLEAN_T decode_cmsghdr(ErlNifEnv*       env,
3087                                 ESockDescriptor* descP,
3088                                 ERL_NIF_TERM     eCMsg,
3089                                 char*            bufP,
3090                                 size_t           rem,
3091                                 size_t*          used);
3092 static BOOLEAN_T decode_cmsghdr_value(ErlNifEnv*       env,
3093                                       ESockDescriptor* descP,
3094                                       int              level,
3095                                       ERL_NIF_TERM     eType,
3096                                       ERL_NIF_TERM     eValue,
3097                                       char*            dataP,
3098                                       size_t           dataLen,
3099                                       size_t*          dataUsedP);
3100 static BOOLEAN_T decode_cmsghdr_data(ErlNifEnv*       env,
3101                                      ESockDescriptor* descP,
3102                                      int              level,
3103                                      ERL_NIF_TERM     eType,
3104                                      ERL_NIF_TERM     eData,
3105                                      char*            dataP,
3106                                      size_t           dataLen,
3107                                      size_t*          dataUsedP);
3108 static void *init_cmsghdr(struct cmsghdr* cmsgP,
3109                           size_t          rem,
3110                           size_t          size,
3111                           size_t*         usedP);
3112 static void encode_msg_flags(ErlNifEnv*       env,
3113                              ESockDescriptor* descP,
3114                              int              msgFlags,
3115                              ERL_NIF_TERM*    flags);
3116 #if defined(IP_TOS)
3117 static BOOLEAN_T decode_ip_tos(ErlNifEnv*   env,
3118                                ERL_NIF_TERM eVal,
3119                                int*         val);
3120 #endif
3121 #if defined(IP_MTU_DISCOVER)
3122 static BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv*   env,
3123                                     ERL_NIF_TERM eVal,
3124                                     int*         val);
3125 #endif
3126 #if defined(IP_MTU_DISCOVER)
3127 static void encode_ip_pmtudisc(ErlNifEnv*    env,
3128                                int           val,
3129                                ERL_NIF_TERM* eVal);
3130 #endif
3131 #if defined(IPV6_MTU_DISCOVER)
3132 static BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv*   env,
3133                                       ERL_NIF_TERM eVal,
3134                                       int*         val);
3135 #endif
3136 #if defined(IPV6_MTU_DISCOVER)
3137 static void encode_ipv6_pmtudisc(ErlNifEnv*    env,
3138                                  int           val,
3139                                  ERL_NIF_TERM* eVal);
3140 #endif
3141 #if defined(IPV6_MULTICAST_HOPS) || defined(IPV6_UNICAST_HOPS)
3142 static
3143 BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val);
3144 #endif
3145 
3146 /*
3147 static BOOLEAN_T decode_bool(ErlNifEnv*   env,
3148                              ERL_NIF_TERM eVal,
3149                              BOOLEAN_T*   val);
3150 */
3151 // static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal);
3152 static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val);
3153 
3154 #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
3155 static BOOLEAN_T decode_sctp_assoc_t(ErlNifEnv*    env,
3156                                      ERL_NIF_TERM  eVal,
3157                                      sctp_assoc_t* val);
3158 static ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env,
3159                                         sctp_assoc_t val);
3160 #endif // #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
3161 
3162 static void esock_stop_handle_current(ErlNifEnv*       env,
3163                                       const char*      role,
3164                                       ESockDescriptor* descP,
3165                                       ERL_NIF_TERM     sockRef,
3166                                       ESockRequestor*  reqP);
3167 static void inform_waiting_procs(ErlNifEnv*         env,
3168                                  const char*        role,
3169                                  ESockDescriptor*   descP,
3170                                  ERL_NIF_TERM       sockRef,
3171                                  ESockRequestQueue* q,
3172                                  ERL_NIF_TERM       reason);
3173 
3174 static int socket_setopt(int             sock,
3175                          int             level,
3176                          int             opt,
3177                          const void*     optVal,
3178                          const socklen_t optLen);
3179 
3180 static BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err);
3181 
3182 static ESockDescriptor* alloc_descriptor(SOCKET sock, ErlNifEvent event);
3183 
3184 
3185 static BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how);
3186 #ifdef HAVE_SETNS
3187 static BOOLEAN_T esock_open4_get_netns(ErlNifEnv*   env,
3188                                        ERL_NIF_TERM opts,
3189                                        char**       netns);
3190 static BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err);
3191 static BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err);
3192 #endif
3193 
3194 static BOOLEAN_T cnt_inc(ESockCounter* cnt, ESockCounter inc);
3195 static void      cnt_dec(ESockCounter* cnt, ESockCounter dec);
3196 
3197 static void inc_socket(int domain, int type, int protocol);
3198 static void dec_socket(int domain, int type, int protocol);
3199 
3200 
3201 
3202 /* *** activate_next_acceptor ***
3203  * *** activate_next_writer   ***
3204  * *** activate_next_reader   ***
3205  *
3206  * All the activate-next functions for acceptor, writer and reader
3207  * have exactly the same API, so we apply some macro magic to simplify.
3208  * They simply operates on dufferent data structures.
3209  *
3210  */
3211 
3212 #define ACTIVATE_NEXT_FUNCS_DEFS     \
3213     ACTIVATE_NEXT_FUNC_DEF(acceptor) \
3214     ACTIVATE_NEXT_FUNC_DEF(writer)   \
3215     ACTIVATE_NEXT_FUNC_DEF(reader)
3216 
3217 #define ACTIVATE_NEXT_FUNC_DEF(F)                                 \
3218     static BOOLEAN_T activate_next_##F(ErlNifEnv*       env,      \
3219                                        ESockDescriptor* descP,    \
3220                                        ERL_NIF_TERM     sockRef);
3221 ACTIVATE_NEXT_FUNCS_DEFS
3222 #undef ACTIVATE_NEXT_FUNC_DEF
3223 
3224 /* *** acceptor_search4pid | writer_search4pid | reader_search4pid ***
3225  * *** acceptor_push       | writer_push       | reader_push       ***
3226  * *** acceptor_pop        | writer_pop        | reader_pop        ***
3227  * *** acceptor_unqueue    | writer_unqueue    | reader_unqueue    ***
3228  *
3229  * All the queue operator functions (search4pid, push, pop
3230  * and unqueue) for acceptor, writer and reader has exactly
3231  * the same API, so we apply some macro magic to simplify.
3232  */
3233 
3234 #define ESOCK_OPERATOR_FUNCS_DEFS      \
3235     ESOCK_OPERATOR_FUNCS_DEF(acceptor) \
3236     ESOCK_OPERATOR_FUNCS_DEF(writer)   \
3237     ESOCK_OPERATOR_FUNCS_DEF(reader)
3238 
3239 #define ESOCK_OPERATOR_FUNCS_DEF(O)                            \
3240     static BOOLEAN_T O##_search4pid(ErlNifEnv*       env,      \
3241                                     ESockDescriptor* descP,    \
3242                                     ErlNifPid*       pid);     \
3243     static void O##_push(ErlNifEnv*       env,         \
3244                          ESockDescriptor* descP,               \
3245                          ErlNifPid        pid,                 \
3246                          ERL_NIF_TERM     ref);                \
3247     static BOOLEAN_T O##_pop(ErlNifEnv*       env,             \
3248                              ESockDescriptor* descP,           \
3249                              ESockRequestor*  reqP);           \
3250     static BOOLEAN_T O##_unqueue(ErlNifEnv*       env,         \
3251                                  ESockDescriptor* descP,       \
3252                                  ERL_NIF_TERM*    refP,        \
3253                                  const ErlNifPid* pidP);
3254 ESOCK_OPERATOR_FUNCS_DEFS
3255 #undef ESOCK_OPERATOR_FUNCS_DEF
3256 
3257 static BOOLEAN_T requestor_pop(ESockRequestQueue* q,
3258                                ESockRequestor*    reqP);
3259 
3260 static void requestor_init(ESockRequestor* reqP);
3261 static void requestor_release(const char*      slogan,
3262                               ErlNifEnv*       env,
3263                               ESockDescriptor* descP,
3264                               ESockRequestor*  reqP);
3265 
3266 static BOOLEAN_T qsearch4pid(ErlNifEnv*         env,
3267                              ESockRequestQueue* q,
3268                              ErlNifPid*         pid);
3269 static void qpush(ESockRequestQueue*        q,
3270                   ESockRequestQueueElement* e);
3271 static ESockRequestQueueElement* qpop(ESockRequestQueue* q);
3272 static BOOLEAN_T qunqueue(ErlNifEnv*         env,
3273                           ESockDescriptor*   descP,
3274                           const char*        slogan,
3275                           ESockRequestQueue* q,
3276                           ERL_NIF_TERM*      refP,
3277                           const ErlNifPid*   pidP);
3278 
3279 static int esock_monitor(const char*      slogan,
3280                          ErlNifEnv*       env,
3281                          ESockDescriptor* descP,
3282                          const ErlNifPid* pid,
3283                          ESockMonitor*    mon);
3284 static int esock_demonitor(const char*      slogan,
3285                            ErlNifEnv*       env,
3286                            ESockDescriptor* descP,
3287                            ESockMonitor*    monP);
3288 static void esock_monitor_init(ESockMonitor* mon);
3289 static ERL_NIF_TERM esock_make_monitor_term(ErlNifEnv*          env,
3290                                             const ESockMonitor* monP);
3291 static BOOLEAN_T esock_monitor_eq(const ESockMonitor* monP,
3292                                   const ErlNifMonitor* mon);
3293 
3294 
3295 
3296 static void esock_down_ctrl(ErlNifEnv*       env,
3297                             ESockDescriptor* descP,
3298                             const ErlNifPid* pidP);
3299 static void esock_down_acceptor(ErlNifEnv*       env,
3300                                 ESockDescriptor* descP,
3301                                 ERL_NIF_TERM     sockRef,
3302                                 const ErlNifPid* pidP,
3303                                 const ErlNifMonitor* monP);
3304 static void esock_down_writer(ErlNifEnv*       env,
3305                               ESockDescriptor* descP,
3306                               ERL_NIF_TERM     sockRef,
3307                               const ErlNifPid* pidP,
3308                               const ErlNifMonitor* monP);
3309 static void esock_down_reader(ErlNifEnv*       env,
3310                               ESockDescriptor* descP,
3311                               ERL_NIF_TERM     sockRef,
3312                               const ErlNifPid* pidP,
3313                               const ErlNifMonitor* monP);
3314 
3315 static void esock_send_reg_add_msg(ErlNifEnv*   env,
3316                                    ESockDescriptor* descP,
3317                                    ERL_NIF_TERM sockRef);
3318 static void esock_send_reg_del_msg(ErlNifEnv*   env,
3319                                    ESockDescriptor* descP,
3320                                    ERL_NIF_TERM sockRef);
3321 
3322 static void esock_send_wrap_msg(ErlNifEnv*       env,
3323                                 ESockDescriptor* descP,
3324                                 ERL_NIF_TERM     sockRef,
3325                                 ERL_NIF_TERM     cnt);
3326 static void esock_send_close_msg(ErlNifEnv*       env,
3327                                  ESockDescriptor* descP,
3328                                  ErlNifPid*       pid);
3329 #ifdef HAVE_SENDFILE
3330 static void
3331 esock_send_sendfile_deferred_close_msg(ErlNifEnv*       env,
3332                                        ESockDescriptor* descP);
3333 #endif
3334 static void esock_send_abort_msg(ErlNifEnv*       env,
3335                                  ESockDescriptor* descP,
3336                                  ERL_NIF_TERM     sockRef,
3337                                  ESockRequestor*  reqP,
3338                                  ERL_NIF_TERM     reason);
3339 static BOOLEAN_T esock_send_msg(ErlNifEnv*   env,
3340                                 ErlNifPid*   pid,
3341                                 ERL_NIF_TERM msg,
3342                                 ErlNifEnv*   msgEnv);
3343 
3344 static ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv*   env,
3345                                    ERL_NIF_TERM sockRef);
3346 static ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv*   env,
3347                                    ERL_NIF_TERM sockRef);
3348 static ERL_NIF_TERM mk_reg_msg(ErlNifEnv*   env,
3349                                ERL_NIF_TERM tag,
3350                                ERL_NIF_TERM sockRef);
3351 static ERL_NIF_TERM mk_abort_msg(ErlNifEnv*   env,
3352                                  ERL_NIF_TERM sockRef,
3353                                  ERL_NIF_TERM opRef,
3354                                  ERL_NIF_TERM reason);
3355 static ERL_NIF_TERM mk_wrap_msg(ErlNifEnv*   env,
3356                                 ERL_NIF_TERM sockRef,
3357                                 ERL_NIF_TERM cnt);
3358 static ERL_NIF_TERM mk_close_msg(ErlNifEnv*   env,
3359                                  ERL_NIF_TERM sockRef,
3360                                  ERL_NIF_TERM closeRef);
3361 static ERL_NIF_TERM mk_select_msg(ErlNifEnv*   env,
3362                                   ERL_NIF_TERM sockRef,
3363                                   ERL_NIF_TERM selectRef);
3364 static ERL_NIF_TERM mk_socket_msg(ErlNifEnv*   env,
3365                                   ERL_NIF_TERM sockRef,
3366                                   ERL_NIF_TERM tag,
3367                                   ERL_NIF_TERM info);
3368 static ERL_NIF_TERM mk_socket(ErlNifEnv*   env,
3369                               ERL_NIF_TERM sockRef);
3370 
3371 static int esock_select_read(ErlNifEnv*       env,
3372                              ErlNifEvent      event,
3373                              void*            obj,
3374                              const ErlNifPid* pidP,
3375                              ERL_NIF_TERM     sockRef,
3376                              ERL_NIF_TERM     selectRef);
3377 static int esock_select_write(ErlNifEnv*       env,
3378                               ErlNifEvent      event,
3379                               void*            obj,
3380                               const ErlNifPid* pidP,
3381                               ERL_NIF_TERM     sockRef,
3382                               ERL_NIF_TERM     selectRef);
3383 static int esock_select_stop(ErlNifEnv*  env,
3384                              ErlNifEvent event,
3385                              void*       obj);
3386 static int esock_select_cancel(ErlNifEnv*             env,
3387                                ErlNifEvent            event,
3388                                enum ErlNifSelectFlags mode,
3389                                void*                  obj);
3390 
3391 static char* extract_debug_filename(ErlNifEnv*   env,
3392                                     ERL_NIF_TERM map);
3393 
3394 
3395 /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *
3396  *                                                                        *
3397  *                                                                        *
3398  * End of non-__WIN32__ section a.k.a UNIX section                        *
3399  *                                                                        *
3400  *                                                                        *
3401  * ---------------------------------------------------------------------- */
3402 #endif // #ifndef __WIN32__
3403 
3404 
3405 /*
3406 #if defined(HAS_AF_LOCAL) || defined(SO_BINDTODEVICE)
3407 static size_t my_strnlen(const char *s, size_t maxlen);
3408 #endif
3409 */
3410 
3411 static void esock_dtor(ErlNifEnv* env, void* obj);
3412 static void esock_stop(ErlNifEnv* env,
3413                        void*      obj,
3414                        ErlNifEvent fd,
3415                        int        is_direct_call);
3416 static void esock_down(ErlNifEnv*           env,
3417                        void*                obj,
3418                        const ErlNifPid*     pidP,
3419                        const ErlNifMonitor* monP);
3420 
3421 static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info);
3422 
3423 
3424 
3425 #if HAVE_IN6
3426 #  if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY
3427 #    if HAVE_DECL_IN6ADDR_ANY_INIT
3428 static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } };
3429 #    else
3430 static const struct in6_addr in6addr_any =
3431     { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
3432 #    endif /* HAVE_IN6ADDR_ANY_INIT */
3433 #  endif /* ! HAVE_DECL_IN6ADDR_ANY */
3434 
3435 #  if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK
3436 #    if HAVE_DECL_IN6ADDR_LOOPBACK_INIT
3437 static const struct in6_addr in6addr_loopback =
3438     { { IN6ADDR_LOOPBACK_INIT } };
3439 #    else
3440 static const struct in6_addr in6addr_loopback =
3441     { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } };
3442 #    endif /* HAVE_IN6ADDR_LOOPBACk_INIT */
3443 #  endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */
3444 #endif /* HAVE_IN6 */
3445 
3446 
3447 
3448 /* *** Global atoms ***
3449  * Note that when an (global) atom is added here, it must also be added
3450  * in the socket_int.h file!
3451  */
3452 #define GLOBAL_ATOMS                                   \
3453     GLOBAL_ATOM_DECL(abort);                           \
3454     GLOBAL_ATOM_DECL(accept);                          \
3455     GLOBAL_ATOM_DECL(acceptconn);                      \
3456     GLOBAL_ATOM_DECL(acceptfilter);                    \
3457     GLOBAL_ATOM_DECL(adaption_layer);                  \
3458     GLOBAL_ATOM_DECL(addr);                            \
3459     GLOBAL_ATOM_DECL(addrform);                        \
3460     GLOBAL_ATOM_DECL(add_membership);                  \
3461     GLOBAL_ATOM_DECL(add_source_membership);           \
3462     GLOBAL_ATOM_DECL(any);                             \
3463     GLOBAL_ATOM_DECL(associnfo);                       \
3464     GLOBAL_ATOM_DECL(authhdr);                         \
3465     GLOBAL_ATOM_DECL(auth_active_key);                 \
3466     GLOBAL_ATOM_DECL(auth_asconf);                     \
3467     GLOBAL_ATOM_DECL(auth_chunk);                      \
3468     GLOBAL_ATOM_DECL(auth_delete_key);                 \
3469     GLOBAL_ATOM_DECL(auth_key);                        \
3470     GLOBAL_ATOM_DECL(auth_level);                      \
3471     GLOBAL_ATOM_DECL(autoclose);                       \
3472     GLOBAL_ATOM_DECL(bad_data);                        \
3473     GLOBAL_ATOM_DECL(bindtodevice);                    \
3474     GLOBAL_ATOM_DECL(block_source);                    \
3475     GLOBAL_ATOM_DECL(broadcast);                       \
3476     GLOBAL_ATOM_DECL(busy_poll);                       \
3477     GLOBAL_ATOM_DECL(checksum);                        \
3478     GLOBAL_ATOM_DECL(close);                           \
3479     GLOBAL_ATOM_DECL(cmsg_cloexec);                    \
3480     GLOBAL_ATOM_DECL(command);                         \
3481     GLOBAL_ATOM_DECL(confirm);                         \
3482     GLOBAL_ATOM_DECL(congestion);                      \
3483     GLOBAL_ATOM_DECL(connect);                         \
3484     GLOBAL_ATOM_DECL(context);                         \
3485     GLOBAL_ATOM_DECL(cork);                            \
3486     GLOBAL_ATOM_DECL(credentials);                     \
3487     GLOBAL_ATOM_DECL(ctrl);                            \
3488     GLOBAL_ATOM_DECL(ctrunc);                          \
3489     GLOBAL_ATOM_DECL(data);                            \
3490     GLOBAL_ATOM_DECL(debug);                           \
3491     GLOBAL_ATOM_DECL(default);                         \
3492     GLOBAL_ATOM_DECL(default_send_params);             \
3493     GLOBAL_ATOM_DECL(delayed_ack_time);                \
3494     GLOBAL_ATOM_DECL(dgram);                           \
3495     GLOBAL_ATOM_DECL(disable_fragments);               \
3496     GLOBAL_ATOM_DECL(domain);                          \
3497     GLOBAL_ATOM_DECL(dontfrag);                        \
3498     GLOBAL_ATOM_DECL(dontroute);                       \
3499     GLOBAL_ATOM_DECL(drop_membership);                 \
3500     GLOBAL_ATOM_DECL(drop_source_membership);          \
3501     GLOBAL_ATOM_DECL(dstopts);                         \
3502     GLOBAL_ATOM_DECL(egp);                             \
3503     GLOBAL_ATOM_DECL(enotsup);                         \
3504     GLOBAL_ATOM_DECL(eor);                             \
3505     GLOBAL_ATOM_DECL(error);                           \
3506     GLOBAL_ATOM_DECL(errqueue);                        \
3507     GLOBAL_ATOM_DECL(esp_network_level);               \
3508     GLOBAL_ATOM_DECL(esp_trans_level);                 \
3509     GLOBAL_ATOM_DECL(events);                          \
3510     GLOBAL_ATOM_DECL(explicit_eor);                    \
3511     GLOBAL_ATOM_DECL(faith);                           \
3512     GLOBAL_ATOM_DECL(false);                           \
3513     GLOBAL_ATOM_DECL(family);                          \
3514     GLOBAL_ATOM_DECL(fastroute);                       \
3515     GLOBAL_ATOM_DECL(flags);                           \
3516     GLOBAL_ATOM_DECL(flowinfo);                        \
3517     GLOBAL_ATOM_DECL(fragment_interleave);             \
3518     GLOBAL_ATOM_DECL(freebind);                        \
3519     GLOBAL_ATOM_DECL(get_peer_addr_info);              \
3520     GLOBAL_ATOM_DECL(hatype);                          \
3521     GLOBAL_ATOM_DECL(hdrincl);                         \
3522     GLOBAL_ATOM_DECL(hmac_ident);                      \
3523     GLOBAL_ATOM_DECL(hoplimit);                        \
3524     GLOBAL_ATOM_DECL(hopopts);                         \
3525     GLOBAL_ATOM_DECL(host);                            \
3526     GLOBAL_ATOM_DECL(icmp);                            \
3527     GLOBAL_ATOM_DECL(icmp6);                           \
3528     GLOBAL_ATOM_DECL(ifindex);                         \
3529     GLOBAL_ATOM_DECL(igmp);                            \
3530     GLOBAL_ATOM_DECL(inet);                            \
3531     GLOBAL_ATOM_DECL(inet6);                           \
3532     GLOBAL_ATOM_DECL(info);                            \
3533     GLOBAL_ATOM_DECL(initmsg);                         \
3534     GLOBAL_ATOM_DECL(invalid);                         \
3535     GLOBAL_ATOM_DECL(integer_range);                   \
3536     GLOBAL_ATOM_DECL(iov);                             \
3537     GLOBAL_ATOM_DECL(ip);                              \
3538     GLOBAL_ATOM_DECL(ipcomp_level);                    \
3539     GLOBAL_ATOM_DECL(ipip);                            \
3540     GLOBAL_ATOM_DECL(ipv6);                            \
3541     GLOBAL_ATOM_DECL(i_want_mapped_v4_addr);           \
3542     GLOBAL_ATOM_DECL(join_group);                      \
3543     GLOBAL_ATOM_DECL(keepalive);                       \
3544     GLOBAL_ATOM_DECL(keepcnt);                         \
3545     GLOBAL_ATOM_DECL(keepidle);                        \
3546     GLOBAL_ATOM_DECL(keepintvl);                       \
3547     GLOBAL_ATOM_DECL(kernel);                          \
3548     GLOBAL_ATOM_DECL(leave_group);                     \
3549     GLOBAL_ATOM_DECL(level);                           \
3550     GLOBAL_ATOM_DECL(linger);                          \
3551     GLOBAL_ATOM_DECL(local);                           \
3552     GLOBAL_ATOM_DECL(local_auth_chunks);               \
3553     GLOBAL_ATOM_DECL(loopback);                        \
3554     GLOBAL_ATOM_DECL(lowdelay);                        \
3555     GLOBAL_ATOM_DECL(mark);                            \
3556     GLOBAL_ATOM_DECL(maxburst);                        \
3557     GLOBAL_ATOM_DECL(maxseg);                          \
3558     GLOBAL_ATOM_DECL(md5sig);                          \
3559     GLOBAL_ATOM_DECL(mincost);                         \
3560     GLOBAL_ATOM_DECL(minttl);                          \
3561     GLOBAL_ATOM_DECL(more);                            \
3562     GLOBAL_ATOM_DECL(msfilter);                        \
3563     GLOBAL_ATOM_DECL(mtu);                             \
3564     GLOBAL_ATOM_DECL(mtu_discover);                    \
3565     GLOBAL_ATOM_DECL(multicast);                       \
3566     GLOBAL_ATOM_DECL(multicast_all);                   \
3567     GLOBAL_ATOM_DECL(multicast_hops);                  \
3568     GLOBAL_ATOM_DECL(multicast_if);                    \
3569     GLOBAL_ATOM_DECL(multicast_loop);                  \
3570     GLOBAL_ATOM_DECL(multicast_ttl);                   \
3571     GLOBAL_ATOM_DECL(nodelay);                         \
3572     GLOBAL_ATOM_DECL(nodefrag);                        \
3573     GLOBAL_ATOM_DECL(noopt);                           \
3574     GLOBAL_ATOM_DECL(nopush);                          \
3575     GLOBAL_ATOM_DECL(nosignal);                        \
3576     GLOBAL_ATOM_DECL(not_found);                       \
3577     GLOBAL_ATOM_DECL(not_owner);                       \
3578     GLOBAL_ATOM_DECL(ok);                              \
3579     GLOBAL_ATOM_DECL(oob);                             \
3580     GLOBAL_ATOM_DECL(oobinline);                       \
3581     GLOBAL_ATOM_DECL(options);                         \
3582     GLOBAL_ATOM_DECL(origdstaddr);                     \
3583     GLOBAL_ATOM_DECL(otherhost);                       \
3584     GLOBAL_ATOM_DECL(outgoing);                        \
3585     GLOBAL_ATOM_DECL(packet);                          \
3586     GLOBAL_ATOM_DECL(partial_delivery_point);          \
3587     GLOBAL_ATOM_DECL(passcred);                        \
3588     GLOBAL_ATOM_DECL(path);                            \
3589     GLOBAL_ATOM_DECL(peek);                            \
3590     GLOBAL_ATOM_DECL(peek_off);                        \
3591     GLOBAL_ATOM_DECL(peer_addr_params);                \
3592     GLOBAL_ATOM_DECL(peer_auth_chunks);                \
3593     GLOBAL_ATOM_DECL(peercred);                        \
3594     GLOBAL_ATOM_DECL(pktinfo);                         \
3595     GLOBAL_ATOM_DECL(pktoptions);                      \
3596     GLOBAL_ATOM_DECL(pkttype);                         \
3597     GLOBAL_ATOM_DECL(port);                            \
3598     GLOBAL_ATOM_DECL(portrange);                       \
3599     GLOBAL_ATOM_DECL(primary_addr);                    \
3600     GLOBAL_ATOM_DECL(priority);                        \
3601     GLOBAL_ATOM_DECL(protocol);                        \
3602     GLOBAL_ATOM_DECL(raw);                             \
3603     GLOBAL_ATOM_DECL(rcvbuf);                          \
3604     GLOBAL_ATOM_DECL(rcvbufforce);                     \
3605     GLOBAL_ATOM_DECL(rcvlowat);                        \
3606     GLOBAL_ATOM_DECL(rcvtimeo);                        \
3607     GLOBAL_ATOM_DECL(rdm);                             \
3608     GLOBAL_ATOM_DECL(recv);                            \
3609     GLOBAL_ATOM_DECL(recvdstaddr);                     \
3610     GLOBAL_ATOM_DECL(recverr);                         \
3611     GLOBAL_ATOM_DECL(recvfrom);                        \
3612     GLOBAL_ATOM_DECL(recvhoplimit);                    \
3613     GLOBAL_ATOM_DECL(recvif);                          \
3614     GLOBAL_ATOM_DECL(recvmsg);                         \
3615     GLOBAL_ATOM_DECL(recvopts);                        \
3616     GLOBAL_ATOM_DECL(recvorigdstaddr);                 \
3617     GLOBAL_ATOM_DECL(recvpktinfo);                     \
3618     GLOBAL_ATOM_DECL(recvtclass);                      \
3619     GLOBAL_ATOM_DECL(recvtos);                         \
3620     GLOBAL_ATOM_DECL(recvttl);                         \
3621     GLOBAL_ATOM_DECL(reliability);                     \
3622     GLOBAL_ATOM_DECL(reset_streams);                   \
3623     GLOBAL_ATOM_DECL(retopts);                         \
3624     GLOBAL_ATOM_DECL(reuseaddr);                       \
3625     GLOBAL_ATOM_DECL(reuseport);                       \
3626     GLOBAL_ATOM_DECL(rights);                          \
3627     GLOBAL_ATOM_DECL(router_alert);                    \
3628     GLOBAL_ATOM_DECL(rthdr);                           \
3629     GLOBAL_ATOM_DECL(rtoinfo);                         \
3630     GLOBAL_ATOM_DECL(rxq_ovfl);                        \
3631     GLOBAL_ATOM_DECL(scope_id);                        \
3632     GLOBAL_ATOM_DECL(sctp);                            \
3633     GLOBAL_ATOM_DECL(sec);                             \
3634     GLOBAL_ATOM_DECL(select_failed);                   \
3635     GLOBAL_ATOM_DECL(select_sent);                     \
3636     GLOBAL_ATOM_DECL(send);                            \
3637     GLOBAL_ATOM_DECL(sendmsg);                         \
3638     GLOBAL_ATOM_DECL(sendsrcaddr);                     \
3639     GLOBAL_ATOM_DECL(sendto);                          \
3640     GLOBAL_ATOM_DECL(seqpacket);                       \
3641     GLOBAL_ATOM_DECL(setfib);                          \
3642     GLOBAL_ATOM_DECL(set_peer_primary_addr);           \
3643     GLOBAL_ATOM_DECL(socket);                          \
3644     GLOBAL_ATOM_DECL(sndbuf);                          \
3645     GLOBAL_ATOM_DECL(sndbufforce);                     \
3646     GLOBAL_ATOM_DECL(sndlowat);                        \
3647     GLOBAL_ATOM_DECL(sndtimeo);                        \
3648     GLOBAL_ATOM_DECL(spec_dst);                        \
3649     GLOBAL_ATOM_DECL(status);                          \
3650     GLOBAL_ATOM_DECL(stream);                          \
3651     GLOBAL_ATOM_DECL(syncnt);                          \
3652     GLOBAL_ATOM_DECL(tclass);                          \
3653     GLOBAL_ATOM_DECL(tcp);                             \
3654     GLOBAL_ATOM_DECL(throughput);                      \
3655     GLOBAL_ATOM_DECL(timestamp);                       \
3656     GLOBAL_ATOM_DECL(tos);                             \
3657     GLOBAL_ATOM_DECL(transparent);                     \
3658     GLOBAL_ATOM_DECL(true);                            \
3659     GLOBAL_ATOM_DECL(trunc);                           \
3660     GLOBAL_ATOM_DECL(ttl);                             \
3661     GLOBAL_ATOM_DECL(type);                            \
3662     GLOBAL_ATOM_DECL(udp);                             \
3663     GLOBAL_ATOM_DECL(unblock_source);                  \
3664     GLOBAL_ATOM_DECL(undefined);                       \
3665     GLOBAL_ATOM_DECL(unicast_hops);                    \
3666     GLOBAL_ATOM_DECL(unspec);                          \
3667     GLOBAL_ATOM_DECL(usec);                            \
3668     GLOBAL_ATOM_DECL(user);                            \
3669     GLOBAL_ATOM_DECL(user_timeout);                    \
3670     GLOBAL_ATOM_DECL(use_ext_recvinfo);                \
3671     GLOBAL_ATOM_DECL(use_min_mtu);                     \
3672     GLOBAL_ATOM_DECL(v6only)
3673 
3674 
3675 /* *** Global error reason atoms *** */
3676 #define GLOBAL_ERROR_REASON_ATOMS   \
3677     GLOBAL_ATOM_DECL(eagain);       \
3678     GLOBAL_ATOM_DECL(einval)
3679 
3680 
3681 #define GLOBAL_ATOM_DECL(A) ERL_NIF_TERM esock_atom_##A
3682 GLOBAL_ATOMS;
3683 GLOBAL_ERROR_REASON_ATOMS;
3684 #undef GLOBAL_ATOM_DECL
3685 ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket')
3686 
3687 /* *** Local atoms *** */
3688 #define LOCAL_ATOMS                    \
3689     LOCAL_ATOM_DECL(accepting);	       \
3690     LOCAL_ATOM_DECL(acc_success);      \
3691     LOCAL_ATOM_DECL(acc_fails);        \
3692     LOCAL_ATOM_DECL(acc_tries);        \
3693     LOCAL_ATOM_DECL(acc_waits);        \
3694     LOCAL_ATOM_DECL(adaptation_layer); \
3695     LOCAL_ATOM_DECL(add);              \
3696     LOCAL_ATOM_DECL(addr_unreach);     \
3697     LOCAL_ATOM_DECL(address);          \
3698     LOCAL_ATOM_DECL(adm_prohibited);   \
3699     LOCAL_ATOM_DECL(already);          \
3700     LOCAL_ATOM_DECL(association);      \
3701     LOCAL_ATOM_DECL(assoc_id);         \
3702     LOCAL_ATOM_DECL(authentication);   \
3703     LOCAL_ATOM_DECL(boolean);          \
3704     LOCAL_ATOM_DECL(bound);	       \
3705     LOCAL_ATOM_DECL(bufsz);            \
3706     LOCAL_ATOM_DECL(close);            \
3707     LOCAL_ATOM_DECL(closed);           \
3708     LOCAL_ATOM_DECL(closing);          \
3709     LOCAL_ATOM_DECL(code);             \
3710     LOCAL_ATOM_DECL(connected);        \
3711     LOCAL_ATOM_DECL(connecting);       \
3712     LOCAL_ATOM_DECL(cookie_life);      \
3713     LOCAL_ATOM_DECL(counter_wrap);     \
3714     LOCAL_ATOM_DECL(counters);         \
3715     LOCAL_ATOM_DECL(ctype);            \
3716     LOCAL_ATOM_DECL(data_io);          \
3717     LOCAL_ATOM_DECL(data_size);        \
3718     LOCAL_ATOM_DECL(debug_filename);   \
3719     LOCAL_ATOM_DECL(del);              \
3720     LOCAL_ATOM_DECL(dest_unreach);     \
3721     LOCAL_ATOM_DECL(do);               \
3722     LOCAL_ATOM_DECL(dont);             \
3723     LOCAL_ATOM_DECL(dtor);             \
3724     LOCAL_ATOM_DECL(dup);              \
3725     LOCAL_ATOM_DECL(efile);            \
3726     LOCAL_ATOM_DECL(exclude);          \
3727     LOCAL_ATOM_DECL(false);            \
3728     LOCAL_ATOM_DECL(frag_needed);      \
3729     LOCAL_ATOM_DECL(host_unknown);     \
3730     LOCAL_ATOM_DECL(host_unreach);     \
3731     LOCAL_ATOM_DECL(how);              \
3732     LOCAL_ATOM_DECL(in4_sockaddr);     \
3733     LOCAL_ATOM_DECL(in6_sockaddr);     \
3734     LOCAL_ATOM_DECL(include);          \
3735     LOCAL_ATOM_DECL(initial);          \
3736     LOCAL_ATOM_DECL(interface);        \
3737     LOCAL_ATOM_DECL(integer);          \
3738     LOCAL_ATOM_DECL(iov_max);          \
3739     LOCAL_ATOM_DECL(iow);              \
3740     LOCAL_ATOM_DECL(listening);	       \
3741     LOCAL_ATOM_DECL(local_rwnd);       \
3742     LOCAL_ATOM_DECL(max);              \
3743     LOCAL_ATOM_DECL(max_attempts);     \
3744     LOCAL_ATOM_DECL(max_init_timeo);   \
3745     LOCAL_ATOM_DECL(max_instreams);    \
3746     LOCAL_ATOM_DECL(asocmaxrxt);       \
3747     LOCAL_ATOM_DECL(min);              \
3748     LOCAL_ATOM_DECL(missing);          \
3749     LOCAL_ATOM_DECL(mode);             \
3750     LOCAL_ATOM_DECL(msg);              \
3751     LOCAL_ATOM_DECL(msg_flags);        \
3752     LOCAL_ATOM_DECL(multiaddr);        \
3753     LOCAL_ATOM_DECL(net_unknown);      \
3754     LOCAL_ATOM_DECL(net_unreach);      \
3755     LOCAL_ATOM_DECL(netns);            \
3756     LOCAL_ATOM_DECL(none);             \
3757     LOCAL_ATOM_DECL(noroute);          \
3758     LOCAL_ATOM_DECL(not_neighbour);    \
3759     LOCAL_ATOM_DECL(null);             \
3760     LOCAL_ATOM_DECL(num_acceptors);    \
3761     LOCAL_ATOM_DECL(num_cnt_bits);     \
3762     LOCAL_ATOM_DECL(num_dinet);        \
3763     LOCAL_ATOM_DECL(num_dinet6);       \
3764     LOCAL_ATOM_DECL(num_dlocal);       \
3765     LOCAL_ATOM_DECL(num_outstreams);   \
3766     LOCAL_ATOM_DECL(number_peer_destinations); \
3767     LOCAL_ATOM_DECL(num_pip);          \
3768     LOCAL_ATOM_DECL(num_psctp);        \
3769     LOCAL_ATOM_DECL(num_ptcp);         \
3770     LOCAL_ATOM_DECL(num_pudp);         \
3771     LOCAL_ATOM_DECL(num_readers);      \
3772     LOCAL_ATOM_DECL(num_sockets);      \
3773     LOCAL_ATOM_DECL(num_tdgrams);      \
3774     LOCAL_ATOM_DECL(num_tseqpkgs);     \
3775     LOCAL_ATOM_DECL(num_tstreams);     \
3776     LOCAL_ATOM_DECL(num_writers);      \
3777     LOCAL_ATOM_DECL(offender);         \
3778     LOCAL_ATOM_DECL(onoff);            \
3779     LOCAL_ATOM_DECL(options);          \
3780     LOCAL_ATOM_DECL(origin);           \
3781     LOCAL_ATOM_DECL(otp);              \
3782     LOCAL_ATOM_DECL(otp_socket_option);\
3783     LOCAL_ATOM_DECL(owner);            \
3784     LOCAL_ATOM_DECL(partial_delivery); \
3785     LOCAL_ATOM_DECL(peer_error);       \
3786     LOCAL_ATOM_DECL(peer_rwnd);        \
3787     LOCAL_ATOM_DECL(pkt_toobig);       \
3788     LOCAL_ATOM_DECL(policy_fail);      \
3789     LOCAL_ATOM_DECL(port_unreach);     \
3790     LOCAL_ATOM_DECL(prim_file);        \
3791     LOCAL_ATOM_DECL(probe);            \
3792     LOCAL_ATOM_DECL(protocols);        \
3793     LOCAL_ATOM_DECL(rcvctrlbuf);       \
3794     LOCAL_ATOM_DECL(read);             \
3795     LOCAL_ATOM_DECL(read_byte);        \
3796     LOCAL_ATOM_DECL(read_fails);       \
3797     LOCAL_ATOM_DECL(read_pkg);         \
3798     LOCAL_ATOM_DECL(read_pkg_max);     \
3799     LOCAL_ATOM_DECL(read_tries);       \
3800     LOCAL_ATOM_DECL(read_waits);       \
3801     LOCAL_ATOM_DECL(read_write);       \
3802     LOCAL_ATOM_DECL(registry);         \
3803     LOCAL_ATOM_DECL(reject_route);     \
3804     LOCAL_ATOM_DECL(remote);           \
3805     LOCAL_ATOM_DECL(rstates);          \
3806     LOCAL_ATOM_DECL(select);           \
3807     LOCAL_ATOM_DECL(selected);         \
3808     LOCAL_ATOM_DECL(sender_dry);       \
3809     LOCAL_ATOM_DECL(send_failure);     \
3810     LOCAL_ATOM_DECL(sendfile);         \
3811     LOCAL_ATOM_DECL(sendfile_byte);    \
3812     LOCAL_ATOM_DECL(sendfile_deferred_close); \
3813     LOCAL_ATOM_DECL(sendfile_fails);   \
3814     LOCAL_ATOM_DECL(sendfile_max);     \
3815     LOCAL_ATOM_DECL(sendfile_pkg);     \
3816     LOCAL_ATOM_DECL(sendfile_pkg_max); \
3817     LOCAL_ATOM_DECL(sendfile_tries);   \
3818     LOCAL_ATOM_DECL(sendfile_waits);   \
3819     LOCAL_ATOM_DECL(shutdown);         \
3820     LOCAL_ATOM_DECL(slist);            \
3821     LOCAL_ATOM_DECL(sndctrlbuf);       \
3822     LOCAL_ATOM_DECL(sockaddr);         \
3823     LOCAL_ATOM_DECL(socket_debug);     \
3824     LOCAL_ATOM_DECL(socket_level);     \
3825     LOCAL_ATOM_DECL(socket_option);    \
3826     LOCAL_ATOM_DECL(sourceaddr);       \
3827     LOCAL_ATOM_DECL(state);            \
3828     LOCAL_ATOM_DECL(time_exceeded);    \
3829     LOCAL_ATOM_DECL(timeout);          \
3830     LOCAL_ATOM_DECL(true);             \
3831     LOCAL_ATOM_DECL(txstatus);         \
3832     LOCAL_ATOM_DECL(txtime);           \
3833     LOCAL_ATOM_DECL(use_registry);     \
3834     LOCAL_ATOM_DECL(value);            \
3835     LOCAL_ATOM_DECL(want);             \
3836     LOCAL_ATOM_DECL(write);            \
3837     LOCAL_ATOM_DECL(write_byte);       \
3838     LOCAL_ATOM_DECL(write_fails);      \
3839     LOCAL_ATOM_DECL(write_pkg);        \
3840     LOCAL_ATOM_DECL(write_pkg_max);    \
3841     LOCAL_ATOM_DECL(write_tries);      \
3842     LOCAL_ATOM_DECL(write_waits);      \
3843     LOCAL_ATOM_DECL(wstates);          \
3844     LOCAL_ATOM_DECL(zero);             \
3845     LOCAL_ATOM_DECL(zerocopy)
3846 
3847 /* Local error reason atoms */
3848 #define LOCAL_ERROR_REASON_ATOMS                \
3849     LOCAL_ATOM_DECL(select_read);               \
3850     LOCAL_ATOM_DECL(select_write)
3851 
3852 #define LOCAL_ATOM_DECL(LA) static ERL_NIF_TERM atom_##LA
3853 LOCAL_ATOMS;
3854 LOCAL_ERROR_REASON_ATOMS;
3855 #undef LOCAL_ATOM_DECL
3856 
3857 
3858 /* *** Sockets *** */
3859 static ErlNifResourceType*    esocks;
3860 static ErlNifResourceTypeInit esockInit = {
3861    esock_dtor,
3862    esock_stop,
3863    (ErlNifResourceDown*) esock_down
3864 };
3865 
3866 // Initiated when the nif is loaded
3867 static ESockData data;
3868 
3869 
3870 /* These two (inline) functions are primarily intended for debugging,
3871  * that is, to make it easy to add debug printouts.
3872  */
esock_free_env(const char * slogan,ErlNifEnv * env)3873 static ESOCK_INLINE void esock_free_env(const char* slogan, ErlNifEnv* env)
3874 {
3875     SGDBG( ("SOCKET", "env free - %s: 0x%lX\r\n", slogan, env) );
3876     // esock_dbg_printf("SOCK ENV", "free - %s: 0x%lX\r\n", slogan, env);
3877 
3878     if (env != NULL) enif_free_env(env);
3879 }
3880 
3881 
esock_alloc_env(const char * slogan)3882 static ESOCK_INLINE ErlNifEnv* esock_alloc_env(const char* slogan)
3883 {
3884     ErlNifEnv* env;
3885 
3886     ESOCK_ASSERT( (env = enif_alloc_env()) != NULL);
3887 
3888     SGDBG( ("SOCKET", "env alloc - %s: 0x%lX\r\n", slogan, env) );
3889     // esock_dbg_printf("SOCK ENV", "alloc - %s: 0x%lX\r\n", slogan, env);
3890 
3891     return env;
3892 }
3893 
3894 
3895 /* ----------------------------------------------------------------------
3896  *  N I F   F u n c t i o n s
3897  * ----------------------------------------------------------------------
3898  *
3899  * Utility and admin functions:
3900  * ----------------------------
3901  * nif_info/0
3902  * nif_command/1
3903  * nif_supports/1
3904  *
3905  * The "proper" socket functions:
3906  * ------------------------------
3907  * nif_open(FD, Extra)
3908  * nif_open(Domain, Type, Protocol, Extra)
3909  * nif_bind(Sock, LocalAddr)
3910  * nif_connect(Sock, SockAddr)
3911  * nif_listen(Sock, Backlog)
3912  * nif_accept(LSock, Ref)
3913  * nif_send(Sock, SendRef, Data, Flags)
3914  * nif_sendto(Sock, SendRef, Data, Dest, Flags)
3915  * nif_sendmsg(Sock, SendRef, Msg, Flags)
3916  * nif_sendfile(Sock, SendRef, Offset, Count, InFileRef)
3917  * nif_sendfile(Sock, SendRef, Offset, Count)
3918  * nif_sendfile(Sock)
3919  * nif_recv(Sock, Length, Flags, RecvRef)
3920  * nif_recvfrom(Sock, RecvRef, BufSz, Flags)
3921  * nif_recvmsg(Sock, RecvRef, BufSz, CtrlSz, Flags)
3922  * nif_close(Sock)
3923  * nif_shutdown(Sock, How)
3924  * nif_sockname(Sock)
3925  * nif_peername(Sock)
3926  *
3927  * And some functions to manipulate and retrieve socket options:
3928  * -------------------------------------------------------------
3929  * nif_setopt/5
3930  * nif_getopt/3,4
3931  *
3932  * And some utility functions:
3933  * -------------------------------------------------------------
3934  *
3935  * And some socket admin functions:
3936  * -------------------------------------------------------------
3937  * nif_cancel(Sock, Ref)
3938  */
3939 
3940 
3941 /* ----------------------------------------------------------------------
3942  * nif_info
3943  *
3944  * Description:
3945  * This is currently just a placeholder...
3946  */
3947 
3948 static
nif_info(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])3949 ERL_NIF_TERM nif_info(ErlNifEnv*         env,
3950                       int                argc,
3951                       const ERL_NIF_TERM argv[])
3952 {
3953 #ifdef __WIN32__
3954     return enif_raise_exception(env, MKA(env, "notsup"));
3955 #else
3956     ERL_NIF_TERM info;
3957     ESockDescriptor* descP;
3958 
3959     SGDBG( ("SOCKET", "nif_info -> entry with %d args\r\n", argc) );
3960 
3961     if (argc == 0)
3962         return esock_global_info(env);
3963 
3964     ESOCK_ASSERT( argc == 1 );
3965 
3966     if (!ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
3967         return enif_make_badarg(env);
3968     }
3969 
3970     MLOCK(descP->readMtx);
3971     MLOCK(descP->writeMtx);
3972 
3973     SSDBG( descP, ("SOCKET", "nif_info(%T) {%d,0x%X} -> get socket info\r\n",
3974 		   argv[0], descP->sock,
3975 		   descP->readState | descP->writeState) );
3976 
3977     info = esock_socket_info(env, descP);
3978 
3979     SSDBG( descP, ("SOCKET", "nif_info(%T) -> get socket info done with"
3980 		   "\r\n   info: %T"
3981 		   "\r\n", argv[0], info) );
3982 
3983     MUNLOCK(descP->writeMtx);
3984     MUNLOCK(descP->readMtx);
3985 
3986     return info;
3987 #endif
3988 }
3989 
3990 
3991 /*
3992  * This function return a property list containing "global" info.
3993  *
3994  * Note that we also include something in the counter list that is not
3995  * actually a counter, the num_cnt_bits. This is the "size" of each counter,
3996  * in number of bits: 16 | 24 | 32 | 48 | 64.
3997  */
3998 #ifndef __WIN32__
3999 static
esock_global_info(ErlNifEnv * env)4000 ERL_NIF_TERM esock_global_info(ErlNifEnv* env)
4001 {
4002     ERL_NIF_TERM
4003         numBits, numSockets, numTypeDGrams, numTypeStreams,
4004         numTypeSeqPkgs, numDomLocal, numDomInet, numDomInet6,
4005         numProtoIP, numProtoTCP, numProtoUDP, numProtoSCTP,
4006         sockDbg, iovMax, dbg, useReg, iow;
4007 
4008     MLOCK(data.cntMtx);
4009     numBits        = MKUI(env, ESOCK_COUNTER_SIZE);
4010     numSockets     = MKUI(env, data.numSockets);
4011     numTypeDGrams  = MKUI(env, data.numTypeDGrams);
4012     numTypeStreams = MKUI(env, data.numTypeStreams);
4013     numTypeSeqPkgs = MKUI(env, data.numTypeSeqPkgs);
4014     numDomLocal    = MKUI(env, data.numDomainLocal);
4015     numDomInet     = MKUI(env, data.numDomainInet);
4016     numDomInet6    = MKUI(env, data.numDomainInet6);
4017     numProtoIP     = MKUI(env, data.numProtoIP);
4018     numProtoTCP    = MKUI(env, data.numProtoTCP);
4019     numProtoUDP    = MKUI(env, data.numProtoUDP);
4020     numProtoSCTP   = MKUI(env, data.numProtoSCTP);
4021     sockDbg        = BOOL2ATOM(data.sockDbg);
4022     MUNLOCK(data.cntMtx);
4023 
4024     iovMax         = MKI(env,  data.iov_max);
4025     dbg            = BOOL2ATOM(data.dbg);
4026     useReg         = BOOL2ATOM(data.useReg);
4027     iow            = BOOL2ATOM(data.iow);
4028 
4029     {
4030         ERL_NIF_TERM gcntVals[] =
4031             {numBits,
4032              numSockets,
4033              numTypeDGrams, numTypeStreams, numTypeSeqPkgs,
4034              numDomLocal, numDomInet, numDomInet6,
4035              numProtoIP, numProtoTCP, numProtoUDP, numProtoSCTP};
4036         ERL_NIF_TERM gcntKeys[] =
4037             {atom_num_cnt_bits,
4038              atom_num_sockets,
4039              atom_num_tdgrams, atom_num_tstreams, atom_num_tseqpkgs,
4040              atom_num_dlocal, atom_num_dinet, atom_num_dinet6,
4041              atom_num_pip, atom_num_ptcp, atom_num_pudp, atom_num_psctp};
4042         unsigned int numGCntVals = NUM(gcntVals);
4043         unsigned int numGCntKeys = NUM(gcntKeys);
4044         ERL_NIF_TERM gcnt;
4045 
4046         ESOCK_ASSERT( numGCntKeys == numGCntVals );
4047         ESOCK_ASSERT( MKMA(env, gcntKeys, gcntVals, numGCntKeys, &gcnt) );
4048 
4049         {
4050             ERL_NIF_TERM
4051                 keys[] = {esock_atom_debug,
4052                           atom_socket_debug,
4053                           atom_use_registry,
4054                           atom_iow,
4055                           atom_counters,
4056                           atom_iov_max},
4057                 vals[] = {dbg,
4058                           sockDbg,
4059                           useReg,
4060                           iow,
4061                           gcnt,
4062                           iovMax},
4063                 info;
4064             unsigned int
4065                 numKeys = NUM(keys),
4066                 numVals = NUM(vals);
4067 
4068             ESOCK_ASSERT( numKeys == numVals );
4069             ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &info) );
4070 
4071             return info;
4072         }
4073     }
4074 }
4075 #endif // #ifndef __WIN32__
4076 
4077 
4078 /*
4079  * This function return a property *map*. The properties are:
4080  *    domain:    The domain of the socket
4081  *    type:      The type of the socket
4082  *    protocol:  The protocol of the socket
4083  *    owner:     Controlling process (owner) of the socket)
4084  *    rstates:   Socket read state(s)
4085  *    wstates:   Socket write state(s)
4086  *    counters:  A list of each socket counter and there current values
4087  *    readers:   The number of current and waiting readers
4088  *    writers:   The number of current and waiting writers
4089  *    acceptors: The number of current and waiting acceptors
4090  */
4091 #ifndef __WIN32__
4092 static
esock_socket_info(ErlNifEnv * env,ESockDescriptor * descP)4093 ERL_NIF_TERM esock_socket_info(ErlNifEnv*       env,
4094                                ESockDescriptor* descP)
4095 {
4096     ERL_NIF_TERM domain    = esock_socket_info_domain(env, descP);
4097     ERL_NIF_TERM type      = esock_socket_info_type(env, descP);
4098     ERL_NIF_TERM protocol  = MKI(env, descP->protocol);
4099     ERL_NIF_TERM ctrlPid   = MKPID(env, &descP->ctrlPid);
4100     ERL_NIF_TERM rstates   = esock_socket_info_state(env, descP->readState);
4101     ERL_NIF_TERM wstates   = esock_socket_info_state(env, descP->writeState);
4102     ERL_NIF_TERM ctype     = esock_socket_info_ctype(env, descP);
4103     ERL_NIF_TERM counters  = esock_socket_info_counters(env, descP);
4104     ERL_NIF_TERM readers   = esock_socket_info_readers(env, descP);
4105     ERL_NIF_TERM writers   = esock_socket_info_writers(env, descP);
4106     ERL_NIF_TERM acceptors = esock_socket_info_acceptors(env, descP);
4107 
4108     {
4109         ERL_NIF_TERM keys[]
4110             = {esock_atom_domain,
4111                esock_atom_type,
4112                esock_atom_protocol,
4113                atom_owner,
4114                atom_rstates,
4115                atom_wstates,
4116                atom_ctype,
4117                atom_counters,
4118                atom_num_readers,
4119                atom_num_writers,
4120                atom_num_acceptors};
4121         ERL_NIF_TERM vals[]
4122             = {domain,
4123                type,
4124                protocol,
4125                ctrlPid,
4126                rstates,
4127                wstates,
4128                ctype,
4129                counters,
4130                readers,
4131                writers,
4132                acceptors};
4133         ERL_NIF_TERM info;
4134         unsigned int numKeys  = NUM(keys);
4135         unsigned int numVals  = NUM(vals);
4136 
4137         SSDBG( descP, ("SOCKET", "esock_socket_info -> "
4138                        "\r\n   numKeys: %d"
4139                        "\r\n   numVals: %d"
4140                        "\r\n", numKeys, numVals) );
4141 
4142         ESOCK_ASSERT( numKeys == numVals );
4143         ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &info) );
4144 
4145         return info;
4146     }
4147 }
4148 #endif // #ifndef __WIN32__
4149 
4150 
4151 /*
4152  * Encode the socket domain
4153  */
4154 #ifndef __WIN32__
4155 static
esock_socket_info_domain(ErlNifEnv * env,ESockDescriptor * descP)4156 ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv*       env,
4157                                       ESockDescriptor* descP)
4158 {
4159     int          domain = descP->domain;
4160     ERL_NIF_TERM edomain;
4161 
4162     esock_encode_domain(env, domain, &edomain);
4163     return edomain;
4164 }
4165 #endif // #ifndef __WIN32__
4166 
4167 
4168 /*
4169  * Encode the socket type
4170  */
4171 #ifndef __WIN32__
4172 static
esock_socket_info_type(ErlNifEnv * env,ESockDescriptor * descP)4173 ERL_NIF_TERM esock_socket_info_type(ErlNifEnv*       env,
4174                                     ESockDescriptor* descP)
4175 {
4176     int          type = descP->type;
4177     ERL_NIF_TERM etype;
4178 
4179     esock_encode_type(env, type, &etype);
4180 
4181     return etype;
4182 }
4183 #endif // #ifndef __WIN32__
4184 
4185 
4186 /*
4187  * Encode the socket "create type"
4188  * That is "show" how this socket was created:
4189  *
4190  *           normal | fromfd | {fromfd, integer()}
4191  */
4192 #ifndef __WIN32__
4193 static
esock_socket_info_ctype(ErlNifEnv * env,ESockDescriptor * descP)4194 ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv*       env,
4195                                      ESockDescriptor* descP)
4196 {
4197     ERL_NIF_TERM ctype;
4198 
4199     SSDBG( descP, ("SOCKET", "esock_socket_info_ctype {%d} -> entry with"
4200                    "\r\n   origFD:       %d"
4201                    "\r\n   closeOnClose: %s"
4202                    "\r\n", descP->sock,
4203                    descP->origFD, B2S(descP->closeOnClose)) );
4204 
4205     if (descP->origFD > 0) {
4206         /* Created from other FD */
4207         if (descP->closeOnClose) {
4208             /* We *have* dup'ed: {fromfd, integer()} */
4209             ctype = MKT2(env, MKA(env, "fromfd"), MKI(env, descP->origFD));
4210         } else {
4211             /* We have *not* dup'ed: fromfd */
4212             ctype = MKA(env, "fromfd");
4213         }
4214     } else {
4215         /* Normal socket */
4216         ctype = MKA(env, "normal");
4217     }
4218 
4219     SSDBG( descP, ("SOCKET", "esock_socket_info_ctype {%d} -> done:"
4220                    "\r\n   ctype: %T"
4221                    "\r\n", descP->sock, ctype) );
4222 
4223     return ctype;
4224 }
4225 #endif // #ifndef __WIN32__
4226 
4227 
4228 /*
4229  * Encode the socket "state"
4230  * That is "show" how this socket was created:
4231  *
4232  *           normal | fromfd | {fromfd, integer()}
4233  */
4234 #ifndef __WIN32__
4235 static
esock_socket_info_state(ErlNifEnv * env,unsigned int state)4236 ERL_NIF_TERM esock_socket_info_state(ErlNifEnv*   env,
4237                                      unsigned int state)
4238 {
4239     SocketTArray estate = TARRAY_CREATE(10);
4240     ERL_NIF_TERM estateList;
4241 
4242 
4243     if ((state & ESOCK_STATE_BOUND) != 0) {
4244       /*
4245       SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> bound"
4246 		     "\r\n", descP->sock) );
4247       */
4248       TARRAY_ADD(estate, atom_bound);
4249     }
4250 
4251     if ((state & ESOCK_STATE_LISTENING) != 0) {
4252       /*
4253       SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> listening"
4254 		     "\r\n", descP->sock) );
4255       */
4256       TARRAY_ADD(estate, atom_listening);
4257     }
4258 
4259     if ((state & ESOCK_STATE_ACCEPTING) != 0) {
4260       /*
4261       SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> accepting"
4262 		     "\r\n", descP->sock) );
4263       */
4264       TARRAY_ADD(estate, atom_accepting);
4265     }
4266 
4267     if ((state & ESOCK_STATE_CONNECTING) != 0) {
4268       /*
4269       SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> connecting"
4270 		     "\r\n", descP->sock) );
4271       */
4272       TARRAY_ADD(estate, atom_connecting);
4273     }
4274 
4275     if ((state & ESOCK_STATE_CONNECTED) != 0) {
4276       /*
4277       SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> connected"
4278 		     "\r\n", descP->sock) );
4279       */
4280       TARRAY_ADD(estate, atom_connected);
4281     }
4282 
4283     if ((state & ESOCK_STATE_SELECTED) != 0) {
4284       /*
4285       SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> selected"
4286 		     "\r\n", descP->sock) );
4287       */
4288       TARRAY_ADD(estate, atom_selected);
4289     }
4290 
4291     if ((state & ESOCK_STATE_CLOSING) != 0) {
4292       /*
4293       SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> closing"
4294 		     "\r\n", descP->sock) );
4295       */
4296       TARRAY_ADD(estate, atom_closing);
4297     }
4298 
4299     if ((state & ESOCK_STATE_CLOSED) != 0) {
4300       /*
4301       SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> closed"
4302 		     "\r\n", descP->sock) );
4303       */
4304       TARRAY_ADD(estate, atom_closed);
4305     }
4306 
4307     if ((state & ESOCK_STATE_DTOR) != 0) {
4308       /*
4309       SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> dror"
4310 		     "\r\n", descP->sock) );
4311       */
4312       TARRAY_ADD(estate, atom_dtor);
4313     }
4314 
4315     TARRAY_TOLIST(estate, env, &estateList);
4316 
4317     return estateList;
4318 }
4319 #endif // #ifndef __WIN32__
4320 
4321 
4322 /*
4323  * Collect all counters for a socket.
4324  */
4325 #ifndef __WIN32__
4326 static
esock_socket_info_counters(ErlNifEnv * env,ESockDescriptor * descP)4327 ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv*       env,
4328                                         ESockDescriptor* descP)
4329 {
4330     ERL_NIF_TERM keys[] = {atom_read_byte,
4331                            atom_read_fails,
4332                            atom_read_pkg,
4333                            atom_read_pkg_max,
4334                            atom_read_tries,
4335                            atom_read_waits,
4336                            atom_write_byte,
4337                            atom_write_fails,
4338                            atom_write_pkg,
4339                            atom_write_pkg_max,
4340                            atom_write_tries,
4341                            atom_write_waits,
4342                            atom_acc_success,
4343                            atom_acc_fails,
4344                            atom_acc_tries,
4345                            atom_acc_waits};
4346     unsigned int numKeys = NUM(keys);
4347     ERL_NIF_TERM vals[] = {MKCNT(env, descP->readByteCnt),
4348                            MKCNT(env, descP->readFails),
4349                            MKCNT(env, descP->readPkgCnt),
4350                            MKCNT(env, descP->readPkgMax),
4351                            MKCNT(env, descP->readTries),
4352                            MKCNT(env, descP->readWaits),
4353                            MKCNT(env, descP->writeByteCnt),
4354                            MKCNT(env, descP->writeFails),
4355                            MKCNT(env, descP->writePkgCnt),
4356                            MKCNT(env, descP->writePkgMax),
4357                            MKCNT(env, descP->writeTries),
4358                            MKCNT(env, descP->writeWaits),
4359                            MKCNT(env, descP->accSuccess),
4360                            MKCNT(env, descP->accFails),
4361                            MKCNT(env, descP->accTries),
4362                            MKCNT(env, descP->accWaits)};
4363     unsigned int numVals = NUM(vals);
4364     ERL_NIF_TERM cnts;
4365 
4366     SSDBG( descP, ("SOCKET", "esock_socket_info_counters -> "
4367                    "\r\n   numKeys: %d"
4368                    "\r\n   numVals: %d"
4369                    "\r\n", numKeys, numVals) );
4370 
4371     ESOCK_ASSERT( numKeys == numVals );
4372     ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &cnts) );
4373 
4374 #ifdef HAVE_SENDFILE
4375     if (descP->sendfileCountersP != NULL) {
4376         ESockSendfileCounters *cP = descP->sendfileCountersP;
4377         ERL_NIF_TERM m,
4378             sfKeys[] =
4379             {atom_sendfile,
4380              atom_sendfile_byte,
4381              atom_sendfile_fails,
4382              atom_sendfile_max,
4383              atom_sendfile_pkg,
4384              atom_sendfile_pkg_max,
4385              atom_sendfile_tries,
4386              atom_sendfile_waits},
4387             sfVals[] =
4388             {MKUI(env, cP->cnt),
4389              MKUI(env, cP->byteCnt),
4390              MKUI(env, cP->fails),
4391              MKUI(env, cP->max),
4392              MKUI(env, cP->pkg),
4393              MKUI(env, cP->pkgMax),
4394              MKUI(env, cP->tries),
4395              MKUI(env, cP->waits)};
4396         size_t n, numSfKeys = NUM(sfKeys);
4397 
4398         ESOCK_ASSERT( numSfKeys == NUM(sfVals) );
4399         for (n = 0;  n < numSfKeys;  n++) {
4400             ESOCK_ASSERT( enif_make_map_put(env, cnts,
4401                                             sfKeys[n], sfVals[n],
4402                                             &m) );
4403             cnts = m;
4404         }
4405     }
4406 #endif
4407 
4408     SSDBG( descP, ("SOCKET", "esock_socket_info_counters -> done with"
4409                    "\r\n   cnts: %T"
4410                    "\r\n", cnts) );
4411 
4412     return cnts;
4413 }
4414 #endif // #ifndef __WIN32__
4415 
4416 
4417 
4418 /* ----------------------------------------------------------------------
4419  * nif_command
4420  *
4421  * Description:
4422  * This function is intended to handle "various" commands. That is,
4423  * commands and operations that are not part of the socket API proper.
4424  * It's a map with two attributes command and (command) data:
4425  * #{command :: atom(), data :: term()}
4426  *
4427  * Currently it handles setting:
4428  *
4429  * Command                    Data
4430  * -------                    ----
4431  * (global) debug             boolean()
4432  * socket_debug               boolean()
4433  * use_registry               boolean()
4434  *
4435  */
4436 
4437 static
nif_command(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])4438 ERL_NIF_TERM nif_command(ErlNifEnv*         env,
4439                          int                argc,
4440                          const ERL_NIF_TERM argv[])
4441 {
4442 #ifdef __WIN32__
4443     return enif_raise_exception(env, MKA(env, "notsup"));
4444 #else
4445     ERL_NIF_TERM command, cdata, result;
4446 
4447     ESOCK_ASSERT( argc == 1 );
4448 
4449     SGDBG( ("SOCKET", "nif_command -> entry with %d args\r\n", argc) );
4450 
4451     if (! GET_MAP_VAL(env, argv[0], esock_atom_command, &command)) {
4452         SGDBG( ("SOCKET",
4453                 "nif_command -> field not found: command\r\n") );
4454         return enif_make_badarg(env);
4455     }
4456     if (! GET_MAP_VAL(env, argv[0], esock_atom_data, &cdata)) {
4457         SGDBG( ("SOCKET",
4458                 "nif_command -> field not found: data\r\n") );
4459         return enif_make_badarg(env);
4460     }
4461 
4462     SGDBG( ("SOCKET", "nif_command -> "
4463             "\r\n   command:  %T"
4464             "\r\n   cdata:     %T"
4465             "\r\n", command, cdata) );
4466 
4467     result = esock_command(env, command, cdata);
4468 
4469     SGDBG( ("SOCKET", "nif_command -> done with result: "
4470            "\r\n   %T"
4471            "\r\n", result) );
4472 
4473     return result;
4474 
4475 #endif // #ifdef __WIN32__ #else
4476 }
4477 
4478 
4479 #ifndef __WIN32__
4480 static
4481 ERL_NIF_TERM
esock_command(ErlNifEnv * env,ERL_NIF_TERM command,ERL_NIF_TERM cdata)4482 esock_command(ErlNifEnv* env, ERL_NIF_TERM command, ERL_NIF_TERM cdata)
4483 {
4484     int cmp;
4485 
4486     SGDBG( ("SOCKET", "esock_command -> entry with %T\r\n", command) );
4487 
4488     cmp = COMPARE(command, atom_socket_debug);
4489     if (cmp == 0) {
4490         return esock_command_socket_debug(env, cdata);
4491     } else if (cmp < 0) {
4492         if (COMPARE(command, esock_atom_debug) == 0)
4493             return esock_command_debug(env, cdata);
4494     } else { // 0 < cmp
4495         if (COMPARE(command, atom_use_registry) == 0)
4496             return esock_command_use_socket_registry(env, cdata);
4497     }
4498 
4499     SGDBG( ("SOCKET", "esock_command -> invalid command: %T\r\n",
4500             command) );
4501 
4502     return esock_raise_invalid(env, MKT2(env, esock_atom_command, command));
4503 }
4504 #endif // #ifndef __WIN32__
4505 
4506 
4507 #ifndef __WIN32__
4508 static
esock_command_debug(ErlNifEnv * env,ERL_NIF_TERM cdata)4509 ERL_NIF_TERM esock_command_debug(ErlNifEnv* env, ERL_NIF_TERM cdata)
4510 {
4511     ERL_NIF_TERM result;
4512 
4513     /* The data *should* be a boolean() */
4514     if (esock_decode_bool(cdata, &data.dbg))
4515         result = esock_atom_ok;
4516     else
4517         result =
4518             esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata));
4519 
4520     return result;
4521 }
4522 #endif // #ifndef __WIN32__
4523 
4524 
4525 #ifndef __WIN32__
4526 static
esock_command_socket_debug(ErlNifEnv * env,ERL_NIF_TERM cdata)4527 ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env, ERL_NIF_TERM cdata)
4528 {
4529     BOOLEAN_T dbg;
4530 
4531     /* The data *should* be a boolean() */
4532     if (! esock_decode_bool(cdata, &dbg))
4533         return
4534             esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata));
4535 
4536     MLOCK(data.cntMtx);
4537     data.sockDbg = dbg;
4538     MUNLOCK(data.cntMtx);
4539 
4540     return esock_atom_ok;;
4541 }
4542 #endif // #ifndef __WIN32__
4543 
4544 
4545 #ifndef __WIN32__
4546 static
esock_command_use_socket_registry(ErlNifEnv * env,ERL_NIF_TERM cdata)4547 ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv*   env,
4548                                                ERL_NIF_TERM cdata)
4549 {
4550     BOOLEAN_T useReg = FALSE;
4551 
4552     /* The data *should* be a boolean() */
4553     if (! esock_decode_bool(cdata, &useReg))
4554         return
4555             esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata));
4556 
4557     MLOCK(data.cntMtx);
4558     data.useReg = useReg;
4559     MUNLOCK(data.cntMtx);
4560 
4561     return esock_atom_ok;;
4562 }
4563 #endif
4564 
4565 
4566 
4567 
4568 /* *** esock_socket_info_readers   ***
4569  * *** esock_socket_info_writers   ***
4570  * *** esock_socket_info_acceptors ***
4571  *
4572  * Calculate how many readers | writers | acceptors we have for this socket.
4573  * Current requestor + any waiting requestors (of the type).
4574  *
4575  */
4576 
4577 #ifndef __WIN32__
4578 
4579 #define ESOCK_INFO_REQ_FUNCS                                            \
4580     ESOCK_INFO_REQ_FUNC_DECL(readers,   currentReaderP,   readersQ)     \
4581     ESOCK_INFO_REQ_FUNC_DECL(writers,   currentWriterP,  writersQ)     \
4582     ESOCK_INFO_REQ_FUNC_DECL(acceptors, currentAcceptorP, acceptorsQ)
4583 
4584 #define ESOCK_INFO_REQ_FUNC_DECL(F, CRP, Q)                             \
4585     static                                                              \
4586     ERL_NIF_TERM esock_socket_info_##F(ErlNifEnv*       env,            \
4587                                        ESockDescriptor* descP)          \
4588     {                                                                   \
4589         return socket_info_reqs(env, descP, descP->CRP, &descP->Q);     \
4590     }
4591 ESOCK_INFO_REQ_FUNCS
4592 #undef ESOCK_INFO_REQ_FUNC_DECL
4593 
4594 static
socket_info_reqs(ErlNifEnv * env,ESockDescriptor * descP,ESockRequestor * currentRequestorP,ESockRequestQueue * q)4595 ERL_NIF_TERM socket_info_reqs(ErlNifEnv*         env,
4596                               ESockDescriptor*   descP,
4597                               ESockRequestor*    currentRequestorP,
4598                               ESockRequestQueue* q)
4599 {
4600     ESockRequestQueueElement* tmp;
4601     ERL_NIF_TERM              info;
4602     unsigned int              cnt = 0;
4603 
4604     if (currentRequestorP != NULL) {
4605         // We have an active requestor!
4606         cnt++;
4607 
4608         // And add all the waiting requestors
4609         tmp = q->first;
4610         while (tmp != NULL) {
4611             cnt++;
4612             tmp = tmp->nextP;
4613         }
4614     }
4615 
4616     info = MKUI(env, cnt);
4617 
4618     SSDBG( descP, ("SOCKET", "socket_info_reqs -> done with"
4619                    "\r\n   info: %T"
4620                    "\r\n", info) );
4621 
4622     return info;
4623 }
4624 
4625 #endif // #ifndef __WIN32__
4626 
4627 
4628 /* ----------------------------------------------------------------------
4629  * nif_supports
4630  *
4631  * Description:
4632  * This function is intended to answer the question: "Is X supported?"
4633  * Currently three keys are "supported": options | sctp | ipv6
4634  * That results in a list of all *known options* (known by us) and if
4635  * the platform supports (OS) it or not.
4636  *
4637  * Key
4638  * ---
4639  * (arity 0)       [{sctp,      boolean()},
4640  *                  {ipv6,     boolean()},
4641  *                  {local,    boolean()},
4642  *                  {netns,    boolean()},
4643  *                  {sendfile, boolean()}]
4644  *
4645  * msg_flags       [{MsgFlag,              boolean()}]
4646  * protocols       [{[Protocol | Aliases], integer()}],
4647  * options         [{socket,               [{Opt, boolean()} | Opt]} |
4648  *                  {ProtoNum::integer(),  [{Opt, boolean()} | Opt]}]
4649  */
4650 
4651 static
nif_supports(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])4652 ERL_NIF_TERM nif_supports(ErlNifEnv*         env,
4653                           int                argc,
4654                           const ERL_NIF_TERM argv[])
4655 {
4656 #ifdef __WIN32__
4657     return enif_raise_exception(env, MKA(env, "notsup"));
4658 #else
4659 
4660     SGDBG( ("SOCKET", "nif_supports -> entry with %d args\r\n", argc) );
4661 
4662     /* Extract arguments and perform preliminary validation */
4663 
4664     if (argc == 0)
4665         return esock_supports_0(env);
4666 
4667     ESOCK_ASSERT( argc == 1 );
4668 
4669     return esock_supports_1(env, argv[0]);
4670 #endif
4671 }
4672 
4673 /* esock_supports - what features do we support?
4674  *
4675  * This gives information about what features actually
4676  * work on the current platform.
4677  */
4678 #ifndef __WIN32__
4679 static
esock_supports_0(ErlNifEnv * env)4680 ERL_NIF_TERM esock_supports_0(ErlNifEnv* env)
4681 {
4682     SocketTArray opts = TARRAY_CREATE(8);
4683     ERL_NIF_TERM is_supported, opts_list;
4684 
4685     SGDBG( ("SOCKET", "esock_supports_0 -> entry\r\n") );
4686 
4687 #if defined(HAVE_SCTP)
4688     is_supported = esock_atom_true;
4689 #else
4690     is_supported = esock_atom_false;
4691 #endif
4692     TARRAY_ADD(opts, MKT2(env, esock_atom_sctp, is_supported));
4693 
4694     /* Is this (test) really sufficient for testing if we support IPv6? */
4695 #if defined(HAVE_IPV6)
4696     is_supported = esock_atom_true;
4697 #else
4698     is_supported = esock_atom_false;
4699 #endif
4700     TARRAY_ADD(opts, MKT2(env, esock_atom_ipv6, is_supported));
4701 
4702 #if defined(AF_LOCAL)
4703     is_supported = esock_atom_true;
4704 #else
4705     is_supported = esock_atom_false;
4706 #endif
4707     TARRAY_ADD(opts, MKT2(env, esock_atom_local, is_supported));
4708 
4709 #if defined(HAVE_SETNS)
4710     is_supported = esock_atom_true;
4711 #else
4712     is_supported = esock_atom_false;
4713 #endif
4714     TARRAY_ADD(opts, MKT2(env, atom_netns, is_supported));
4715 
4716 #if defined(HAVE_SENDFILE)
4717     is_supported = esock_atom_true;
4718 #else
4719     is_supported = esock_atom_false;
4720 #endif
4721     TARRAY_ADD(opts, MKT2(env, atom_sendfile, is_supported));
4722 
4723     TARRAY_TOLIST(opts, env, &opts_list);
4724     return opts_list;
4725 }
4726 #endif // #ifndef __WIN32__
4727 
4728 #ifndef __WIN32__
4729 static
esock_supports_1(ErlNifEnv * env,ERL_NIF_TERM key)4730 ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key)
4731 {
4732     ERL_NIF_TERM result;
4733 
4734     SGDBG( ("SOCKET",
4735             "esock_supports_1 -> entry"
4736             "\r\n   key: %T"
4737             "\r\n", key) );
4738 
4739     if (COMPARE(key, atom_msg_flags) == 0)
4740         result = esock_supports_msg_flags(env);
4741     else if (COMPARE(key, atom_protocols) == 0)
4742         result = esock_supports_protocols(env);
4743     else if (COMPARE(key, atom_options) == 0)
4744         result = esock_supports_options(env);
4745     else
4746         result = MKEL(env);
4747 
4748     return result;
4749 }
4750 #endif // #ifndef __WIN32__
4751 
4752 
4753 
4754 #ifndef __WIN32__
4755 
esock_supports_msg_flags(ErlNifEnv * env)4756 static ERL_NIF_TERM esock_supports_msg_flags(ErlNifEnv* env) {
4757     size_t n;
4758     ERL_NIF_TERM result;
4759 
4760     result = MKEL(env);
4761     for (n = 0;  n < NUM(msg_flags);  n++) {
4762         result =
4763             MKC(env,
4764                 MKT2(env,
4765                      *(msg_flags[n].name),
4766                      MKI(env, msg_flags[n].flag)),
4767                 result);
4768     }
4769 
4770     return result;
4771 }
4772 
4773 #endif // #ifndef __WIN32__
4774 
4775 
4776 #ifndef __WIN32__
4777 static
esock_supports_protocols(ErlNifEnv * env)4778 ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env)
4779 {
4780     ERL_NIF_TERM protocols;
4781     struct protoent *pe;
4782     int stayopen;
4783 
4784     protocols = MKEL(env);
4785 
4786 #if defined(HAVE_GETPROTOENT) && \
4787     defined(HAVE_SETPROTOENT) && \
4788     defined(HAVE_ENDPROTOENT)
4789 
4790     MLOCK(data.protocolsMtx);
4791     stayopen = TRUE;
4792     setprotoent(stayopen);
4793     while ((pe = getprotoent()) != NULL) {
4794         ERL_NIF_TERM names;
4795         char **aliases;
4796 
4797         names = MKEL(env);
4798         for (aliases = pe->p_aliases;  *aliases != NULL;  aliases++)
4799             names = MKC(env, MKA(env, *aliases), names);
4800         names = MKC(env, MKA(env, pe->p_name), names);
4801 
4802         protocols =
4803             MKC(env, MKT2(env, names, MKI(env, pe->p_proto)), protocols);
4804     }
4805     endprotoent();
4806     MUNLOCK(data.protocolsMtx);
4807 
4808 #endif
4809 
4810     /* Defaults for known protocols in case getprotoent()
4811      * does not work or does not exist.  Prepended to the list
4812      * so a subsequent maps:from_list/2 will take the default
4813      * only when there is nothing from getprotoent().
4814      */
4815 
4816     protocols =
4817         MKC(env,
4818             MKT2(env,
4819                  MKL1(env, esock_atom_ip),
4820                  MKI(env,
4821 #ifdef SOL_IP
4822                      SOL_IP
4823 #else
4824                      IPPROTO_IP
4825 #endif
4826                      )),
4827             protocols);
4828 
4829 #ifdef HAVE_IPV6
4830     protocols =
4831         MKC(env,
4832             MKT2(env,
4833                  MKL1(env, esock_atom_ipv6),
4834                  MKI(env,
4835 #ifdef SOL_IPV6
4836                      SOL_IPV6
4837 #else
4838                      IPPROTO_IPV6
4839 #endif
4840                      )),
4841             protocols);
4842 #endif
4843 
4844     protocols =
4845         MKC(env,
4846             MKT2(env, MKL1(env, esock_atom_tcp), MKI(env, IPPROTO_TCP)),
4847             protocols);
4848 
4849     protocols =
4850         MKC(env,
4851             MKT2(env, MKL1(env, esock_atom_udp), MKI(env, IPPROTO_UDP)),
4852             protocols);
4853 
4854 #ifdef HAVE_SCTP
4855     protocols =
4856         MKC(env,
4857             MKT2(env, MKL1(env, esock_atom_sctp), MKI(env, IPPROTO_SCTP)),
4858             protocols);
4859 #endif
4860 
4861     return protocols;
4862 }
4863 #endif // #ifndef __WIN32__
4864 
4865 
4866 
4867 #ifndef __WIN32__
4868 
4869 static
esock_supports_options(ErlNifEnv * env)4870 ERL_NIF_TERM esock_supports_options(ErlNifEnv* env)
4871 {
4872     ERL_NIF_TERM levels;
4873     size_t n;
4874 
4875     levels = MKEL(env);
4876 
4877     for (n = 0;  n < NUM(optLevels);  n++) {
4878         ERL_NIF_TERM options;
4879         size_t m;
4880         struct ESockOptLevel *levelP;
4881 
4882         options = MKEL(env);
4883         levelP = optLevels + n;
4884         for (m = 0;  m < levelP->num;  m++) {
4885             struct ESockOpt *optP;
4886 
4887             optP = levelP->opts + m;
4888             if (optP->setopt == NULL && optP->getopt == NULL) {
4889                 options = MKC(env, *optP->nameP, options);
4890             } else {
4891                 options =
4892                     MKC(env,
4893                         MKT2(env, *optP->nameP, MKI(env, optP->opt)),
4894                         options);
4895             }
4896         }
4897         levels =
4898             MKC(env,
4899                 MKT2(env,
4900                      esock_encode_level(env, levelP->level), options),
4901                 levels);
4902     }
4903 
4904     return levels;
4905 }
4906 
4907 #endif // #ifndef __WIN32__
4908 
4909 
4910 
4911 /* ----------------------------------------------------------------------
4912  * nif_open
4913  *
4914  * Description:
4915  * Create an endpoint for communication.
4916  * This function "exist" in two variants.
4917  * One with two args and onewith four.
4918  *
4919  * Arguments (2):
4920  * FD       - File Descriptor (of an already open socket).
4921  * Extra    - A map with extra options.
4922  *            The options are:
4923  *               [M] dup    - boolean() - Shall the fd be dup'ed or not.
4924  *               [O] bound  - boolean() - Is the fd already bound.
4925  *               [O] domain - domain()  - We may not be able to retrieve
4926  *                                        this on all platforms, and in
4927  *                                        *those* cases this *must* be
4928  *                                        provided.
4929  *               [O] type   - type()    - We may not be able to retrieve
4930  *                                        this on all platforms, and in
4931  *                                        *those* cases this *must* be
4932  *                                        provided.
4933  *               [O] proto - protocol() - We may not be able to retrieve
4934  *                                        this on all platforms, and in
4935  *                                        *those* cases this *must* be
4936  *                                        provided.
4937  *               [O] use_registry - boolean() - Shall we use the socket
4938  *                                        registry for this socket.
4939  * Arguments (4):
4940  * Domain   - The domain, for example 'inet'
4941  * Type     - Type of socket, for example 'stream'
4942  * Protocol - The protocol, for example 'tcp'
4943  * Extra    - A map with "obscure" options.
4944  *            Currently the only allowed option are:
4945  *               netns        - string()  - Network namespace.  *Only*
4946  *                                          allowed on linux!
4947  *               use_registry - boolean() - Shall we use the socket
4948  *                                          registry for this socket.
4949  *
4950  */
4951 static
nif_open(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])4952 ERL_NIF_TERM nif_open(ErlNifEnv*         env,
4953                       int                argc,
4954                       const ERL_NIF_TERM argv[])
4955 {
4956 #ifdef __WIN32__
4957     return enif_raise_exception(env, MKA(env, "notsup"));
4958 #else
4959     ERL_NIF_TERM result;
4960 
4961     SGDBG( ("SOCKET", "nif_open -> "
4962             "\r\n   argc: %d"
4963             "\r\n", argc) );
4964 
4965     if (argc == 2) {
4966         int          fd;
4967 	ERL_NIF_TERM eopts;
4968 
4969 	if (! GET_INT(env, argv[0], &fd)) {
4970             if (IS_INTEGER(env, argv[0]))
4971                 return esock_make_error_integer_range(env, argv[0]);
4972             else
4973                 return enif_make_badarg(env);
4974 	}
4975 	if (! IS_MAP(env,  argv[1])) {
4976 	    return enif_make_badarg(env);
4977 	}
4978 	eopts = argv[1];
4979 
4980 	SGDBG( ("SOCKET", "nif_open -> "
4981 		"\r\n   FD:    %d"
4982 		"\r\n   eopts: %T"
4983 		"\r\n", fd, eopts) );
4984 
4985 	MLOCK(data.cntMtx);
4986 	result = esock_open2(env, fd, eopts);
4987 	MUNLOCK(data.cntMtx);
4988 
4989     } else {
4990         ERL_NIF_TERM edomain, etype, eopts;
4991 	int          domain, type, proto;
4992 
4993 	ESOCK_ASSERT( argc == 4 );
4994 
4995 	/* Extract arguments and perform preliminary validation */
4996 
4997 	if (! GET_INT(env, argv[2], &proto)) {
4998             if (IS_INTEGER(env, argv[2]))
4999                 return esock_make_error_integer_range(env, argv[2]);
5000             else
5001                 return enif_make_badarg(env);
5002         }
5003 	if (! IS_MAP(env,  argv[3])) {
5004 	    return enif_make_badarg(env);
5005 	}
5006 	edomain = argv[0];
5007 	etype = argv[1];
5008 	eopts  = argv[3];
5009 
5010 	SGDBG( ("SOCKET", "nif_open -> "
5011 		"\r\n   edomain: %T"
5012 		"\r\n   etype:   %T"
5013 		"\r\n   proto:   %T"
5014 		"\r\n   eopts:   %T"
5015 		"\r\n", argv[0], argv[1], argv[2], eopts) );
5016 
5017 	if (esock_decode_domain(env, edomain, &domain) == 0) {
5018 	    SGDBG( ("SOCKET",
5019 		    "nif_open -> invalid domain: %d\r\n", edomain) );
5020 	    return esock_make_invalid(env, esock_atom_domain);
5021 	}
5022 
5023 	if (! esock_decode_type(env, etype, &type)) {
5024 	    SGDBG( ("SOCKET",
5025 		    "nif_open -> invalid type: %d\r\n", etype) );
5026 	    return esock_make_invalid(env, esock_atom_type);
5027 	}
5028 
5029 	MLOCK(data.cntMtx);
5030 	result = esock_open4(env, domain, type, proto, eopts);
5031 	MUNLOCK(data.cntMtx);
5032     }
5033 
5034     SGDBG( ("SOCKET", "nif_open -> done with result: "
5035             "\r\n   %T"
5036             "\r\n", result) );
5037 
5038     return result;
5039 
5040 #endif // #ifdef __WIN32__  #else
5041 }
5042 
5043 
5044 /* esock_open - create an endpoint (from an existing fd) for communication
5045  *
5046  * Assumes the input has been validated.
5047  *
5048  * Normally we want debugging on (individual) sockets to be controlled
5049  * by the sockets own debug flag. But since we don't even have a socket
5050  * yet, we must use the global debug flag.
5051  */
5052 #ifndef __WIN32__
5053 static
esock_open2(ErlNifEnv * env,int fd,ERL_NIF_TERM eopts)5054 ERL_NIF_TERM esock_open2(ErlNifEnv*   env,
5055                          int          fd,
5056                          ERL_NIF_TERM eopts)
5057 {
5058     BOOLEAN_T        dbg    = esock_open_is_debug(env, eopts, data.sockDbg);
5059     BOOLEAN_T        useReg = esock_open_use_registry(env, eopts, data.useReg);
5060     ESockDescriptor* descP;
5061     ERL_NIF_TERM     sockRef;
5062     int              domain, type, protocol;
5063     int              save_errno = 0;
5064     BOOLEAN_T        closeOnClose;
5065     SOCKET           sock;
5066     ErlNifEvent      event;
5067     ErlNifPid        self;
5068 
5069     /* Keep track of the creator
5070      * This should not be a problem, but just in case
5071      * the *open* function is used with the wrong kind
5072      * of environment...
5073      */
5074     ESOCK_ASSERT( enif_self(env, &self) != NULL );
5075 
5076     SSDBG2( dbg,
5077             ("SOCKET", "esock_open2 -> entry with"
5078              "\r\n   fd:    %d"
5079              "\r\n   eopts: %T"
5080              "\r\n", fd, eopts) );
5081 
5082     /*
5083      * Before we do anything else, we try to retrieve domain, type and protocol
5084      * This information is either present in the eopts map or if not we need
5085      * to "get" it from the system (getsockopt).
5086      * Note that its not possible to get all of these on all platoforms,
5087      * and in those cases the user *must* provide us with them (eopts).
5088      *
5089      * We try the system first (since its more reliable) and if that fails
5090      * we check the eopts map. If neither one works, we *give up*!
5091      */
5092 
5093     if (! esock_open_which_domain(fd, &domain)) {
5094         SSDBG2( dbg,
5095                 ("SOCKET",
5096                  "esock_open2 -> failed get domain from system\r\n") );
5097 
5098         if (! esock_open2_get_domain(env, eopts, &domain)) {
5099             return esock_make_invalid(env, esock_atom_domain);
5100         }
5101     }
5102 
5103     if (! esock_open_which_type(fd, &type)) {
5104         SSDBG2( dbg,
5105                 ("SOCKET", "esock_open2 -> failed get type from system\r\n") );
5106 
5107         if (! esock_open2_get_type(env, eopts, &type))
5108             return esock_make_invalid(env, esock_atom_type);
5109     }
5110 
5111     if (! esock_open_which_protocol(fd, &protocol)) {
5112         SSDBG2( dbg,
5113                 ("SOCKET",
5114                  "esock_open2 -> failed get protocol from system\r\n") );
5115 
5116         if (! esock_extract_int_from_map(env, eopts,
5117                                         esock_atom_protocol, &protocol)) {
5118             SSDBG2( dbg,
5119                     ("SOCKET",
5120                      "esock_open2 -> trying protocol 0\r\n") );
5121             protocol = 0;
5122         }
5123     }
5124 
5125 
5126     SSDBG2( dbg,
5127             ("SOCKET", "esock_open2 -> "
5128              "\r\n   domain:   %d"
5129              "\r\n   type:     %d"
5130              "\r\n   protocol: %d"
5131              "\r\n", domain, type, protocol) );
5132 
5133 
5134     if (esock_open2_todup(env, eopts)) {
5135         /* We shall dup the socket */
5136         if (ESOCK_IS_ERROR(sock = dup(fd))) {
5137             save_errno = sock_errno();
5138 
5139             SSDBG2( dbg,
5140                     ("SOCKET",
5141                      "esock_open2 -> dup failed: %d\r\n",
5142                      save_errno) );
5143 
5144             return esock_make_error_errno(env, save_errno);
5145         }
5146         closeOnClose = TRUE;
5147     } else {
5148         sock         = fd;
5149         closeOnClose = FALSE;
5150     }
5151 
5152     event = sock;
5153 
5154     SET_NONBLOCKING(sock);
5155 
5156     /* Create and initiate the socket "descriptor" */
5157     descP               = alloc_descriptor(sock, event);
5158     descP->ctrlPid      = self;
5159     descP->domain       = domain;
5160     descP->type         = type;
5161     descP->protocol     = protocol;
5162     descP->closeOnClose = closeOnClose;
5163     descP->origFD       = fd;
5164 
5165     /* Check if we are already connected, if so change state */
5166     {
5167         ESockAddress remote;
5168         SOCKLEN_T    addrLen = sizeof(remote);
5169         sys_memzero((char *) &remote, addrLen);
5170         if (sock_peer(descP->sock,
5171                       (struct sockaddr*) &remote,
5172                       &addrLen) == 0) {
5173             SSDBG2( dbg, ("SOCKET", "esock_open2 -> connected\r\n") );
5174             descP->writeState |= ESOCK_STATE_CONNECTED;
5175         } else {
5176             SSDBG2( dbg, ("SOCKET", "esock_open2 -> not connected\r\n") );
5177         }
5178     }
5179 
5180     /* And create the 'socket' resource */
5181     sockRef = enif_make_resource(env, descP);
5182     enif_release_resource(descP);
5183 
5184     ESOCK_ASSERT( MONP("esock_open2 -> ctrl",
5185                        env, descP,
5186                        &descP->ctrlPid,
5187                        &descP->ctrlMon) == 0 );
5188 
5189     descP->dbg    = dbg;
5190     descP->useReg = useReg;
5191     inc_socket(domain, type, protocol);
5192 
5193     /* And finally (maybe) update the registry.
5194      * Shall we keep track of the fact that this socket is created elsewhere?
5195      */
5196     if (descP->useReg) esock_send_reg_add_msg(env, descP, sockRef);
5197 
5198     SSDBG2( dbg,
5199             ("SOCKET", "esock_open2 -> done: %T\r\n", sockRef) );
5200 
5201     return esock_make_ok2(env, sockRef);
5202 }
5203 #endif // #ifndef __WIN32__
5204 
5205 
5206 /* The eextra contains a boolean 'dup' key. Defaults to TRUE.
5207  */
5208 #ifndef __WIN32__
5209 static
esock_open2_todup(ErlNifEnv * env,ERL_NIF_TERM eextra)5210 BOOLEAN_T esock_open2_todup(ErlNifEnv* env, ERL_NIF_TERM eextra)
5211 {
5212     return esock_get_bool_from_map(env, eextra, atom_dup, TRUE);
5213 }
5214 #endif // #ifndef __WIN32__
5215 
5216 /* The eextra contains an integer 'domain' key.
5217  */
5218 #ifndef __WIN32__
5219 static
esock_open2_get_domain(ErlNifEnv * env,ERL_NIF_TERM eopts,int * domain)5220 BOOLEAN_T esock_open2_get_domain(ErlNifEnv* env,
5221                                  ERL_NIF_TERM eopts, int* domain)
5222 {
5223     ERL_NIF_TERM edomain;
5224 
5225     SGDBG( ("SOCKET", "esock_open2_get_domain -> entry with"
5226 	    "\r\n   eopts: %T"
5227 	    "\r\n", eopts) );
5228 
5229     if (!GET_MAP_VAL(env, eopts,
5230 		     esock_atom_domain, &edomain))
5231       return FALSE;
5232 
5233     if (esock_decode_domain(env, edomain, domain) == 0)
5234       return FALSE;
5235 
5236     return TRUE;
5237 }
5238 #endif // #ifndef __WIN32__
5239 
5240 /* The eextra contains an integer 'type' key.
5241  */
5242 #ifndef __WIN32__
5243 static
esock_open2_get_type(ErlNifEnv * env,ERL_NIF_TERM eopts,int * type)5244 BOOLEAN_T esock_open2_get_type(ErlNifEnv* env,
5245                                ERL_NIF_TERM eopts, int* type)
5246 {
5247     ERL_NIF_TERM etype;
5248 
5249     SGDBG( ("SOCKET", "esock_open2_get_type -> entry with"
5250             "\r\n   eopts: %T"
5251             "\r\n", eopts) );
5252 
5253     if (! GET_MAP_VAL(env, eopts, esock_atom_type, &etype))
5254         return FALSE;
5255 
5256     if (! esock_decode_type(env, etype, type))
5257         return FALSE;
5258 
5259     return TRUE;
5260 }
5261 #endif // #ifndef __WIN32__
5262 
5263 
5264 /* esock_open4 - create an endpoint for communication
5265  *
5266  * Assumes the input has been validated.
5267  *
5268  * Normally we want debugging on (individual) sockets to be controlled
5269  * by the sockets own debug flag. But since we don't even have a socket
5270  * yet, we must use the global debug flag.
5271  */
5272 #ifndef __WIN32__
5273 static
esock_open4(ErlNifEnv * env,int domain,int type,int protocol,ERL_NIF_TERM eopts)5274 ERL_NIF_TERM esock_open4(ErlNifEnv*   env,
5275                          int          domain,
5276                          int          type,
5277                          int          protocol,
5278                          ERL_NIF_TERM eopts)
5279 {
5280     BOOLEAN_T        dbg = esock_open_is_debug(env, eopts, data.sockDbg);
5281     BOOLEAN_T        useReg = esock_open_use_registry(env, eopts, data.useReg);
5282     ESockDescriptor* descP;
5283     ERL_NIF_TERM     sockRef;
5284     int              proto = protocol, save_errno;
5285     SOCKET           sock;
5286     ErlNifEvent      event;
5287     char*            netns;
5288 #ifdef HAVE_SETNS
5289     int              current_ns = 0;
5290 #endif
5291     ErlNifPid        self;
5292 
5293     /* Keep track of the creator
5294      * This should not be a problem, but just in case
5295      * the *open* function is used with the wrong kind
5296      * of environment...
5297      */
5298     ESOCK_ASSERT( enif_self(env, &self) != NULL );
5299 
5300     SSDBG2( dbg,
5301             ("SOCKET", "esock_open4 -> entry with"
5302              "\r\n   domain:   %d"
5303              "\r\n   type:     %d"
5304              "\r\n   protocol: %d"
5305              "\r\n   eopts:    %T"
5306              "\r\n", domain, type, protocol, eopts) );
5307 
5308 
5309 #ifdef HAVE_SETNS
5310     if (esock_open4_get_netns(env, eopts, &netns)) {
5311         SGDBG( ("SOCKET", "nif_open -> namespace: %s\r\n", netns) );
5312     }
5313 #else
5314     netns = NULL;
5315 #endif
5316 
5317 
5318 #ifdef HAVE_SETNS
5319     if ((netns != NULL) &&
5320         (! change_network_namespace(netns, &current_ns, &save_errno))) {
5321         FREE(netns);
5322         return esock_make_error_errno(env, save_errno);
5323     }
5324 #endif
5325 
5326     if (ESOCK_IS_ERROR(sock = sock_open(domain, type, proto))) {
5327         if (netns != NULL) FREE(netns);
5328         return esock_make_error_errno(env, sock_errno());
5329     }
5330 
5331     SSDBG2( dbg, ("SOCKET", "esock_open -> open success: %d\r\n", sock) );
5332 
5333 
5334     /* NOTE that if the protocol = 0 (default) and the domain is not
5335      * local (AF_LOCAL) we need to explicitly get the protocol here!
5336      */
5337 
5338     if (proto == 0)
5339         (void) esock_open_which_protocol(sock, &proto);
5340 
5341 #ifdef HAVE_SETNS
5342     if (netns != NULL) {
5343         FREE(netns);
5344         if (! restore_network_namespace(current_ns, sock, &save_errno))
5345             return esock_make_error_errno(env, save_errno);
5346     }
5347 #endif
5348 
5349 
5350     if ((event = sock_create_event(sock)) == INVALID_EVENT) {
5351         save_errno = sock_errno();
5352         (void) sock_close(sock);
5353         return esock_make_error_errno(env, save_errno);
5354     }
5355 
5356     SSDBG2( dbg, ("SOCKET", "esock_open4 -> event success: %d\r\n", event) );
5357 
5358     SET_NONBLOCKING(sock);
5359 
5360 
5361     /* Create and initiate the socket "descriptor" */
5362     descP           = alloc_descriptor(sock, event);
5363     descP->ctrlPid  = self;
5364     descP->domain   = domain;
5365     descP->type     = type;
5366     descP->protocol = proto;
5367 
5368     sockRef = enif_make_resource(env, descP);
5369     enif_release_resource(descP);
5370 
5371     ESOCK_ASSERT( MONP("esock_open -> ctrl",
5372                        env, descP,
5373                        &descP->ctrlPid,
5374                        &descP->ctrlMon) == 0 );
5375 
5376     descP->dbg    = dbg;
5377     descP->useReg = useReg;
5378     inc_socket(domain, type, protocol);
5379 
5380     /* And finally (maybe) update the registry */
5381     if (descP->useReg) esock_send_reg_add_msg(env, descP, sockRef);
5382 
5383     return esock_make_ok2(env, sockRef);
5384 }
5385 #endif // #ifndef __WIN32__
5386 
5387 /* The eextra map "may" contain a boolean 'debug' key.
5388  */
5389 #ifndef __WIN32__
5390 static
esock_open_is_debug(ErlNifEnv * env,ERL_NIF_TERM eextra,BOOLEAN_T dflt)5391 BOOLEAN_T esock_open_is_debug(ErlNifEnv* env, ERL_NIF_TERM eextra,
5392                               BOOLEAN_T dflt)
5393 {
5394     return esock_get_bool_from_map(env, eextra, esock_atom_debug, dflt);
5395 }
5396 #endif // #ifndef __WIN32__
5397 
5398 
5399 #ifndef __WIN32__
5400 static
esock_open_use_registry(ErlNifEnv * env,ERL_NIF_TERM eextra,BOOLEAN_T dflt)5401 BOOLEAN_T esock_open_use_registry(ErlNifEnv* env, ERL_NIF_TERM eextra,
5402                                   BOOLEAN_T dflt)
5403 {
5404     return esock_get_bool_from_map(env, eextra, atom_use_registry, dflt);
5405 }
5406 #endif
5407 
5408 
5409 #ifndef __WIN32__
5410 static
esock_open_which_domain(SOCKET sock,int * domain)5411 BOOLEAN_T esock_open_which_domain(SOCKET sock, int* domain)
5412 {
5413 #if defined(SO_DOMAIN)
5414     if (esock_getopt_int(sock, SOL_SOCKET, SO_DOMAIN, domain))
5415         return TRUE;
5416 #endif
5417     return FALSE;
5418 }
5419 #endif // #ifndef __WIN32__
5420 
5421 
5422 #ifndef __WIN32__
5423 static
esock_open_which_type(SOCKET sock,int * type)5424 BOOLEAN_T esock_open_which_type(SOCKET sock, int* type)
5425 {
5426 #if defined(SO_TYPE)
5427     if (esock_getopt_int(sock, SOL_SOCKET, SO_TYPE, type))
5428         return TRUE;
5429 #endif
5430     return FALSE;
5431 }
5432 #endif // #ifndef __WIN32__
5433 
5434 
5435 #ifndef __WIN32__
5436 static
esock_open_which_protocol(SOCKET sock,int * proto)5437 BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto)
5438 {
5439 #if defined(SO_PROTOCOL)
5440     if (esock_getopt_int(sock, SOL_SOCKET, SO_PROTOCOL, proto))
5441         return TRUE;
5442 #endif
5443     return FALSE;
5444 }
5445 #endif // #ifndef __WIN32__
5446 
5447 
5448 
5449 #ifdef HAVE_SETNS
5450 
5451 
5452 /* We should really have another API, so that we can return errno... */
5453 
5454 /* *** change network namespace ***
5455  * Retreive the current namespace and set the new.
5456  * Return result and previous namespace if successfull.
5457  */
5458 #ifndef __WIN32__
5459 static
change_network_namespace(char * netns,int * cns,int * err)5460 BOOLEAN_T change_network_namespace(char* netns, int* cns, int* err)
5461 {
5462     int save_errno;
5463     int current_ns = 0;
5464     int new_ns     = 0;
5465 
5466     SGDBG( ("SOCKET", "change_network_namespace -> entry with"
5467             "\r\n   new ns: %s"
5468             "\r\n", netns) );
5469 
5470     current_ns = open("/proc/self/ns/net", O_RDONLY);
5471     if (ESOCK_IS_ERROR(current_ns)) {
5472         *err = sock_errno();
5473         return FALSE;
5474     }
5475     new_ns = open(netns, O_RDONLY);
5476     if (ESOCK_IS_ERROR(new_ns)) {
5477         save_errno = sock_errno();
5478         (void) close(current_ns);
5479         *err = save_errno;
5480         return FALSE;
5481     }
5482     if (setns(new_ns, CLONE_NEWNET) != 0) {
5483         save_errno = sock_errno();
5484         (void) close(new_ns);
5485         (void) close(current_ns);
5486         *err = save_errno;
5487         return FALSE;
5488     } else {
5489         (void) close(new_ns);
5490         *cns = current_ns;
5491         return TRUE;
5492     }
5493 }
5494 #endif // #ifndef __WIN32__
5495 
5496 
5497 /* *** restore network namespace ***
5498  * Restore the previous namespace (see above).
5499  */
5500 #ifndef __WIN32__
5501 static
restore_network_namespace(int ns,SOCKET sock,int * err)5502 BOOLEAN_T restore_network_namespace(int ns, SOCKET sock, int* err)
5503 {
5504     SGDBG( ("SOCKET", "restore_network_namespace -> entry with"
5505             "\r\n   ns: %d"
5506             "\r\n", ns) );
5507 
5508     if (setns(ns, CLONE_NEWNET) != 0) {
5509         /* XXX Failed to restore network namespace.
5510          * What to do? Tidy up and return an error...
5511          * Note that the thread now might still be in the namespace.
5512          * Can this even happen? Should the emulator be aborted?
5513          */
5514         int save_errno = sock_errno();
5515         (void) close(sock);
5516         (void) close(ns);
5517         *err = save_errno;
5518         return FALSE;
5519     } else {
5520         (void) close(ns);
5521         return TRUE;
5522     }
5523 }
5524 #endif // #ifndef __WIN32__
5525 
5526 
5527 #endif // ifdef HAVE_SETNS
5528 
5529 
5530 
5531 /* ----------------------------------------------------------------------
5532  * nif_bind
5533  *
5534  * Description:
5535  * Bind a name to a socket.
5536  *
5537  * Arguments:
5538  * [0] Socket (ref) - Points to the socket descriptor.
5539  * [1] LocalAddr    - Local address is a sockaddr map ( socket:sockaddr() ).
5540  */
5541 static
nif_bind(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])5542 ERL_NIF_TERM nif_bind(ErlNifEnv*         env,
5543                       int                argc,
5544                       const ERL_NIF_TERM argv[])
5545 {
5546 #ifdef __WIN32__
5547     return enif_raise_exception(env, MKA(env, "notsup"));
5548 #else
5549     ESockDescriptor* descP;
5550     ERL_NIF_TERM     eSockAddr, ret;
5551     ESockAddress     sockAddr;
5552     SOCKLEN_T       addrLen;
5553 
5554     ESOCK_ASSERT( argc == 2 );
5555 
5556     SGDBG( ("SOCKET", "nif_bind -> entry with argc: %d\r\n", argc) );
5557 
5558     /* Extract arguments and perform preliminary validation */
5559 
5560     if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
5561         return enif_make_badarg(env);
5562     }
5563     eSockAddr = argv[1];
5564 
5565     if (! esock_decode_sockaddr(env, eSockAddr, &sockAddr, &addrLen))
5566         return esock_make_invalid(env, atom_sockaddr);
5567 
5568     MLOCK(descP->readMtx);
5569 
5570     SSDBG( descP,
5571            ("SOCKET", "nif_bind(%T) {%d,0x%X} ->"
5572             "\r\n   SockAddr: %T"
5573             "\r\n",
5574             argv[0], descP->sock, descP->readState,
5575             eSockAddr) );
5576 
5577     ret = esock_bind(env, descP, &sockAddr, addrLen);
5578 
5579     SSDBG( descP, ("SOCKET", "nif_bind(%T) -> done with"
5580                    "\r\n   ret: %T"
5581                    "\r\n", argv[0], ret) );
5582 
5583     MUNLOCK(descP->readMtx);
5584 
5585     return ret;
5586 #endif // #ifdef __WIN32__  #else
5587 }
5588 
5589 
5590 #ifndef __WIN32__
5591 static
esock_bind(ErlNifEnv * env,ESockDescriptor * descP,ESockAddress * sockAddrP,SOCKLEN_T addrLen)5592 ERL_NIF_TERM esock_bind(ErlNifEnv*       env,
5593                         ESockDescriptor* descP,
5594                         ESockAddress*    sockAddrP,
5595                         SOCKLEN_T        addrLen)
5596 {
5597     if (! IS_OPEN(descP->readState))
5598         return esock_make_error(env, atom_closed);
5599 
5600     if (sock_bind(descP->sock, &sockAddrP->sa, addrLen) < 0) {
5601         return esock_make_error_errno(env, sock_errno());
5602     }
5603 
5604     descP->readState |= ESOCK_STATE_BOUND;
5605 
5606     return esock_atom_ok;
5607 }
5608 #endif // #ifndef __WIN32__
5609 
5610 
5611 
5612 /* ----------------------------------------------------------------------
5613  * nif_connect
5614  *
5615  * Description:
5616  * Initiate a connection on a socket
5617  *
5618  * Arguments:
5619  * Socket (ref) - Points to the socket descriptor.
5620  * Optional arguments:
5621  * ConnectRef   - Ref for the connection
5622  * SockAddr     - Socket Address of "remote" host.
5623  *                This is sockaddr(), which is either
5624  *                sockaddr_in4 or sockaddr_in6.
5625  */
5626 static
nif_connect(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])5627 ERL_NIF_TERM nif_connect(ErlNifEnv*         env,
5628                          int                argc,
5629                          const ERL_NIF_TERM argv[])
5630 {
5631 #ifdef __WIN32__
5632     return enif_raise_exception(env, MKA(env, "notsup"));
5633 #else
5634     ESockDescriptor* descP;
5635     ERL_NIF_TERM     res, sockRef, connRef;
5636     ESockAddress     addr, *addrP;
5637     SOCKLEN_T        addrLen;
5638 
5639     ESOCK_ASSERT( argc >= 1 );
5640 
5641     SGDBG( ("SOCKET", "nif_connect -> entry with argc: %d\r\n", argc) );
5642 
5643     /* Extract arguments and perform preliminary validation */
5644 
5645     sockRef = argv[0];
5646     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP))
5647         return enif_make_badarg(env);
5648 
5649     if (argc == 3) {
5650         ERL_NIF_TERM eSockAddr = argv[2];
5651 
5652         connRef = argv[1];
5653         if (! enif_is_ref(env, connRef))
5654             return enif_make_badarg(env);
5655 
5656         if (! esock_decode_sockaddr(env, eSockAddr, &addr, &addrLen))
5657             return esock_make_invalid(env, atom_sockaddr);
5658         addrP = &addr;
5659 
5660         MLOCK(descP->writeMtx);
5661 
5662         SSDBG( descP,
5663                ("SOCKET", "nif_connect(%T), {%d0x%X} ->"
5664                 "\r\n   ConnRef: %T"
5665                 "\r\n   SockAddr: %T"
5666                 "\r\n",
5667                 sockRef, descP->sock, descP->writeState,
5668                 connRef, eSockAddr) );
5669     } else {
5670 
5671         ESOCK_ASSERT( argc == 1 );
5672 
5673         connRef = esock_atom_undefined;
5674         addrP = NULL;
5675         addrLen = 0;
5676 
5677         MLOCK(descP->writeMtx);
5678 
5679         SSDBG( descP,
5680                ("SOCKET", "nif_connect(%T), {%d,0x%X} ->"
5681                 "\r\n",
5682                 sockRef, descP->sock, descP->writeState
5683                 ) );
5684     }
5685 
5686     res = esock_connect(env, descP, sockRef, connRef, addrP, addrLen);
5687 
5688     SSDBG( descP, ("SOCKET", "nif_connect(%T) -> done with"
5689                    "\r\n   res: %T"
5690                    "\r\n", sockRef, res) );
5691 
5692     MUNLOCK(descP->writeMtx);
5693 
5694     return res;
5695 
5696 #endif // #ifdef __WIN32__  #else
5697 }
5698 
5699 
5700 #ifndef __WIN32__
5701 static
esock_connect(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM connRef,ESockAddress * addrP,SOCKLEN_T addrLen)5702 ERL_NIF_TERM esock_connect(ErlNifEnv*       env,
5703                            ESockDescriptor* descP,
5704                            ERL_NIF_TERM     sockRef,
5705                            ERL_NIF_TERM     connRef,
5706                            ESockAddress*    addrP,
5707                            SOCKLEN_T        addrLen)
5708 {
5709     int       save_errno;
5710     ErlNifPid self;
5711 
5712     ESOCK_ASSERT( enif_self(env, &self) != NULL );
5713 
5714     /*
5715      * Verify that we are in the proper state
5716      */
5717 
5718     if (! IS_OPEN(descP->writeState))
5719         return esock_make_error(env, atom_closed);
5720 
5721     /* Connect and Write uses the same select flag
5722      * so they can not be simultaneous
5723      */
5724     if (descP->currentWriterP != NULL)
5725         return esock_make_error_invalid(env, atom_state);
5726 
5727     if (descP->connectorP != NULL) {
5728         /* Connect in progress */
5729 
5730         if (COMPARE_PIDS(&self, &descP->connector.pid) != 0) {
5731 	    /* Other process has connect in progress */
5732 	    if (addrP != NULL) {
5733                 return esock_make_error(env, atom_already);
5734 	    } else {
5735 	        /* This is a bad call sequence
5736 		 * - connect without an address is only allowed
5737 		 *   for the connecting process
5738 		 */
5739 	        return esock_raise_invalid(env, atom_state);
5740 	    }
5741         }
5742 
5743         /* Finalize after received select message */
5744 
5745         requestor_release("esock_connect finalize -> connected",
5746                           env, descP, &descP->connector);
5747         descP->connectorP = NULL;
5748         descP->writeState &= ~ESOCK_STATE_CONNECTING;
5749 
5750         if (! verify_is_connected(descP, &save_errno)) {
5751             return esock_make_error_errno(env, save_errno);
5752         }
5753 
5754         descP->writeState |= ESOCK_STATE_CONNECTED;
5755 
5756         return esock_atom_ok;
5757     }
5758 
5759     /* No connect in progress */
5760 
5761     if (addrP == NULL)
5762         /* This is a bad call sequence
5763          * - connect without an address is only allowed when
5764          *   a connect is in progress, after getting the select message
5765          */
5766         return esock_raise_invalid(env, atom_state);
5767 
5768     /* Initial connect call, with address */
5769 
5770     if (sock_connect(descP->sock, (struct sockaddr*) addrP, addrLen) == 0) {
5771         /* Success already! */
5772         SSDBG( descP, ("SOCKET", "esock_connect {%d} -> connected\r\n",
5773                        descP->sock) );
5774 
5775         descP->writeState |= ESOCK_STATE_CONNECTED;
5776 
5777         return esock_atom_ok;
5778     }
5779 
5780     /* Connect returned error */
5781     save_errno = sock_errno();
5782 
5783     switch (save_errno) {
5784 
5785     case ERRNO_BLOCK:   /* Winsock2            */
5786     case EINPROGRESS:   /* Unix & OSE!!        */
5787         SSDBG( descP,
5788                ("SOCKET", "esock_connect {%d} -> would block => select\r\n",
5789                 descP->sock) );
5790         {
5791             int sres;
5792 
5793             if ((sres =
5794                  esock_select_write(env, descP->sock, descP, NULL,
5795                                     sockRef, connRef)) < 0)
5796                 return
5797                     enif_raise_exception(env,
5798                                          MKT2(env, atom_select_write,
5799                                               MKI(env, sres)));
5800             /* Initiate connector */
5801             descP->connector.pid = self;
5802             ESOCK_ASSERT( MONP("esock_connect -> conn",
5803                                env, descP,
5804                                &self, &descP->connector.mon) == 0 );
5805             descP->connector.env = esock_alloc_env("connector");
5806             descP->connector.ref = CP_TERM(descP->connector.env, connRef);
5807             descP->connectorP = &descP->connector;
5808             descP->writeState |=
5809                 (ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED);
5810 
5811             return atom_select;
5812         }
5813         break;
5814 
5815     default:
5816         SSDBG( descP,
5817                ("SOCKET", "esock_connect {%d} -> error: %d\r\n",
5818                 descP->sock, save_errno) );
5819 
5820         return esock_make_error_errno(env, save_errno);
5821 
5822     } // switch(save_errno)
5823 }
5824 #endif // #ifndef __WIN32__
5825 
5826 
5827 
5828 /* *** verify_is_connected ***
5829  * Check if a connection has been established.
5830  */
5831 #ifndef __WIN32__
5832 static
verify_is_connected(ESockDescriptor * descP,int * err)5833 BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err)
5834 {
5835     /*
5836      * *** This is strange ***
5837      *
5838      * This *should* work on Windows NT too, but doesn't.
5839      * An bug in Winsock 2.0 for Windows NT?
5840      *
5841      * See "Unix Netwok Programming", "The Sockets Networking API",
5842      * W.R.Stevens, Volume 1, third edition, 16.4 Nonblocking 'connect',
5843      * before Interrupted 'connect' (p 412) for a discussion about
5844      * Unix portability and non blocking connect.
5845      */
5846 
5847     int error = 0;
5848 
5849 #ifdef SO_ERROR
5850     if (! esock_getopt_int(descP->sock, SOL_SOCKET, SO_ERROR, &error)) {
5851         // Solaris does it this way according to W.R.Stevens
5852         error = sock_errno();
5853     }
5854 #elif 1
5855     char buf[0];
5856     if (ESOCK_IS_ERROR(read(descP->sock, buf, sizeof(buf)))) {
5857         error = sock_errno();
5858     }
5859 #else
5860     /* This variant probably returns wrong error value
5861      * ENOTCONN instead of the actual connect error
5862      */
5863     ESockAddress remote;
5864     SOCKLEN_T    addrLen = sizeof(remote);
5865     sys_memzero((char *) &remote, addrLen);
5866     if (sock_peer(descP->sock,
5867                   (struct sockaddr*) &remote, &addrLen)) < 0) {
5868         error = sock_errno();
5869     }
5870 #endif
5871 
5872     if (error != 0) {
5873         *err = error;
5874         return FALSE;
5875     }
5876     return TRUE;
5877 }
5878 #endif // #ifndef __WIN32__
5879 
5880 
5881 
5882 /* ----------------------------------------------------------------------
5883  * nif_listen
5884  *
5885  * Description:
5886  * Listen for connections on a socket.
5887  *
5888  * Arguments:
5889  * Socket (ref) - Points to the socket descriptor.
5890  * Backlog      - The maximum length to which the queue of pending
5891  *                connections for socket may grow.
5892  */
5893 static
nif_listen(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])5894 ERL_NIF_TERM nif_listen(ErlNifEnv*         env,
5895                         int                argc,
5896                         const ERL_NIF_TERM argv[])
5897 {
5898 #ifdef __WIN32__
5899     return enif_raise_exception(env, MKA(env, "notsup"));
5900 #else
5901     ESockDescriptor* descP;
5902     int              backlog;
5903     ERL_NIF_TERM     ret;
5904 
5905     ESOCK_ASSERT( argc == 2 );
5906 
5907     SGDBG( ("SOCKET", "nif_listen -> entry with argc: %d\r\n", argc) );
5908 
5909     /* Extract arguments and perform preliminary validation */
5910 
5911     if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
5912         return enif_make_badarg(env);
5913     }
5914     if (! GET_INT(env, argv[1], &backlog)) {
5915         if (IS_INTEGER(env, argv[1]))
5916             return esock_make_error_integer_range(env, argv[1]);
5917         else
5918             return enif_make_badarg(env);
5919     }
5920 
5921     MLOCK(descP->readMtx);
5922 
5923     SSDBG( descP,
5924            ("SOCKET", "nif_listen(%T), {%d,0x%X} ->"
5925             "\r\n   backlog: %d"
5926             "\r\n",
5927             argv[0], descP->sock, descP->readState,
5928             backlog) );
5929 
5930     ret = esock_listen(env, descP, backlog);
5931 
5932     SSDBG( descP, ("SOCKET", "nif_listen(%T) -> done with"
5933                    "\r\n   ret: %T"
5934                    "\r\n", argv[0], ret) );
5935 
5936     MUNLOCK(descP->readMtx);
5937 
5938     return ret;
5939 #endif // #ifdef __WIN32__  #else
5940 }
5941 
5942 
5943 
5944 #ifndef __WIN32__
5945 static
esock_listen(ErlNifEnv * env,ESockDescriptor * descP,int backlog)5946 ERL_NIF_TERM esock_listen(ErlNifEnv*       env,
5947                           ESockDescriptor* descP,
5948                           int              backlog)
5949 {
5950 
5951     /*
5952      * Verify that we are in the proper state
5953      */
5954 
5955     if (! IS_OPEN(descP->readState))
5956         return esock_make_error(env, atom_closed);
5957 
5958     /*
5959      * And attempt to make socket listening
5960      */
5961 
5962     if ((sock_listen(descP->sock, backlog)) < 0)
5963         return esock_make_error_errno(env, sock_errno());
5964 
5965     descP->readState |= ESOCK_STATE_LISTENING;
5966 
5967     return esock_atom_ok;
5968 
5969 }
5970 #endif // #ifndef __WIN32__
5971 
5972 
5973 
5974 /* ----------------------------------------------------------------------
5975  * nif_accept
5976  *
5977  * Description:
5978  * Accept a connection on a socket.
5979  *
5980  * Arguments:
5981  * Socket (ref) - Points to the socket descriptor.
5982  * Request ref  - Unique "id" of this request
5983  *                (used for the select, if none is in queue).
5984  */
5985 static
nif_accept(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])5986 ERL_NIF_TERM nif_accept(ErlNifEnv*         env,
5987                         int                argc,
5988                         const ERL_NIF_TERM argv[])
5989 {
5990 #ifdef __WIN32__
5991     return enif_raise_exception(env, MKA(env, "notsup"));
5992 #else
5993 
5994     ESockDescriptor* descP;
5995     ERL_NIF_TERM     sockRef, ref, res;
5996 
5997     ESOCK_ASSERT( argc == 2 );
5998 
5999     SGDBG( ("SOCKET", "nif_accept -> entry with argc: %d\r\n", argc) );
6000 
6001     /* Extract arguments and perform preliminary validation */
6002 
6003     sockRef = argv[0];
6004     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
6005         return enif_make_badarg(env);
6006     }
6007     ref = argv[1];
6008 
6009     MLOCK(descP->readMtx);
6010 
6011     SSDBG( descP,
6012            ("SOCKET", "nif_accept%T), {%d,0x%X} ->"
6013             "\r\n   ReqRef:                %T"
6014             "\r\n   Current Acceptor addr: %p"
6015             "\r\n   Current Acceptor  pid: %T"
6016             "\r\n   Current Acceptor  mon: %T"
6017             "\r\n   Current Acceptor  env: 0x%lX"
6018             "\r\n   Current Acceptor  ref: %T"
6019             "\r\n",
6020             sockRef, descP->sock, descP->readState,
6021             ref,
6022             descP->currentAcceptorP,
6023             descP->currentAcceptor.pid,
6024             esock_make_monitor_term(env, &descP->currentAcceptor.mon),
6025             descP->currentAcceptor.env,
6026             descP->currentAcceptor.ref) );
6027 
6028     res = esock_accept(env, descP, sockRef, ref);
6029 
6030     SSDBG( descP, ("SOCKET", "nif_accept(%T) -> done with"
6031                    "\r\n   res: %T"
6032                    "\r\n", sockRef, res) );
6033 
6034     MUNLOCK(descP->readMtx);
6035 
6036     return res;
6037 
6038 #endif // #ifdef __WIN32__  #else
6039 }
6040 
6041 
6042 #ifndef __WIN32__
6043 static
esock_accept(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM accRef)6044 ERL_NIF_TERM esock_accept(ErlNifEnv*       env,
6045                           ESockDescriptor* descP,
6046                           ERL_NIF_TERM     sockRef,
6047                           ERL_NIF_TERM     accRef)
6048 {
6049     ErlNifPid     caller;
6050 
6051     ESOCK_ASSERT( enif_self(env, &caller) != NULL );
6052 
6053     if (! IS_OPEN(descP->readState))
6054         return esock_make_error(env, atom_closed);
6055 
6056     /* Accept and Read uses the same select flag
6057      * so they can not be simultaneous
6058      */
6059     if (descP->currentReaderP != NULL)
6060         return esock_make_error_invalid(env, atom_state);
6061 
6062     if (descP->currentAcceptorP == NULL) {
6063         SOCKET        accSock;
6064 
6065         /* We have no active acceptor (and therefore no acceptors in queue)
6066          */
6067 
6068         SSDBG( descP, ("SOCKET", "esock_accept {%d} -> try accept\r\n",
6069                        descP->sock) );
6070 
6071 	ESOCK_CNT_INC(env, descP, sockRef, atom_acc_tries, &descP->accTries, 1);
6072 
6073         accSock = sock_accept(descP->sock, NULL, NULL);
6074 
6075         if (ESOCK_IS_ERROR(accSock)) {
6076             int           save_errno;
6077 
6078             save_errno = sock_errno();
6079 
6080             return esock_accept_listening_error(env, descP, sockRef,
6081                                                 accRef, caller, save_errno);
6082         } else {
6083             /* We got an incoming connection */
6084             return
6085                 esock_accept_listening_accept(env, descP, sockRef,
6086                                               accSock, caller);
6087         }
6088     } else {
6089 
6090         /* We have an active acceptor and possibly acceptors waiting in queue.
6091          * If the pid of the calling process is not the pid of the
6092 	 * "current process", push the requester onto the (acceptor) queue.
6093          */
6094 
6095         SSDBG( descP, ("SOCKET", "esock_accept_accepting -> check: "
6096                        "is caller current acceptor:"
6097                        "\r\n   Caller:  %T"
6098                        "\r\n   Current: %T"
6099                        "\r\n", caller, descP->currentAcceptor.pid) );
6100 
6101         if (COMPARE_PIDS(&descP->currentAcceptor.pid, &caller) == 0) {
6102 
6103             SSDBG( descP,
6104                    ("SOCKET",
6105                     "esock_accept_accepting {%d} -> current acceptor\r\n",
6106                     descP->sock) );
6107 
6108             return esock_accept_accepting_current(env, descP, sockRef, accRef);
6109 
6110         } else {
6111 
6112             /* Not the "current acceptor", so (maybe) push onto queue */
6113 
6114             SSDBG( descP,
6115                    ("SOCKET",
6116                     "esock_accept_accepting {%d} -> *not* current acceptor\r\n",
6117                     descP->sock) );
6118 
6119             return esock_accept_accepting_other(env, descP, accRef, caller);
6120         }
6121     }
6122 }
6123 #endif // #ifndef __WIN32__
6124 
6125 /* *** esock_accept_listening_error ***
6126  *
6127  * The accept call resultet in an error - handle it.
6128  * There are only two cases:
6129  * 1) BLOCK => Attempt a "retry"
6130  * 2) Other => Return the value (converted to an atom)
6131  */
6132 #ifndef __WIN32__
6133 static
esock_accept_listening_error(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM accRef,ErlNifPid caller,int save_errno)6134 ERL_NIF_TERM esock_accept_listening_error(ErlNifEnv*       env,
6135                                           ESockDescriptor* descP,
6136                                           ERL_NIF_TERM     sockRef,
6137                                           ERL_NIF_TERM     accRef,
6138                                           ErlNifPid        caller,
6139                                           int              save_errno)
6140 {
6141     ERL_NIF_TERM res;
6142 
6143     if (save_errno == ERRNO_BLOCK ||
6144         save_errno == EAGAIN) {
6145 
6146         /* *** Try again later *** */
6147 
6148         SSDBG( descP,
6149                ("SOCKET",
6150                 "esock_accept_listening_error {%d} -> would block\r\n",
6151                 descP->sock) );
6152 
6153 	descP->currentAcceptor.pid = caller;
6154         ESOCK_ASSERT( MONP("esock_accept_listening -> current acceptor",
6155                            env, descP,
6156                            &descP->currentAcceptor.pid,
6157                            &descP->currentAcceptor.mon) == 0 );
6158         ESOCK_ASSERT( descP->currentAcceptor.env == NULL );
6159         descP->currentAcceptor.env = esock_alloc_env("current acceptor");
6160         descP->currentAcceptor.ref =
6161             CP_TERM(descP->currentAcceptor.env, accRef);
6162         descP->currentAcceptorP = &descP->currentAcceptor;
6163         res = esock_accept_busy_retry(env, descP, sockRef, accRef, NULL);
6164     } else {
6165         SSDBG( descP,
6166                ("SOCKET",
6167                 "esock_accept_listening {%d} -> errno: %d\r\n",
6168                 descP->sock, save_errno) );
6169 
6170         ESOCK_CNT_INC(env, descP, sockRef, atom_acc_fails, &descP->accFails, 1);
6171 
6172         res = esock_make_error_errno(env, save_errno);
6173     }
6174 
6175     return res;
6176 }
6177 #endif // #ifndef __WIN32__
6178 
6179 
6180 /* *** esock_accept_listening_accept ***
6181  *
6182  * The accept call was successful (accepted) - handle the new connection.
6183  */
6184 #ifndef __WIN32__
6185 static
esock_accept_listening_accept(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,SOCKET accSock,ErlNifPid caller)6186 ERL_NIF_TERM esock_accept_listening_accept(ErlNifEnv*       env,
6187                                            ESockDescriptor* descP,
6188                                            ERL_NIF_TERM     sockRef,
6189                                            SOCKET           accSock,
6190                                            ErlNifPid        caller)
6191 {
6192     ERL_NIF_TERM res;
6193 
6194     esock_accept_accepted(env, descP, sockRef, accSock, caller, &res);
6195 
6196     return res;
6197 }
6198 #endif // #ifndef __WIN32__
6199 
6200 
6201 /* *** esock_accept_accepting_current ***
6202  * Handles when the current acceptor makes another attempt.
6203  */
6204 #ifndef __WIN32__
6205 static
esock_accept_accepting_current(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM accRef)6206 ERL_NIF_TERM esock_accept_accepting_current(ErlNifEnv*       env,
6207                                             ESockDescriptor* descP,
6208                                             ERL_NIF_TERM     sockRef,
6209                                             ERL_NIF_TERM     accRef)
6210 {
6211     SOCKET        accSock;
6212     int           save_errno;
6213     ERL_NIF_TERM  res;
6214 
6215     SSDBG( descP,
6216            ("SOCKET",
6217             "esock_accept_accepting_current {%d} -> try accept\r\n",
6218             descP->sock) );
6219 
6220     ESOCK_CNT_INC(env, descP, sockRef, atom_acc_tries, &descP->accTries, 1);
6221 
6222     accSock = sock_accept(descP->sock, NULL, NULL);
6223 
6224     if (ESOCK_IS_ERROR(accSock)) {
6225 
6226         save_errno = sock_errno();
6227 
6228         res = esock_accept_accepting_current_error(env, descP, sockRef,
6229                                                    accRef, save_errno);
6230     } else {
6231 
6232         res = esock_accept_accepting_current_accept(env, descP, sockRef,
6233                                                     accSock);
6234     }
6235 
6236     return res;
6237 }
6238 #endif // #ifndef __WIN32__
6239 
6240 
6241 /* *** esock_accept_accepting_current_accept ***
6242  * Handles when the current acceptor succeeded in its accept call -
6243  * handle the new connection.
6244  */
6245 #ifndef __WIN32__
6246 static
esock_accept_accepting_current_accept(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,SOCKET accSock)6247 ERL_NIF_TERM esock_accept_accepting_current_accept(ErlNifEnv*       env,
6248                                                    ESockDescriptor* descP,
6249                                                    ERL_NIF_TERM     sockRef,
6250                                                    SOCKET           accSock)
6251 {
6252     ERL_NIF_TERM res;
6253 
6254     SSDBG( descP,
6255            ("SOCKET",
6256             "esock_accept_accepting_current_accept {%d}"
6257             "\r\n", descP->sock) );
6258 
6259     if (esock_accept_accepted(env, descP, sockRef, accSock,
6260                               descP->currentAcceptor.pid, &res)) {
6261 
6262         if (!activate_next_acceptor(env, descP, sockRef)) {
6263 
6264             SSDBG( descP,
6265                    ("SOCKET",
6266                     "esock_accept_accepting_current_accept {%d} ->"
6267                     " no more acceptors"
6268                     "\r\n", descP->sock) );
6269 
6270             descP->readState &= ~ESOCK_STATE_ACCEPTING;
6271 
6272             descP->currentAcceptorP = NULL;
6273         }
6274 
6275     }
6276 
6277     return res;
6278 }
6279 #endif // #ifndef __WIN32__
6280 
6281 
6282 /* *** esock_accept_accepting_current_error ***
6283  * The accept call of current acceptor resultet in an error - handle it.
6284  * There are only two cases:
6285  * 1) BLOCK => Attempt a "retry"
6286  * 2) Other => Return the value (converted to an atom)
6287  */
6288 #ifndef __WIN32__
6289 static
esock_accept_accepting_current_error(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM opRef,int save_errno)6290 ERL_NIF_TERM esock_accept_accepting_current_error(ErlNifEnv*       env,
6291                                                   ESockDescriptor* descP,
6292                                                   ERL_NIF_TERM     sockRef,
6293                                                   ERL_NIF_TERM     opRef,
6294                                                   int              save_errno)
6295 {
6296     ERL_NIF_TERM   res, reason;
6297 
6298     if (save_errno == ERRNO_BLOCK ||
6299         save_errno == EAGAIN) {
6300 
6301         /*
6302          * Just try again, no real error, just a ghost trigger from poll,
6303          */
6304 
6305         SSDBG( descP,
6306                ("SOCKET",
6307                 "esock_accept_accepting_current_error {%d} -> "
6308                 "would block: try again\r\n", descP->sock) );
6309 
6310         ESOCK_CNT_INC(env, descP, sockRef, atom_acc_waits, &descP->accWaits, 1);
6311 
6312         res = esock_accept_busy_retry(env, descP, sockRef, opRef,
6313                                       &descP->currentAcceptor.pid);
6314 
6315     } else {
6316         ESockRequestor req;
6317 
6318         SSDBG( descP,
6319                ("SOCKET",
6320                 "esock_accept_accepting_current_error {%d} -> "
6321                 "error: %d\r\n", descP->sock, save_errno) );
6322 
6323         ESOCK_CNT_INC(env, descP, sockRef, atom_acc_fails, &descP->accFails, 1);
6324 
6325         requestor_release("esock_accept_accepting_current_error",
6326                           env, descP, &descP->currentAcceptor);
6327 
6328         reason = MKA(env, erl_errno_id(save_errno));
6329         res    = esock_make_error(env, reason);
6330 
6331         req.env = NULL;
6332         while (acceptor_pop(env, descP, &req)) {
6333             SSDBG( descP,
6334                    ("SOCKET",
6335                     "esock_accept_accepting_current_error {%d} -> abort %T\r\n",
6336                     descP->sock, req.pid) );
6337 
6338             esock_send_abort_msg(env, descP, sockRef, &req, reason);
6339 
6340             (void) DEMONP("esock_accept_accepting_current_error -> "
6341                           "pop'ed writer",
6342                           env, descP, &req.mon);
6343         }
6344         descP->currentAcceptorP = NULL;
6345     }
6346 
6347     return res;
6348 }
6349 #endif // #ifndef __WIN32__
6350 
6351 
6352 /* *** esock_accept_accepting_other ***
6353  * Handles when the another acceptor makes an attempt, which
6354  * results (maybe) in the request beeing pushed onto the
6355  * acceptor queue.
6356  */
6357 #ifndef __WIN32__
6358 ERL_NIF_TERM
esock_accept_accepting_other(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM ref,ErlNifPid caller)6359 esock_accept_accepting_other(ErlNifEnv*       env,
6360                              ESockDescriptor* descP,
6361                              ERL_NIF_TERM     ref,
6362                              ErlNifPid        caller)
6363 {
6364     if (! acceptor_search4pid(env, descP, &caller)) {
6365         acceptor_push(env, descP, caller, ref);
6366 	return atom_select;
6367     } else {
6368         /* Acceptor already in queue */
6369         return esock_raise_invalid(env, atom_state);
6370     }
6371 }
6372 #endif // #ifndef __WIN32__
6373 
6374 
6375 /* *** esock_accept_busy_retry ***
6376  *
6377  * Perform a retry select. If successful, set nextState.
6378  */
6379 #ifndef __WIN32__
6380 static
esock_accept_busy_retry(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM accRef,ErlNifPid * pidP)6381 ERL_NIF_TERM esock_accept_busy_retry(ErlNifEnv*       env,
6382                                      ESockDescriptor* descP,
6383                                      ERL_NIF_TERM     sockRef,
6384                                      ERL_NIF_TERM     accRef,
6385                                      ErlNifPid*       pidP)
6386 {
6387     int          sres;
6388     ERL_NIF_TERM res;
6389 
6390     if ((sres = esock_select_read(env, descP->sock, descP, pidP,
6391                                   sockRef, accRef)) < 0) {
6392 
6393         ESOCK_ASSERT( DEMONP("esock_accept_busy_retry - select failed",
6394                              env, descP, &descP->currentAcceptor.mon) == 0);
6395         /* It is very unlikely that a next acceptor will be able
6396          * to do anything succesful, but we will clean the queue
6397          */
6398         if (!activate_next_acceptor(env, descP, sockRef)) {
6399             SSDBG( descP,
6400                    ("SOCKET",
6401                     "esock_accept_busy_retry {%d} -> no more acceptors\r\n",
6402                     descP->sock) );
6403 
6404             descP->readState &= ~ESOCK_STATE_ACCEPTING;
6405 
6406             descP->currentAcceptorP = NULL;
6407         }
6408 
6409         res =
6410             enif_raise_exception(env,
6411                                  MKT2(env, atom_select_read,
6412                                       MKI(env, sres)));
6413     } else {
6414         descP->readState |=
6415             (ESOCK_STATE_ACCEPTING | ESOCK_STATE_SELECTED);
6416         res = atom_select;
6417     }
6418 
6419     return res;
6420 }
6421 #endif // #ifndef __WIN32__
6422 
6423 
6424 /* *** esock_accept_accepted ***
6425  *
6426  * Generic function handling a successful accept.
6427  */
6428 #ifndef __WIN32__
6429 static
esock_accept_accepted(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,SOCKET accSock,ErlNifPid pid,ERL_NIF_TERM * result)6430 BOOLEAN_T esock_accept_accepted(ErlNifEnv*       env,
6431                                 ESockDescriptor* descP,
6432                                 ERL_NIF_TERM     sockRef,
6433                                 SOCKET           accSock,
6434                                 ErlNifPid        pid,
6435                                 ERL_NIF_TERM*    result)
6436 {
6437     ESockDescriptor* accDescP;
6438     ErlNifEvent      accEvent;
6439     ERL_NIF_TERM     accRef;
6440     int              save_errno;
6441 
6442     /*
6443      * We got one
6444      */
6445 
6446     ESOCK_CNT_INC(env, descP, sockRef, atom_acc_success, &descP->accSuccess, 1);
6447 
6448     if ((accEvent = sock_create_event(accSock)) == INVALID_EVENT) {
6449         save_errno = sock_errno();
6450         (void) sock_close(accSock);
6451         *result = esock_make_error_errno(env, save_errno);
6452         return FALSE;
6453     }
6454 
6455     accDescP           = alloc_descriptor(accSock, accEvent);
6456     accDescP->domain   = descP->domain;
6457     accDescP->type     = descP->type;
6458     accDescP->protocol = descP->protocol;
6459 
6460     MLOCK(descP->writeMtx);
6461 
6462     accDescP->rBufSz   = descP->rBufSz;  // Inherit buffer size
6463     accDescP->rNum     = descP->rNum;    // Inherit buffer uses
6464     accDescP->rNumCnt  = 0;
6465     accDescP->rCtrlSz  = descP->rCtrlSz; // Inherit buffer size
6466     accDescP->wCtrlSz  = descP->wCtrlSz; // Inherit buffer size
6467     accDescP->iow      = descP->iow;     // Inherit iow
6468     accDescP->dbg      = descP->dbg;     // Inherit debug flag
6469     accDescP->useReg   = descP->useReg;  // Inherit useReg flag
6470     inc_socket(accDescP->domain, accDescP->type, accDescP->protocol);
6471 
6472     accRef = enif_make_resource(env, accDescP);
6473     enif_release_resource(accDescP);
6474 
6475     accDescP->ctrlPid = pid;
6476     /* pid has actually been compared equal to self()
6477      * in this code path just a little while ago
6478      */
6479     ESOCK_ASSERT( MONP("esock_accept_accepted -> ctrl",
6480                        env, accDescP,
6481                        &accDescP->ctrlPid,
6482                        &accDescP->ctrlMon) == 0 );
6483 
6484     SET_NONBLOCKING(accDescP->sock);
6485 
6486     descP->writeState |= ESOCK_STATE_CONNECTED;
6487 
6488     MUNLOCK(descP->writeMtx);
6489 
6490     /* And finally (maybe) update the registry */
6491     if (descP->useReg) esock_send_reg_add_msg(env, descP, accRef);
6492 
6493     *result = esock_make_ok2(env, accRef);
6494 
6495     return TRUE;
6496 }
6497 #endif // #ifndef __WIN32__
6498 
6499 
6500 
6501 /* ----------------------------------------------------------------------
6502  * nif_send
6503  *
6504  * Description:
6505  * Send a message on a socket
6506  *
6507  * Arguments:
6508  * Socket (ref) - Points to the socket descriptor.
6509  * Bin          - The data to send as a binary()
6510  * Flags        - Send flags as an integer()
6511  * SendRef      - A unique id reference() for this (send) request.
6512  */
6513 
6514 static
nif_send(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])6515 ERL_NIF_TERM nif_send(ErlNifEnv*         env,
6516                       int                argc,
6517                       const ERL_NIF_TERM argv[])
6518 {
6519 #ifdef __WIN32__
6520     return enif_raise_exception(env, MKA(env, "notsup"));
6521 #else
6522     ESockDescriptor* descP;
6523     ERL_NIF_TERM     sockRef, sendRef;
6524     ErlNifBinary     sndData;
6525     int              flags;
6526     ERL_NIF_TERM     res;
6527 
6528     ESOCK_ASSERT( argc == 4 );
6529 
6530     SGDBG( ("SOCKET", "nif_send -> entry with argc: %d\r\n", argc) );
6531 
6532     sockRef = argv[0]; // We need this in case we send in case we send abort
6533     sendRef = argv[3];
6534 
6535     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
6536         SGDBG( ("SOCKET", "nif_send -> get resource failed\r\n") );
6537         return enif_make_badarg(env);
6538     }
6539 
6540     /* Extract arguments and perform preliminary validation */
6541 
6542     if ((! enif_is_ref(env, sendRef)) ||
6543         (! GET_BIN(env, argv[1], &sndData))) {
6544         SSDBG( descP, ("SOCKET", "nif_send -> argv decode failed\r\n") );
6545         return enif_make_badarg(env);
6546     }
6547     if (! GET_INT(env, argv[2], &flags)) {
6548         SSDBG( descP, ("SOCKET", "nif_send -> argv decode failed\r\n") );
6549         if (IS_INTEGER(env, argv[2]))
6550             return esock_make_error_integer_range(env, argv[2]);
6551         else
6552             return enif_make_badarg(env);
6553     }
6554     MLOCK(descP->writeMtx);
6555 
6556     SSDBG( descP,
6557            ("SOCKET", "nif_send(%T), {%d,0x%X} ->"
6558             "\r\n   SendRef:   %T"
6559             "\r\n   Data size: %u"
6560             "\r\n   flags:     0x%X"
6561             "\r\n",
6562             sockRef, descP->sock, descP->writeState,
6563             sendRef, sndData.size, flags) );
6564 
6565     /* We need to handle the case when another process tries
6566      * to write at the same time.
6567      * If the current write could not write its entire package
6568      * this time (resulting in an select). The write of the
6569      * other process must be made to wait until current
6570      * is done!
6571      */
6572 
6573     res = esock_send(env, descP, sockRef, sendRef, &sndData, flags);
6574 
6575     SSDBG( descP, ("SOCKET", "nif_send(%T) -> done with"
6576                    "\r\n   res: %T"
6577                    "\r\n", sockRef, res) );
6578 
6579     MUNLOCK(descP->writeMtx);
6580 
6581     SGDBG( ("SOCKET", "nif_send -> done with result: "
6582             "\r\n   %T"
6583             "\r\n", res) );
6584     return res;
6585 
6586 #endif // #ifdef __WIN32__  #else
6587 }
6588 
6589 
6590 
6591 /* *** esock_send ***
6592  *
6593  * Do the actual send.
6594  * Do some initial writer checks, do the actual send and then
6595  * analyze the result. If we are done, another writer may be
6596  * scheduled (if there is one in the writer queue).
6597  */
6598 #ifndef __WIN32__
6599 static
esock_send(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM sendRef,ErlNifBinary * sndDataP,int flags)6600 ERL_NIF_TERM esock_send(ErlNifEnv*       env,
6601                         ESockDescriptor* descP,
6602                         ERL_NIF_TERM     sockRef,
6603                         ERL_NIF_TERM     sendRef,
6604                         ErlNifBinary*    sndDataP,
6605                         int              flags)
6606 {
6607     ssize_t      send_result;
6608     ERL_NIF_TERM writerCheck;
6609 
6610     if (! IS_OPEN(descP->writeState))
6611         return esock_make_error(env, atom_closed);
6612 
6613     /* Connect and Write uses the same select flag
6614      * so they can not be simultaneous
6615      */
6616     if (descP->connectorP != NULL)
6617         return esock_make_error_invalid(env, atom_state);
6618 
6619     send_result = (ssize_t) sndDataP->size;
6620     if ((size_t) send_result != sndDataP->size)
6621         return esock_make_error_invalid(env, atom_data_size);
6622 
6623     /* Ensure that we either have no current writer or we are it,
6624      * or enqueue this process if there is a current writer  */
6625     if (! send_check_writer(env, descP, sendRef, &writerCheck)) {
6626         SSDBG( descP, ("SOCKET", "esock_send {%d} -> writer check failed: "
6627                        "\r\n   %T\r\n", descP->sock, writerCheck) );
6628         return writerCheck;
6629     }
6630 
6631     ESOCK_CNT_INC(env, descP, sockRef, atom_write_tries, &descP->writeTries, 1);
6632 
6633     send_result =
6634         sock_send(descP->sock, sndDataP->data, sndDataP->size, flags);
6635 
6636     return send_check_result(env, descP,
6637                              send_result, sndDataP->size, FALSE,
6638                              sockRef, sendRef);
6639 
6640 }
6641 #endif // #ifndef __WIN32__
6642 
6643 
6644 
6645 /* ----------------------------------------------------------------------
6646  * nif_sendto
6647  *
6648  * Description:
6649  * Send a message on a socket
6650  *
6651  * Arguments:
6652  * Socket (ref) - Points to the socket descriptor.
6653  * Bin          - The data to send as a binary()
6654  * Dest         - Destination (socket) address.
6655  * Flags        - Send flags as an integer().
6656  * SendRef      - A unique id reference() for this (send) request.
6657  */
6658 
6659 static
nif_sendto(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])6660 ERL_NIF_TERM nif_sendto(ErlNifEnv*         env,
6661                         int                argc,
6662                         const ERL_NIF_TERM argv[])
6663 {
6664 #ifdef __WIN32__
6665     return enif_raise_exception(env, MKA(env, "notsup"));
6666 #else
6667     ESockDescriptor* descP;
6668     ERL_NIF_TERM     sockRef, sendRef;
6669     ErlNifBinary     sndData;
6670     int              flags;
6671     ERL_NIF_TERM     eSockAddr;
6672     ESockAddress     remoteAddr;
6673     SOCKLEN_T        remoteAddrLen;
6674     ERL_NIF_TERM     res;
6675 
6676     ESOCK_ASSERT( argc == 5 );
6677 
6678     SGDBG( ("SOCKET", "nif_sendto -> entry with argc: %d\r\n", argc) );
6679 
6680     sockRef   = argv[0]; // We need this in case we send abort (to the caller)
6681     sendRef   = argv[4];
6682 
6683     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
6684         SGDBG( ("SOCKET", "nif_sendto -> get resource failed\r\n") );
6685         return enif_make_badarg(env);
6686     }
6687 
6688     /* Extract arguments and perform preliminary validation */
6689 
6690     if ((! enif_is_ref(env, sendRef)) ||
6691         (! GET_BIN(env, argv[1], &sndData))) {
6692         SSDBG( descP, ("SOCKET", "nif_sendto -> argv decode failed\r\n") );
6693         return enif_make_badarg(env);
6694     }
6695     if (! GET_INT(env, argv[3], &flags)) {
6696         SSDBG( descP, ("SOCKET", "nif_sendto -> argv decode failed\r\n") );
6697         if (IS_INTEGER(env, argv[3]))
6698             return esock_make_error_integer_range(env, argv[3]);
6699         else
6700             return enif_make_badarg(env);
6701     }
6702     eSockAddr = argv[2];
6703     if (! esock_decode_sockaddr(env, eSockAddr,
6704                                 &remoteAddr,
6705                                 &remoteAddrLen)) {
6706         SSDBG( descP,
6707                ("SOCKET",
6708                 "nif_sendto(%T), {%d} -> sockaddr decode failed \r\n",
6709                 sockRef, descP->sock) );
6710 
6711         return esock_make_invalid(env, atom_sockaddr);
6712     }
6713 
6714     MLOCK(descP->writeMtx);
6715 
6716     SSDBG( descP,
6717            ("SOCKET", "nif_sendto(%T), {%d,0x%X} ->"
6718             "\r\n   sendRef:   %T"
6719             "\r\n   Data size: %u"
6720             "\r\n   eSockAddr: %T"
6721             "\r\n   flags:     0x%X"
6722             "\r\n",
6723             sockRef, descP->sock, descP->readState,
6724             sendRef, sndData.size, eSockAddr, flags) );
6725 
6726     res = esock_sendto(env, descP, sockRef, sendRef, &sndData, flags,
6727                        &remoteAddr, remoteAddrLen);
6728 
6729     SSDBG( descP, ("SOCKET", "nif_sendto(%T) -> done with"
6730                    "\r\n   res: %T"
6731                    "\r\n", sockRef, res) );
6732 
6733     MUNLOCK(descP->writeMtx);
6734 
6735     return res;
6736 
6737 #endif // if defined(__WIN32__)
6738 }
6739 
6740 
6741 #ifndef __WIN32__
6742 static
esock_sendto(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM sendRef,ErlNifBinary * dataP,int flags,ESockAddress * toAddrP,SOCKLEN_T toAddrLen)6743 ERL_NIF_TERM esock_sendto(ErlNifEnv*       env,
6744                           ESockDescriptor* descP,
6745                           ERL_NIF_TERM     sockRef,
6746                           ERL_NIF_TERM     sendRef,
6747                           ErlNifBinary*    dataP,
6748                           int              flags,
6749                           ESockAddress*    toAddrP,
6750                           SOCKLEN_T        toAddrLen)
6751 {
6752     ssize_t      sendto_result;
6753     ERL_NIF_TERM writerCheck;
6754 
6755     if (! IS_OPEN(descP->writeState))
6756         return esock_make_error(env, atom_closed);
6757 
6758     /* Connect and Write uses the same select flag
6759      * so they can not be simultaneous
6760      */
6761     if (descP->connectorP != NULL)
6762         return esock_make_error_invalid(env, atom_state);
6763 
6764     sendto_result = (ssize_t) dataP->size;
6765     if ((size_t) sendto_result != dataP->size)
6766         return esock_make_error_invalid(env, atom_data_size);
6767 
6768     /* Ensure that we either have no current writer or we are it,
6769      * or enqueue this process if there is a current writer  */
6770     if (! send_check_writer(env, descP, sendRef, &writerCheck)) {
6771         SSDBG( descP, ("SOCKET", "esock_sendto {%d} -> writer check failed: "
6772                        "\r\n   %T\r\n", descP->sock, writerCheck) );
6773         return writerCheck;
6774     }
6775 
6776     ESOCK_CNT_INC(env, descP, sockRef, atom_write_tries, &descP->writeTries, 1);
6777 
6778     if (toAddrP != NULL) {
6779         sendto_result =
6780             sock_sendto(descP->sock,
6781                         dataP->data, dataP->size, flags,
6782                         &toAddrP->sa, toAddrLen);
6783     } else {
6784         sendto_result =
6785             sock_sendto(descP->sock,
6786                         dataP->data, dataP->size, flags,
6787                         NULL, 0);
6788     }
6789 
6790     return send_check_result(env, descP, sendto_result, dataP->size, FALSE,
6791                              sockRef, sendRef);
6792 }
6793 #endif // #ifndef __WIN32__
6794 
6795 
6796 
6797 /* ----------------------------------------------------------------------
6798  * nif_sendmsg
6799  *
6800  * Description:
6801  * Send a message on a socket
6802  *
6803  * Arguments:
6804  * Socket (ref) - Points to the socket descriptor.
6805  * Msg          - Message - map() with data and (maybe) control and dest
6806  * Flags        - Send flags as an integer().
6807  * SendRef      - A unique id reference() for this (send) request.
6808  */
6809 
6810 static
nif_sendmsg(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])6811 ERL_NIF_TERM nif_sendmsg(ErlNifEnv*         env,
6812                          int                argc,
6813                          const ERL_NIF_TERM argv[])
6814 {
6815 #ifdef __WIN32__
6816     return enif_raise_exception(env, MKA(env, "notsup"));
6817 #else
6818     ERL_NIF_TERM     res, sockRef, sendRef, eMsg, eIOV;
6819     ESockDescriptor* descP;
6820     int              flags;
6821 
6822     ESOCK_ASSERT( argc == 5 );
6823 
6824     SGDBG( ("SOCKET", "nif_sendmsg -> entry with argc: %d\r\n", argc) );
6825 
6826     sockRef = argv[0]; // We need this in case we send abort (to the caller)
6827     eMsg    = argv[1];
6828     sendRef = argv[3];
6829     eIOV    = argv[4];
6830 
6831     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
6832         SGDBG( ("SOCKET", "nif_sendmsg -> get resource failed\r\n") );
6833         return enif_make_badarg(env);
6834     }
6835 
6836     /* Extract arguments and perform preliminary validation */
6837 
6838     if ((! enif_is_ref(env, sendRef)) ||
6839         (! IS_MAP(env, eMsg))) {
6840         SSDBG( descP, ("SOCKET", "nif_sendmsg -> argv decode failed\r\n") );
6841         return enif_make_badarg(env);
6842     }
6843     if (! GET_INT(env, argv[2], &flags)) {
6844         if (IS_INTEGER(env, argv[2]))
6845             return esock_make_error_integer_range(env, argv[2]);
6846         else
6847             return enif_make_badarg(env);
6848     }
6849 
6850     MLOCK(descP->writeMtx);
6851 
6852     SSDBG( descP,
6853            ("SOCKET", "nif_sendmsg(%T), {%d,0x%X} ->"
6854             "\r\n   SendRef:   %T"
6855             "\r\n   flags:     0x%X"
6856             "\r\n",
6857             sockRef, descP->sock, descP->writeState,
6858             sendRef, flags) );
6859 
6860     res = esock_sendmsg(env, descP, sockRef, sendRef, eMsg, flags, eIOV);
6861 
6862     MUNLOCK(descP->writeMtx);
6863 
6864     SSDBG( descP, ("SOCKET", "nif_sendmsg(%T) -> done with"
6865                    "\r\n   res: %T"
6866                    "\r\n", sockRef, res) );
6867 
6868     return res;
6869 
6870 #endif // #ifdef __WIN32__  #else
6871 }
6872 
6873 
6874 #ifndef __WIN32__
6875 static
esock_sendmsg(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM sendRef,ERL_NIF_TERM eMsg,int flags,ERL_NIF_TERM eIOV)6876 ERL_NIF_TERM esock_sendmsg(ErlNifEnv*       env,
6877                            ESockDescriptor* descP,
6878                            ERL_NIF_TERM     sockRef,
6879                            ERL_NIF_TERM     sendRef,
6880                            ERL_NIF_TERM     eMsg,
6881                            int              flags,
6882                            ERL_NIF_TERM     eIOV)
6883 {
6884     ERL_NIF_TERM  res, eAddr, eCtrl;
6885     ESockAddress  addr;
6886     struct msghdr msgHdr;
6887     ErlNifIOVec  *iovec = NULL;
6888     char*         ctrlBuf;
6889     size_t        ctrlBufLen,  ctrlBufUsed;
6890     ssize_t       dataSize,    sendmsg_result;
6891     ERL_NIF_TERM  writerCheck, tail;
6892 
6893     if (! IS_OPEN(descP->writeState))
6894         return esock_make_error(env, atom_closed);
6895 
6896     /* Connect and Write uses the same select flag
6897      * so they can not be simultaneous
6898      */
6899     if (descP->connectorP != NULL)
6900         return esock_make_error_invalid(env, atom_state);
6901 
6902     /* Ensure that we either have no current writer or we are it,
6903      * or enqueue this process if there is a current writer  */
6904     if (! send_check_writer(env, descP, sendRef, &writerCheck)) {
6905         SSDBG( descP,
6906                ("SOCKET", "esock_sendmsg {%d} -> writer check failed: "
6907                 "\r\n   %T\r\n", descP->sock, writerCheck) );
6908         return writerCheck;
6909     }
6910 
6911     /* Initiate the .name and .namelen fields depending on if
6912      * we have an address or not
6913      */
6914     if (! GET_MAP_VAL(env, eMsg, esock_atom_addr, &eAddr)) {
6915 
6916         SSDBG( descP, ("SOCKET",
6917                        "esock_sendmsg {%d} -> no address\r\n", descP->sock) );
6918 
6919         msgHdr.msg_name    = NULL;
6920         msgHdr.msg_namelen = 0;
6921     } else {
6922         msgHdr.msg_name    = (void*) &addr;
6923         msgHdr.msg_namelen = sizeof(addr);
6924         sys_memzero((char *) msgHdr.msg_name, msgHdr.msg_namelen);
6925 
6926         SSDBG( descP, ("SOCKET", "esock_sendmsg {%d} ->"
6927                        "\r\n   address: %T"
6928                        "\r\n", descP->sock, eAddr) );
6929 
6930         if (! esock_decode_sockaddr(env, eAddr,
6931                                     msgHdr.msg_name,
6932                                     &msgHdr.msg_namelen)) {
6933             SSDBG( descP, ("SOCKET",
6934                            "esock_sendmsg {%d} -> invalid address\r\n",
6935                            descP->sock) );
6936             return esock_make_invalid(env, esock_atom_addr);
6937         }
6938     }
6939 
6940     /* Extract the *mandatory* 'iov', which must be an erlang:iovec(),
6941      * from which we take at most IOV_MAX binaries
6942      */
6943     if ((! enif_inspect_iovec(NULL, data.iov_max, eIOV, &tail, &iovec))) {
6944         SSDBG( descP, ("SOCKET",
6945                        "esock_sendmsg {%d} -> not an iov\r\n",
6946                        descP->sock) );
6947 
6948         return esock_make_invalid(env, esock_atom_iov);
6949     }
6950 
6951     SSDBG( descP, ("SOCKET", "esock_sendmsg {%d} ->"
6952                    "\r\n   iovcnt: %lu"
6953                    "\r\n   tail:   %s"
6954                    "\r\n", descP->sock,
6955                    (unsigned long) iovec->iovcnt,
6956                    B2S(! enif_is_empty_list(env, tail))) );
6957 
6958     /* We now have an allocated iovec */
6959 
6960     eCtrl             = esock_atom_undefined;
6961     ctrlBufLen        = 0;
6962     ctrlBuf           = NULL;
6963 
6964     if (iovec->iovcnt > data.iov_max) {
6965         if (descP->type == SOCK_STREAM) {
6966             iovec->iovcnt = data.iov_max;
6967         } else {
6968             /* We can not send the whole packet in one sendmsg() call */
6969             SSDBG( descP, ("SOCKET",
6970                            "esock_sendmsg {%d} -> iovcnt > iov_max\r\n",
6971                            descP->sock) );
6972             res = esock_make_invalid(env, esock_atom_iov);
6973             goto done_free_iovec;
6974         }
6975     }
6976 
6977     dataSize = 0;
6978     {
6979         ERL_NIF_TERM h,   t;
6980         ErlNifBinary bin;
6981         size_t       i;
6982 
6983         /* Find out if there is remaining data in the tail.
6984          * Skip empty binaries otherwise break.
6985          * If 'tail' after loop exit is the empty list
6986          * there was no more data.  Otherwise there is more
6987          * data or the 'iov' is invalid.
6988          */
6989         for (;;) {
6990             if (enif_get_list_cell(env, tail, &h, &t) &&
6991                 enif_inspect_binary(env, h, &bin) &&
6992                 (bin.size == 0)) {
6993                 tail = t;
6994                 continue;
6995             } else
6996                 break;
6997         }
6998 
6999         if ((! enif_is_empty_list(env, tail)) &&
7000             (descP->type != SOCK_STREAM)) {
7001             /* We can not send the whole packet in one sendmsg() call */
7002             SSDBG( descP, ("SOCKET",
7003                            "esock_sendmsg {%d} -> invalid tail\r\n",
7004                            descP->sock) );
7005             res = esock_make_invalid(env, esock_atom_iov);
7006             goto done_free_iovec;
7007         }
7008 
7009         /* Calculate the data size */
7010 
7011         for (i = 0;  i < iovec->iovcnt;  i++) {
7012             size_t len = iovec->iov[i].iov_len;
7013             dataSize += len;
7014             if (dataSize < len) {
7015                 /* Overflow */
7016                 SSDBG( descP, ("SOCKET", "esock_sendmsg {%d} -> Overflow"
7017                                "\r\n   i:         %lu"
7018                                "\r\n   len:       %lu"
7019                                "\r\n   dataSize:  %ld"
7020                                "\r\n", descP->sock, (unsigned long) i,
7021                                (unsigned long) len, (long) dataSize) );
7022                 res = esock_make_invalid(env, esock_atom_iov);
7023                 goto done_free_iovec;
7024             }
7025         }
7026     }
7027     SSDBG( descP,
7028            ("SOCKET",
7029             "esock_sendmsg {%d} ->"
7030             "\r\n   iov length: %lu"
7031             "\r\n   data size:  %u"
7032             "\r\n",
7033             descP->sock,
7034             (unsigned long) iovec->iovcnt, (long) dataSize) );
7035 
7036     msgHdr.msg_iovlen = iovec->iovcnt;
7037     msgHdr.msg_iov    = iovec->iov;
7038 
7039     /* Extract the *optional* 'ctrl' */
7040     if (GET_MAP_VAL(env, eMsg, esock_atom_ctrl, &eCtrl)) {
7041         ctrlBufLen = descP->wCtrlSz;
7042         ctrlBuf    = (char*) MALLOC(ctrlBufLen);
7043         ESOCK_ASSERT( ctrlBuf != NULL );
7044     }
7045     SSDBG( descP, ("SOCKET", "esock_sendmsg {%d} -> optional ctrl: "
7046                    "\r\n   ctrlBuf:    %p"
7047                    "\r\n   ctrlBufLen: %lu"
7048                    "\r\n   eCtrl:      %T"
7049                    "\r\n", descP->sock,
7050                    ctrlBuf, (unsigned long) ctrlBufLen, eCtrl) );
7051 
7052     /* Decode the ctrl and initiate that part of the msghdr.
7053      */
7054     if (ctrlBuf != NULL) {
7055         if (! decode_cmsghdrs(env, descP,
7056                               eCtrl,
7057                               ctrlBuf, ctrlBufLen, &ctrlBufUsed)) {
7058             SSDBG( descP, ("SOCKET",
7059                            "esock_sendmsg {%d} -> invalid ctrl\r\n",
7060                            descP->sock) );
7061             res = esock_make_invalid(env, esock_atom_ctrl);
7062             goto done_free_iovec;
7063         }
7064     } else {
7065         ctrlBufUsed = 0;
7066     }
7067     msgHdr.msg_control    = ctrlBuf;
7068     msgHdr.msg_controllen = ctrlBufUsed;
7069 
7070     /* The msg_flags field is not used when sending,
7071      * but zero it just in case */
7072     msgHdr.msg_flags      = 0;
7073 
7074     ESOCK_CNT_INC(env, descP, sockRef, atom_write_tries, &descP->writeTries, 1);
7075 
7076     /* And now, try to send the message */
7077     sendmsg_result = sock_sendmsg(descP->sock, &msgHdr, flags);
7078 
7079     res = send_check_result(env, descP, sendmsg_result, dataSize,
7080                             (! enif_is_empty_list(env, tail)),
7081                             sockRef, sendRef);
7082 
7083  done_free_iovec:
7084     enif_free_iovec(iovec);
7085     if (ctrlBuf != NULL) FREE(ctrlBuf);
7086 
7087     SSDBG( descP,
7088            ("SOCKET", "esock_sendmsg {%d} ->"
7089             "\r\n   %T\r\n", descP->sock, res) );
7090     return res;
7091 }
7092 #endif // #ifndef __WIN32__
7093 
7094 
7095 
7096 #ifdef FOOBAR
7097 
7098 /* ----------------------------------------------------------------------
7099  * nif_writev / nif_sendv
7100  *
7101  * Description:
7102  * Send a message (vector) on a socket
7103  *
7104  * Arguments:
7105  * Socket (ref) - Points to the socket descriptor.
7106  * SendRef      - A unique id for this (send) request.
7107  * Data         - A vector of binaries
7108  * Flags        - Send flags.
7109  */
7110 
7111 static
nwritev(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sendRef,ERL_NIF_TERM data)7112 ERL_NIF_TERM nwritev(ErlNifEnv*       env,
7113                      ESockDescriptor* descP,
7114                      ERL_NIF_TERM     sendRef,
7115                      ERL_NIF_TERM     data)
7116 {
7117     ERL_NIF_TERM tail;
7118     ErlNifIOVec  vec;
7119     ErlNifIOVec* iovec = &vec;
7120     SysIOVec*    sysiovec;
7121     int          save_errno;
7122     int          iovcnt, n;
7123 
7124     if (! enif_inspect_iovec(env, MAX_VSZ, data, &tail, &iovec))
7125         return enif_make_badarg(env);
7126 
7127     if (enif_ioq_size(descP->outQ) > 0) {
7128         /* If the I/O queue contains data we enqueue the iovec
7129          * and then peek the data to write out of the queue.
7130          */
7131         if (!enif_ioq_enqv(q, iovec, 0))
7132             return -3;
7133 
7134         sysiovec = enif_ioq_peek(descP->outQ, &iovcnt);
7135 
7136     } else {
7137         /* If the I/O queue is empty we skip the trip through it. */
7138         iovcnt   = iovec->iovcnt;
7139         sysiovec = iovec->iov;
7140     }
7141 
7142     /* Attempt to write the data */
7143     n = writev(fd, sysiovec, iovcnt);
7144     saved_errno = errno;
7145 
7146     if (enif_ioq_size(descP->outQ) == 0) {
7147         /* If the I/O queue was initially empty we enqueue any
7148            remaining data into the queue for writing later. */
7149         if (n >= 0 && !enif_ioq_enqv(descP->outQ, iovec, n))
7150             return -3;
7151     } else {
7152         /* Dequeue any data that was written from the queue. */
7153         if (n > 0 && !enif_ioq_deq(descP->outQ, n, NULL))
7154             return -4;
7155     }
7156     /* return n, which is either number of bytes written or -1 if
7157        some error happened */
7158     errno = saved_errno;
7159     return n;
7160 }
7161 
7162 #endif // #ifdef FOOBAR
7163 
7164 
7165 
7166 /* ----------------------------------------------------------------------
7167  * nif_sendfile/1,4,5
7168  *
7169  * Description:
7170  * Send a file on a socket
7171  *
7172  * Arguments:
7173  * Socket (ref) - Points to the socket descriptor.
7174  *
7175  * SendRef      - A unique id reference() for this (send) request.
7176  *
7177  * Offset       - File offset to start from.
7178  * Count        - The number of bytes to send.
7179  *
7180  * InFileRef    - A file NIF resource.
7181  */
7182 
7183 static ERL_NIF_TERM
nif_sendfile(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])7184 nif_sendfile(ErlNifEnv*         env,
7185              int                argc,
7186              const ERL_NIF_TERM argv[])
7187 {
7188 #if defined(__WIN32__) || !defined(HAVE_SENDFILE)
7189     return enif_raise_exception(env, MKA(env, "notsup"));
7190 #else
7191     ESockDescriptor       *descP;
7192     ERL_NIF_TERM           sockRef, res;
7193 
7194     SGDBG( ("SOCKET", "nif_sendfile -> entry with argc: %d\r\n", argc) );
7195 
7196     if (argc < 1) {
7197         SGDBG( ("SOCKET", "nif_sendfile -> argc < 1\r\n") );
7198         return enif_make_badarg(env);
7199     }
7200     sockRef = argv[0];
7201     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) (&descP))) {
7202         SGDBG( ("SOCKET", "nif_sendfile -> get resource failed\r\n") );
7203         return enif_make_badarg(env);
7204     }
7205 
7206     if (argc < 2) { // argc == 1
7207 
7208         MLOCK(descP->writeMtx);
7209 
7210         SSDBG( descP,
7211                ("SOCKET", "nif_sendfile(%T), {%d,%d,0x%X} ->"
7212                 "\r\n",
7213                 sockRef,
7214                 descP->sock, descP->sendfileHandle, descP->writeState) );
7215 
7216         res = esock_sendfile_deferred_close(env, descP);
7217 
7218     } else {
7219         ERL_NIF_TERM sendRef;
7220         ErlNifSInt64 offset64;
7221         ErlNifUInt64 count64u;
7222         off_t        offset;
7223         size_t       count;
7224         BOOLEAN_T    a2ok;
7225 
7226         ESOCK_ASSERT( argc >= 4 );
7227 
7228         sendRef = argv[1];
7229         if ((! enif_is_ref(env, sendRef))) {
7230             SSDBG( descP,
7231                    ("SOCKET", "nif_sendfile -> argv[1] decode failed\r\n") );
7232             return enif_make_badarg(env);
7233         }
7234 
7235         if ((! (a2ok = GET_INT64(env, argv[2], &offset64))) ||
7236             (! GET_UINT64(env, argv[3], &count64u))) {
7237             if ((! IS_INTEGER(env, argv[3])) ||
7238                 (! IS_INTEGER(env, argv[3])))
7239                 return enif_make_badarg(env);
7240             if (! a2ok)
7241                 return esock_make_error_integer_range(env, argv[2]);
7242             else
7243                 return esock_make_error_integer_range(env, argv[3]);
7244         }
7245         offset = (off_t) offset64;
7246         if (offset64 != (ErlNifSInt64) offset)
7247             return esock_make_error_integer_range(env, argv[2]);
7248         count = (size_t) count64u;
7249         if (count64u != (ErlNifUInt64) count)
7250             return esock_make_error_integer_range(env, argv[3]);
7251 
7252         if (argc == 4) {
7253 
7254             MLOCK(descP->writeMtx);
7255 
7256             SSDBG( descP,
7257                    ("SOCKET", "nif_sendfile(%T), {%d,0x%X} ->"
7258                     "\r\n   sendRef: %T"
7259                     "\r\n   offset:  %ld"
7260                     "\r\n   count:   %ld"
7261                     "\r\n",
7262                     sockRef, descP->sock, descP->readState,
7263                     sendRef, (long) offset, (long) count) );
7264 
7265             res =
7266                 esock_sendfile_cont(env, descP, sockRef, sendRef,
7267                                     offset, count);
7268         } else {
7269             ERL_NIF_TERM  fRef;
7270 
7271             ESOCK_ASSERT( argc == 5 );
7272 
7273             fRef = argv[4];
7274             if ((! enif_is_ref(env, fRef)))
7275                 return enif_make_badarg(env);
7276 
7277             MLOCK(descP->writeMtx);
7278 
7279             SSDBG( descP,
7280                    ("SOCKET", "nif_sendfile(%T), {%d,0x%X} ->"
7281                     "\r\n   sendRef: %T"
7282                     "\r\n   offset:  %ld"
7283                 "\r\n   count:   %ld"
7284                     "\r\n   fRef:    %T"
7285                     "\r\n",
7286                     sockRef, descP->sock, descP->readState,
7287                     sendRef, (long) offset, (long) count, fRef) );
7288 
7289             res =
7290                 esock_sendfile_start(env, descP, sockRef, sendRef,
7291                                      offset, count, fRef);
7292         }
7293     }
7294 
7295     SSDBG( descP, ("SOCKET", "nif_sendfile(%T) -> done with"
7296                    "\r\n   res: %T"
7297                    "\r\n", sockRef, res) );
7298 
7299     MUNLOCK(descP->writeMtx);
7300 
7301     return res;
7302 
7303 #endif // #if defined(__WIN32__) || !defined(HAVE_SENDFILE)
7304 }
7305 
7306 #ifndef __WIN32__
7307 #ifdef HAVE_SENDFILE
7308 
7309 /* Start a sendfile() operation
7310  */
7311 static ERL_NIF_TERM
esock_sendfile_start(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM sendRef,off_t offset,size_t count,ERL_NIF_TERM fRef)7312 esock_sendfile_start(ErlNifEnv       *env,
7313                      ESockDescriptor *descP,
7314                      ERL_NIF_TERM     sockRef,
7315                      ERL_NIF_TERM     sendRef,
7316                      off_t            offset,
7317                      size_t           count,
7318                      ERL_NIF_TERM     fRef) {
7319     ERL_NIF_TERM           writerCheck;
7320     ssize_t                res;
7321     int                    err;
7322 
7323     SSDBG( descP, ("SOCKET",
7324                    "esock_sendfile_start(%T) {%d} -> sendRef: %T\r\n"
7325                    "   fRef:   %T\r\n"
7326                    "   offset: %lu\r\n"
7327                    "   count:  %lu\r\n",
7328                    sockRef, descP->sock, sendRef,
7329                    fRef, (unsigned long) offset, (unsigned long) count) );
7330 
7331     if (! IS_OPEN(descP->writeState)) {
7332         return esock_make_error(env, atom_closed);
7333     }
7334 
7335     /* Connect and Write uses the same select flag
7336      * so they can not be simultaneous
7337      */
7338     if (descP->connectorP != NULL) {
7339         return esock_make_error_invalid(env, atom_state);
7340     }
7341 
7342     /* Ensure that we either have no current writer or we are it,
7343      * or enqueue this process if there is a current writer
7344      */
7345     if (! send_check_writer(env, descP, sendRef, &writerCheck)) {
7346         SSDBG( descP, ("SOCKET",
7347                        "esock_sendfile_start {%d} -> writer check failed: "
7348                        "\r\n   %T\r\n", descP->sock, writerCheck) );
7349 
7350         /* Returns 'select' if current process got enqueued,
7351          * or exception invalid state if current process already
7352          * was enqueued
7353          */
7354         return writerCheck;
7355     }
7356 
7357     if (descP->sendfileHandle != INVALID_HANDLE)
7358         return esock_make_error_invalid(env, atom_state);
7359 
7360     /* Get a dup:ed file handle from prim_file_nif
7361      * through a NIF dyncall
7362      */
7363     {
7364         struct prim_file_nif_dyncall_dup dc_dup;
7365 
7366         dc_dup.op = prim_file_nif_dyncall_dup;
7367         dc_dup.result = EINVAL; // should not be needed
7368 
7369         /* Request the handle */
7370         if (enif_dynamic_resource_call(env,
7371                                        atom_prim_file, atom_efile, fRef,
7372                                        &dc_dup)
7373             != 0) {
7374             return
7375                 esock_sendfile_error(env, descP, sockRef,
7376                                      MKT2(env, esock_atom_invalid,
7377                                           atom_efile));
7378         }
7379         if (dc_dup.result != 0) {
7380             return
7381                 esock_sendfile_errno(env, descP, sockRef, dc_dup.result);
7382         }
7383         descP->sendfileHandle = dc_dup.handle;
7384     }
7385 
7386     SSDBG( descP, ("SOCKET",
7387                    "esock_sendfile_start(%T) {%d} -> sendRef: %T\r\n"
7388                    "   sendfileHandle: %d\r\n",
7389                    sockRef, descP->sock, sendRef,
7390                    descP->sendfileHandle) );
7391 
7392     if (descP->sendfileCountersP == NULL) {
7393         descP->sendfileCountersP = MALLOC(sizeof(ESockSendfileCounters));
7394         *descP->sendfileCountersP = initESockSendfileCounters;
7395     }
7396 
7397     ESOCK_CNT_INC(env, descP, sockRef,
7398                   atom_sendfile_tries, &descP->sendfileCountersP->tries, 1);
7399     descP->sendfileCountersP->maxCnt = 0;
7400 
7401     res = esock_sendfile(env, descP, sockRef, offset, &count, &err);
7402 
7403     if (res < 0) { // Terminal error
7404 
7405         (void) close(descP->sendfileHandle);
7406         descP->sendfileHandle = INVALID_HANDLE;
7407 
7408         return esock_sendfile_errno(env, descP, sockRef, err);
7409 
7410     } else if (res > 0) { // Retry by select
7411 
7412         if (descP->currentWriterP == NULL) {
7413             int mon_res;
7414 
7415             /* Register writer as current */
7416             ESOCK_ASSERT( enif_self(env, &descP->currentWriter.pid) != NULL );
7417             mon_res =
7418                 MONP("sendfile -> current writer",
7419                      env, descP,
7420                      &descP->currentWriter.pid,
7421                      &descP->currentWriter.mon);
7422             ESOCK_ASSERT( mon_res >= 0 );
7423 
7424             if (mon_res > 0) {
7425                 /* Caller died already, can happen for dirty NIFs */
7426 
7427                 (void) close(descP->sendfileHandle);
7428                 descP->sendfileHandle = INVALID_HANDLE;
7429 
7430                 return
7431                     esock_sendfile_error(env, descP, sockRef,
7432                                          MKT2(env,
7433                                               esock_atom_invalid,
7434                                               esock_atom_not_owner));
7435             }
7436             ESOCK_ASSERT( descP->currentWriter.env == NULL );
7437             descP->currentWriter.env = esock_alloc_env("current-writer");
7438             descP->currentWriter.ref =
7439                 CP_TERM(descP->currentWriter.env, sendRef);
7440             descP->currentWriterP = &descP->currentWriter;
7441         }
7442         // else current writer is already registered by requestor_pop()
7443 
7444         return esock_sendfile_select(env, descP, sockRef, sendRef, count);
7445 
7446     } else { // res == 0: Done
7447         return esock_sendfile_ok(env, descP, sockRef, count);
7448     }
7449 }
7450 
7451 /* Continue an ongoing sendfile operation
7452  */
7453 static ERL_NIF_TERM
esock_sendfile_cont(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM sendRef,off_t offset,size_t count)7454 esock_sendfile_cont(ErlNifEnv       *env,
7455                     ESockDescriptor *descP,
7456                     ERL_NIF_TERM     sockRef,
7457                     ERL_NIF_TERM     sendRef,
7458                     off_t            offset,
7459                     size_t           count) {
7460     ErlNifPid              caller;
7461     ssize_t                res;
7462     int                    err;
7463 
7464     SSDBG( descP, ("SOCKET",
7465                    "esock_sendfile_cont(%T) {%d} -> sendRef: %T\r\n",
7466                    sockRef, descP->sock, sendRef) );
7467 
7468     if (! IS_OPEN(descP->writeState))
7469         return esock_make_error(env, atom_closed);
7470 
7471     /* Connect and Write uses the same select flag
7472      * so they can not be simultaneous
7473      */
7474     if (descP->connectorP != NULL)
7475         return esock_make_error_invalid(env, atom_state);
7476 
7477     /* Verify that this process has a sendfile operation in progress */
7478     ESOCK_ASSERT( enif_self(env, &caller) != NULL );
7479     if ((descP->currentWriterP == NULL) ||
7480         (descP->sendfileHandle == INVALID_HANDLE) ||
7481         (COMPARE_PIDS(&descP->currentWriter.pid, &caller) != 0)) {
7482         //
7483         return esock_raise_invalid(env, atom_state);
7484     }
7485 
7486     res = esock_sendfile(env, descP, sockRef, offset, &count, &err);
7487 
7488     if (res < 0) { // Terminal error
7489 
7490         (void) close(descP->sendfileHandle);
7491         descP->sendfileHandle = INVALID_HANDLE;
7492 
7493         return esock_sendfile_errno(env, descP, sockRef, err);
7494 
7495      } else if (res > 0) { // Retry by select
7496 
7497         /* Overwrite current writer registration */
7498         enif_clear_env(descP->currentWriter.env);
7499         descP->currentWriter.ref =
7500             CP_TERM(descP->currentWriter.env, sendRef);
7501 
7502         return esock_sendfile_select(env, descP, sockRef, sendRef, count);
7503 
7504     } else { // res == 0: Done
7505         return esock_sendfile_ok(env, descP, sockRef, count);
7506     }
7507 }
7508 
7509 /* Deferred close of the dup:ed file descriptor
7510  */
7511 static ERL_NIF_TERM
esock_sendfile_deferred_close(ErlNifEnv * env,ESockDescriptor * descP)7512 esock_sendfile_deferred_close(ErlNifEnv       *env,
7513                               ESockDescriptor *descP) {
7514     if (descP->sendfileHandle == INVALID_HANDLE)
7515         return esock_make_error_invalid(env, atom_state);
7516 
7517     (void) close(descP->sendfileHandle);
7518     descP->sendfileHandle = INVALID_HANDLE;
7519 
7520     return esock_atom_ok;
7521 }
7522 
7523 /* Platform independent sendfile() function
7524  *
7525  * Return < 0  for terminal error
7526  *        0    for done
7527  *        > 0  for retry with select
7528  */
7529 static int
esock_sendfile(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,off_t offset,size_t * countP,int * errP)7530 esock_sendfile(ErlNifEnv       *env,
7531                ESockDescriptor *descP,
7532                ERL_NIF_TERM     sockRef,
7533                off_t            offset,
7534                size_t          *countP,
7535                int             *errP) {
7536 
7537     size_t pkgSize = 0; // Total sent in this call
7538 
7539     SSDBG( descP, ("SOCKET",
7540                    "esock_sendfile(%T) {%d,%d}\r\n",
7541                    sockRef, descP->sock, descP->sendfileHandle) );
7542 
7543     for (;;) {
7544         size_t  chunk_size = (size_t) 0x20000000UL; // 0.5 GB
7545         size_t  bytes_sent;
7546         ssize_t res;
7547         int     error;
7548 
7549         /* *countP == 0 means send the whole file - use chunk size */
7550         if ((*countP > 0) && (*countP < chunk_size))
7551             chunk_size = *countP;
7552 
7553         {
7554             /* Platform dependent code:
7555              *   update and check offset, set and check bytes_sent, and
7556              *       set res to >= 0 and error to 0, or
7557              *       set res to < 0 and error to sock_errno()
7558              */
7559 #if defined (__linux__)
7560 
7561             off_t prev_offset;
7562 
7563             prev_offset = offset;
7564             res =
7565                 sendfile(descP->sock, descP->sendfileHandle,
7566                          &offset, chunk_size);
7567             error = (res < 0) ? sock_errno() : 0;
7568 
7569             ESOCK_ASSERT( offset >= prev_offset );
7570             ESOCK_ASSERT( (off_t) chunk_size >= (offset - prev_offset) );
7571             bytes_sent = (size_t) (offset - prev_offset);
7572 
7573             SSDBG( descP,
7574                    ("SOCKET",
7575                     "esock_sendfile(%T) {%d,%d}"
7576                     "\r\n   res:         %d"
7577                     "\r\n   bytes_sent:  %lu"
7578                     "\r\n   error:       %d"
7579                     "\r\n",
7580                     sockRef, descP->sock, descP->sendfileHandle,
7581                     res, (unsigned long) bytes_sent, error) );
7582 
7583 #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__DARWIN__)
7584 
7585             off_t sbytes;
7586 
7587 #if defined(__DARWIN__)
7588             sbytes = (off_t) chunk_size;
7589             res = (ssize_t)
7590                 sendfile(descP->sendfileHandle, descP->sock, offset,
7591                          &sbytes, NULL, 0);
7592 #else
7593             sbytes = 0;
7594             res = (ssize_t)
7595                 sendfile(descP->sendfileHandle, descP->sock, offset,
7596                          chunk_size, NULL, &sbytes, 0);
7597 #endif
7598             error = (res < 0) ? sock_errno() : 0;
7599 
7600             /* For an error return, we do not dare trust that sbytes is set
7601              * unless the error is ERRNO_BLOCK or EINTR
7602              * - the man page is to vague
7603              */
7604             if ((res < 0) && (error != ERRNO_BLOCK) && (error != EINTR)) {
7605                 sbytes = 0;
7606             } else {
7607                 ESOCK_ASSERT( sbytes >= 0 );
7608                 ESOCK_ASSERT( (off_t) chunk_size >= sbytes );
7609                 ESOCK_ASSERT( offset + sbytes >= offset );
7610                 offset += sbytes;
7611             }
7612             bytes_sent = (size_t) sbytes;
7613 
7614             SSDBG( descP,
7615                    ("SOCKET",
7616                     "esock_sendfile(%T) {%d,%d}"
7617                     "\r\n   res:         %d"
7618                     "\r\n   bytes_sent:  %lu"
7619                     "\r\n   error:       %d"
7620                     "\r\n",
7621                     sockRef, descP->sock, descP->sendfileHandle,
7622                     res, (unsigned long) bytes_sent, error) );
7623 
7624 #elif defined(__sun) && defined(__SVR4) && defined(HAVE_SENDFILEV)
7625 
7626             sendfilevec_t sfvec[1];
7627 
7628             sfvec[0].sfv_fd = descP->sendfileHandle;
7629             sfvec[0].sfv_flag = 0;
7630             sfvec[0].sfv_off = offset;
7631             sfvec[0].sfv_len = chunk_size;
7632 
7633             res = sendfilev(descP->sock, sfvec, NUM(sfvec), &bytes_sent);
7634             error = (res < 0) ? sock_errno() : 0;
7635 
7636             SSDBG( descP,
7637                    ("SOCKET",
7638                     "esock_sendfile(%T) {%d,%d}"
7639                     "\r\n   res:         %d"
7640                     "\r\n   bytes_sent:  %lu"
7641                     "\r\n   error:       %d"
7642                     "\r\n",
7643                     sockRef, descP->sock, descP->sendfileHandle,
7644                     res, (unsigned long) bytes_sent, error) );
7645 
7646             if ((res < 0) && (error == EINVAL)) {
7647                 /* On e.b SunOS 5.10 using sfv_len > file size
7648                  * lands here - we regard this as a succesful send.
7649                  * All other causes for EINVAL are avoided,
7650                  * except for .sfv_fd not seekable, which would
7651                  * give bytes_sent == 0 that we would interpret
7652                  * as end of file, which is kind of true.
7653                  */
7654                 res = 0;
7655             }
7656             ESOCK_ASSERT( chunk_size >= bytes_sent );
7657             ESOCK_ASSERT( offset + bytes_sent >= offset );
7658             offset += bytes_sent;
7659 
7660 #else
7661 #error "Unsupported sendfile syscall; update configure test."
7662 #endif
7663 
7664             ESOCK_CNT_INC(env, descP, sockRef,
7665                           atom_sendfile, &descP->sendfileCountersP->cnt, 1);
7666 
7667             if (bytes_sent != 0) {
7668 
7669                 pkgSize += bytes_sent;
7670 
7671                 ESOCK_CNT_INC(env, descP, sockRef,
7672                               atom_sendfile_pkg,
7673                               &descP->sendfileCountersP->pkg,
7674                               1);
7675                 ESOCK_CNT_INC(env, descP, sockRef,
7676                               atom_sendfile_byte,
7677                               &descP->sendfileCountersP->byteCnt,
7678                               bytes_sent);
7679 
7680                 if (pkgSize > descP->sendfileCountersP->pkgMax)
7681                     descP->sendfileCountersP->pkgMax = pkgSize;
7682                 if ((descP->sendfileCountersP->maxCnt += bytes_sent)
7683                     > descP->sendfileCountersP->max)
7684                     descP->sendfileCountersP->max =
7685                         descP->sendfileCountersP->maxCnt;
7686             }
7687 
7688             /* *countP == 0 means send whole file */
7689             if (*countP > 0) {
7690 
7691                 *countP -= bytes_sent;
7692 
7693                 if (*countP == 0) { // All sent
7694                     *countP = pkgSize;
7695                     return 0;
7696                 }
7697             }
7698 
7699             if (res < 0) {
7700                 if (error == ERRNO_BLOCK) {
7701                     *countP = pkgSize;
7702                     return 1;
7703                 }
7704                 if (error == EINTR)
7705                     continue;
7706                 *errP = error;
7707                 return -1;
7708             }
7709 
7710             if (bytes_sent == 0) { // End of input file
7711                 *countP = pkgSize;
7712                 return 0;
7713             }
7714         }
7715     } // for (;;)
7716 }
7717 
7718 static ERL_NIF_TERM
esock_sendfile_errno(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,int err)7719 esock_sendfile_errno(ErlNifEnv             *env,
7720                      ESockDescriptor       *descP,
7721                      ERL_NIF_TERM           sockRef,
7722                      int                    err) {
7723     ERL_NIF_TERM reason;
7724 
7725     reason = MKA(env, erl_errno_id(err));
7726     return esock_sendfile_error(env, descP, sockRef, reason);
7727 }
7728 
7729 static ERL_NIF_TERM
esock_sendfile_error(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM reason)7730 esock_sendfile_error(ErlNifEnv             *env,
7731                      ESockDescriptor       *descP,
7732                      ERL_NIF_TERM           sockRef,
7733                      ERL_NIF_TERM           reason) {
7734 
7735     if (descP->sendfileCountersP == NULL) {
7736         descP->sendfileCountersP = MALLOC(sizeof(ESockSendfileCounters));
7737         *descP->sendfileCountersP = initESockSendfileCounters;
7738     }
7739 
7740     ESOCK_CNT_INC(env, descP, sockRef,
7741                   atom_sendfile_fails,
7742                   &descP->sendfileCountersP->fails, 1);
7743 
7744     SSDBG( descP, ("SOCKET",
7745                    "esock_sendfile_error(%T) {%d} -> error: %T\r\n",
7746                    sockRef, descP->sock, reason) );
7747 
7748     /* XXX Should we have special treatment for EINVAL,
7749      * such as to only fail current operation and activate
7750      * the next from the queue?
7751      */
7752 
7753     if (descP->currentWriterP != NULL) {
7754 
7755         (void) DEMONP("esock_sendfile_error",
7756                       env, descP, &descP->currentWriter.mon);
7757 
7758         /* Fail all queued writers */
7759         requestor_release("esock_sendfile_error",
7760                           env, descP, &descP->currentWriter);
7761         send_error_waiting_writers(env, descP, sockRef, reason);
7762         descP->currentWriterP = NULL;
7763 
7764     }
7765 
7766     return esock_make_error(env, reason);
7767 }
7768 
7769 static ERL_NIF_TERM
esock_sendfile_select(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM sendRef,size_t count)7770 esock_sendfile_select(ErlNifEnv       *env,
7771                       ESockDescriptor *descP,
7772                       ERL_NIF_TERM     sockRef,
7773                       ERL_NIF_TERM     sendRef,
7774                       size_t           count) {
7775     int sres;
7776 
7777     /* Select write for this process */
7778     sres =
7779         esock_select_write(env, descP->sock, descP, NULL, sockRef, sendRef);
7780     if (sres < 0) {
7781         ERL_NIF_TERM reason;
7782 
7783         /* Internal select error */
7784         (void) DEMONP("esock_sendfile_select - failed",
7785                       env, descP, &descP->currentWriter.mon);
7786 
7787         /* Fail all queued writers */
7788         reason = MKT2(env, atom_select_write, MKI(env, sres));
7789         requestor_release("esock_sendfile_select_fail",
7790                           env, descP, &descP->currentWriter);
7791         send_error_waiting_writers(env, descP, sockRef, reason);
7792         descP->currentWriterP = NULL;
7793 
7794         (void) close(descP->sendfileHandle);
7795         descP->sendfileHandle = INVALID_HANDLE;
7796 
7797         return enif_raise_exception(env, reason);
7798 
7799     } else {
7800         ErlNifUInt64 bytes_sent;
7801 
7802         SSDBG( descP,
7803                ("SOCKET", "esock_sendfile_select(%T) {%d} -> "
7804                 "sendRef (%T)\r\n"
7805                 "count:  %lu\r\n",
7806                 sockRef, descP->sock, sendRef, (unsigned long) count) );
7807 
7808         ESOCK_CNT_INC(env, descP, sockRef,
7809                       atom_sendfile_waits,
7810                       &descP->sendfileCountersP->waits,
7811                       1);
7812         descP->writeState |= ESOCK_STATE_SELECTED;
7813         bytes_sent = (ErlNifUInt64) count;
7814 
7815         return MKT2(env, atom_select, MKUI64(env, bytes_sent));
7816     }
7817 }
7818 
7819 static ERL_NIF_TERM
esock_sendfile_ok(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,size_t count)7820 esock_sendfile_ok(ErlNifEnv       *env,
7821                   ESockDescriptor *descP,
7822                   ERL_NIF_TERM     sockRef,
7823                   size_t           count) {
7824     ErlNifUInt64 bytes_sent64u;
7825 
7826     SSDBG( descP,
7827            ("SOCKET", "esock_sendfile_ok(%T) {%d} -> "
7828             "everything written (%lu) - done\r\n",
7829             sockRef, descP->sock, (unsigned long) count) );
7830 
7831     if (descP->currentWriterP != NULL) {
7832 
7833         (void) DEMONP("esock_sendfile_ok -> current writer",
7834                       env, descP, &descP->currentWriter.mon);
7835 
7836         /*
7837          * Ok, this write is done maybe activate the next (if any)
7838          */
7839         if (! activate_next_writer(env, descP, sockRef)) {
7840 
7841             SSDBG( descP,
7842                    ("SOCKET",
7843                     "esock_sendfile_ok(%T) {%d} -> no more writers\r\n",
7844                     sockRef, descP->sock) );
7845 
7846             descP->currentWriterP = NULL;
7847         }
7848     }
7849 
7850     descP->writePkgMaxCnt = 0;
7851     bytes_sent64u = (ErlNifUInt64) count;
7852 
7853     (void) close(descP->sendfileHandle);
7854     descP->sendfileHandle = INVALID_HANDLE;
7855 
7856     return esock_make_ok2(env, MKUI64(env, bytes_sent64u));
7857 }
7858 
7859 #endif // #ifdef HAVE_SENDFILE
7860 #endif // #ifndef __WIN32__
7861 
7862 
7863 
7864 /* ----------------------------------------------------------------------
7865  * nif_recv
7866  *
7867  * Description:
7868  * Receive a message on a socket.
7869  * Normally used only on a connected socket!
7870  * If we are trying to read > 0 bytes, then that is what we do.
7871  * But if we have specified 0 bytes, then we want to read
7872  * whatever is in the buffers (everything it got).
7873  *
7874  * Arguments:
7875  * Socket (ref) - NIF resource reference() to the socket descriptor.
7876  * Length       - The number of bytes to receive; integer().
7877  * Flags        - Receive flags; integer().
7878  * RecvRef      - A unique reference() id for this (send) request | 'poll'
7879  */
7880 
7881 static
nif_recv(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])7882 ERL_NIF_TERM nif_recv(ErlNifEnv*         env,
7883                       int                argc,
7884                       const ERL_NIF_TERM argv[])
7885 {
7886 #ifdef __WIN32__
7887     return enif_raise_exception(env, MKA(env, "notsup"));
7888 #else
7889     ESockDescriptor* descP;
7890     ERL_NIF_TERM     sockRef, recvRef;
7891     ErlNifUInt64     elen;
7892     ssize_t          len; /* ssize_t due to the return type of recv() */
7893     int              flags;
7894     ERL_NIF_TERM     res;
7895     BOOLEAN_T        a1ok;
7896 
7897     ESOCK_ASSERT( argc == 4 );
7898 
7899     sockRef = argv[0]; // We need this in case we send abort (to the caller)
7900     recvRef = argv[3];
7901 
7902     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
7903         return enif_make_badarg(env);
7904     }
7905 
7906     if ((! enif_is_ref(env, recvRef)) &&
7907         (COMPARE(recvRef, atom_zero) != 0)) {
7908         return enif_make_badarg(env);
7909     }
7910     if ((! (a1ok = GET_UINT64(env, argv[1], &elen))) ||
7911         (! GET_INT(env, argv[2], &flags))) {
7912         if ((! IS_INTEGER(env, argv[1])) ||
7913             (! IS_INTEGER(env, argv[2])))
7914             return enif_make_badarg(env);
7915 
7916         if (! a1ok)
7917             return esock_make_error_integer_range(env, argv[1]);
7918         return
7919             esock_make_error_integer_range(env, argv[2]);
7920     }
7921     len = (ssize_t) elen;
7922     if (elen != (ErlNifUInt64) len)
7923         return esock_make_error_integer_range(env, elen);
7924 
7925     MLOCK(descP->readMtx);
7926 
7927     SSDBG( descP,
7928            ("SOCKET", "nif_recv(%T), {%d,0x%X} ->"
7929             "\r\n   recvRef: %T"
7930             "\r\n   len:     %ld"
7931             "\r\n   flags:   0x%X"
7932             "\r\n",
7933             sockRef, descP->sock, descP->readState,
7934             recvRef, (long) len, flags) );
7935 
7936     /* We need to handle the case when another process tries
7937      * to receive at the same time.
7938      * If the current recv could not read its entire package
7939      * this time (resulting in an select). The read of the
7940      * other process must be made to wait until current
7941      * is done!
7942      */
7943 
7944     res = esock_recv(env, descP, sockRef, recvRef, len, flags);
7945 
7946     SSDBG( descP, ("SOCKET", "nif_recv(%T) -> done"
7947                    "\r\n", sockRef) );
7948 
7949     MUNLOCK(descP->readMtx);
7950 
7951     return res;
7952 
7953 #endif // #ifdef __WIN32__  #else
7954 }
7955 
7956 
7957 /* The (read) buffer handling should be optimized!
7958  * But for now we make it easy for ourselves by
7959  * allocating a binary (of the specified or default
7960  * size) and then throwing it away...
7961  */
7962 #ifndef __WIN32__
7963 static
esock_recv(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef,ssize_t len,int flags)7964 ERL_NIF_TERM esock_recv(ErlNifEnv*       env,
7965                         ESockDescriptor* descP,
7966                         ERL_NIF_TERM     sockRef,
7967                         ERL_NIF_TERM     recvRef,
7968                         ssize_t          len,
7969                         int              flags)
7970 {
7971     ssize_t      read;
7972     ErlNifBinary buf;
7973     ERL_NIF_TERM readerCheck;
7974     int          save_errno;
7975     size_t       bufSz = (len != 0 ? len : descP->rBufSz);
7976 
7977     SSDBG( descP, ("SOCKET", "esock_recv {%d} -> entry with"
7978                    "\r\n   count,size: (%ld:%u:%lu)"
7979                    "\r\n", descP->sock,
7980                    (long) len, descP->rNumCnt, (unsigned long) bufSz) );
7981 
7982     if (! IS_OPEN(descP->readState))
7983         return esock_make_error(env, atom_closed);
7984 
7985     /* Accept and Read uses the same select flag
7986      * so they can not be simultaneous
7987      */
7988     if (descP->currentAcceptorP != NULL)
7989         return esock_make_error_invalid(env, atom_state);
7990 
7991     /* Ensure that we either have no current reader or that we are it,
7992      * or enqueue this process if there is a current reader */
7993     if (! recv_check_reader(env, descP, recvRef, &readerCheck)) {
7994         SSDBG( descP,
7995                ("SOCKET", "esock_recv {%d} -> reader check failed: "
7996                 "\r\n   %T\r\n", descP->sock, readerCheck) );
7997         return readerCheck;
7998     }
7999 
8000     /* Allocate a buffer:
8001      * Either as much as we want to read or (if zero (0)) use the "default"
8002      * size (what has been configured).
8003      */
8004     ESOCK_ASSERT( ALLOC_BIN(bufSz, &buf) );
8005 
8006     // If it fails (read = -1), we need errno...
8007     SSDBG( descP, ("SOCKET", "esock_recv {%d} -> try read (%lu)\r\n",
8008                    descP->sock, (unsigned long) buf.size) );
8009 
8010     ESOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1);
8011 
8012     read = sock_recv(descP->sock, buf.data, buf.size, flags);
8013     if (ESOCK_IS_ERROR(read)) {
8014         save_errno = sock_errno();
8015     } else {
8016         save_errno = 0; // The value does not actually matter in this case
8017     }
8018 
8019     SSDBG( descP, ("SOCKET",
8020                    "esock_recv {%d} -> read: %ld (%d)\r\n",
8021                    descP->sock, (long) read, save_errno) );
8022 
8023     return recv_check_result(env, descP, read, len, save_errno,
8024                              &buf, sockRef, recvRef);
8025 }
8026 #endif // #ifndef __WIN32__
8027 
8028 
8029 
8030 /* ----------------------------------------------------------------------
8031  * nif_recvfrom
8032  *
8033  * Description:
8034  * Receive a message on a socket.
8035  * Normally used only on a (un-) connected socket!
8036  * If a buffer size = 0 is specified, then the we will use the default
8037  * buffer size for this socket (whatever has been configured).
8038  *
8039  * Arguments:
8040  * Socket (ref) - NIF resource reference() to the socket descriptor.
8041  * BufSz        - integer() ize of the buffer
8042  *                into which we put the received message.
8043  * Flags        - Receive flags; integer().
8044  * RecvRef      - A unique reference() id for this recv request.
8045  *
8046  * <KOLLA>
8047  *
8048  * How do we handle if the peek flag is set? We need to basically keep
8049  * track of if we expect any data from the read. Regardless of the
8050  * number of bytes we try to read.
8051  *
8052  * </KOLLA>
8053  */
8054 
8055 static
nif_recvfrom(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])8056 ERL_NIF_TERM nif_recvfrom(ErlNifEnv*         env,
8057                           int                argc,
8058                           const ERL_NIF_TERM argv[])
8059 {
8060 #ifdef __WIN32__
8061     return enif_raise_exception(env, MKA(env, "notsup"));
8062 #else
8063     ESockDescriptor* descP;
8064     ERL_NIF_TERM     sockRef, recvRef;
8065     ErlNifUInt64     elen;
8066     ssize_t          len; /* ssize_t due to the return type of recvfrom() */
8067     int              flags;
8068     ERL_NIF_TERM     res;
8069     BOOLEAN_T        a1ok;
8070 
8071     ESOCK_ASSERT( argc == 4 );
8072 
8073     SGDBG( ("SOCKET", "nif_recvfrom -> entry with argc: %d\r\n", argc) );
8074 
8075     sockRef = argv[0]; // We need this in case we send abort (to the caller)
8076     recvRef = argv[3];
8077 
8078     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
8079         return enif_make_badarg(env);
8080     }
8081 
8082     /* Extract arguments and perform preliminary validation */
8083 
8084     if ((! enif_is_ref(env, recvRef)) &&
8085         (COMPARE(recvRef, atom_zero) != 0)) {
8086         return enif_make_badarg(env);
8087     }
8088 
8089     if ((! (a1ok = GET_UINT64(env, argv[1], &elen))) ||
8090         (! GET_INT(env, argv[2], &flags))) {
8091         if ((! IS_INTEGER(env, argv[1])) ||
8092             (! IS_INTEGER(env, argv[2])))
8093             return enif_make_badarg(env);
8094 
8095         if (! a1ok)
8096             return esock_make_error_integer_range(env, argv[1]);
8097         return
8098             esock_make_error_integer_range(env, argv[2]);
8099     }
8100     len = (ssize_t) elen;
8101     if (elen != (ErlNifUInt64) len)
8102         return esock_make_error_integer_range(env, elen);
8103 
8104     MLOCK(descP->readMtx);
8105 
8106     SSDBG( descP,
8107            ("SOCKET", "nif_recvfrom(%T), {%d,0x%X} ->"
8108             "\r\n   recvRef: %T"
8109             "\r\n   len:     %ld"
8110             "\r\n   flags:   0x%X"
8111             "\r\n",
8112             sockRef, descP->sock, descP->readState,
8113             recvRef, (long) len, flags) );
8114 
8115     /* <KOLLA>
8116      * We need to handle the case when another process tries
8117      * to receive at the same time.
8118      * If the current recv could not read its entire package
8119      * this time (resulting in an select). The read of the
8120      * other process must be made to wait until current
8121      * is done!
8122      * Basically, we need a read queue!
8123      *
8124      * A 'reading' field (boolean), which is set if we did
8125      * not manage to read the entire message and reset every
8126      * time we do.
8127      * </KOLLA>
8128      */
8129 
8130     res = esock_recvfrom(env, descP, sockRef, recvRef, len, flags);
8131 
8132     SSDBG( descP, ("SOCKET", "nif_recvfrom(%T) -> done"
8133                    "\r\n", sockRef) );
8134 
8135     MUNLOCK(descP->readMtx);
8136 
8137     return res;
8138 #endif // #ifdef __WIN32__  #else
8139 }
8140 
8141 
8142 /* The (read) buffer handling *must* be optimized!
8143  * But for now we make it easy for ourselves by
8144  * allocating a binary (of the specified or default
8145  * size) and then throwing it away...
8146  */
8147 #ifndef __WIN32__
8148 static
esock_recvfrom(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef,ssize_t len,int flags)8149 ERL_NIF_TERM esock_recvfrom(ErlNifEnv*       env,
8150                             ESockDescriptor* descP,
8151                             ERL_NIF_TERM     sockRef,
8152                             ERL_NIF_TERM     recvRef,
8153                             ssize_t          len,
8154                             int              flags)
8155 {
8156     ESockAddress  fromAddr;
8157     SOCKLEN_T     addrLen;
8158     ssize_t       read;
8159     int           save_errno;
8160     ErlNifBinary  buf;
8161     ERL_NIF_TERM  readerCheck;
8162     size_t        bufSz = (len != 0 ? len : descP->rBufSz);
8163 
8164     SSDBG( descP, ("SOCKET", "esock_recvfrom {%d} -> entry with"
8165                    "\r\n   bufSz: %d"
8166                    "\r\n", descP->sock, bufSz) );
8167 
8168     if (! IS_OPEN(descP->readState))
8169         return esock_make_error(env, atom_closed);
8170 
8171     /* Accept and Read uses the same select flag
8172      * so they can not be simultaneous
8173      */
8174     if (descP->currentAcceptorP != NULL)
8175         return esock_make_error_invalid(env, atom_state);
8176 
8177     /* Ensure that we either have no current reader or that we are it,
8178      * or enqueue this process if there is a current reader */
8179     if (! recv_check_reader(env, descP, recvRef, &readerCheck)) {
8180         SSDBG( descP,
8181                ("SOCKET", "esock_recv {%d} -> reader check failed: "
8182                 "\r\n   %T\r\n", descP->sock, readerCheck) );
8183         return readerCheck;
8184     }
8185 
8186     /* Allocate a buffer:
8187      * Either as much as we want to read or (if zero (0)) use the "default"
8188      * size (what has been configured).
8189      */
8190     ESOCK_ASSERT( ALLOC_BIN(bufSz, &buf) );
8191 
8192     ESOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1);
8193 
8194     addrLen = sizeof(fromAddr);
8195     sys_memzero((char*) &fromAddr, addrLen);
8196 
8197     read = sock_recvfrom(descP->sock, buf.data, buf.size, flags,
8198                          &fromAddr.sa, &addrLen);
8199     if (ESOCK_IS_ERROR(read))
8200         save_errno = sock_errno();
8201     else
8202         save_errno = 0; // The value does not actually matter in this case
8203 
8204     return recvfrom_check_result(env, descP, read, save_errno,
8205                                  &buf, &fromAddr, addrLen,
8206                                  sockRef, recvRef);
8207 }
8208 #endif // #ifndef __WIN32__
8209 
8210 
8211 
8212 /* ----------------------------------------------------------------------
8213  * nif_recvmsg
8214  *
8215  * Description:
8216  * Receive a message on a socket.
8217  * Normally used only on a (un-) connected socket!
8218  * If a buffer size = 0 is specified, then we will use the default
8219  * buffer size for this socket (whatever has been configured).
8220  * If ctrl (buffer) size = 0 is specified, then the default ctrl
8221  * (buffer) size is used (1024).
8222  *
8223  * Arguments:
8224  * Socket (ref) - NIF resource reference() to the socket descriptor.
8225  * BufSz        - Size of the buffer into which we put the received message;
8226  *                integer().
8227  * CtrlSz       - Size of the ctrl (buffer) into which we put the received
8228  *                ancillary data; integer().
8229  * Flags        - Receive flags; integer().
8230  * RecvRef      - A unique reference() id for this (send) request.
8231  *
8232  * <KOLLA>
8233  *
8234  * How do we handle if the peek flag is set? We need to basically keep
8235  * track of if we expect any data from the read. Regardless of the
8236  * number of bytes we try to read.
8237  *
8238  * </KOLLA>
8239  */
8240 
8241 static
nif_recvmsg(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])8242 ERL_NIF_TERM nif_recvmsg(ErlNifEnv*         env,
8243                          int                argc,
8244                          const ERL_NIF_TERM argv[])
8245 {
8246 #ifdef __WIN32__
8247     return enif_raise_exception(env, MKA(env, "notsup"));
8248 #else
8249     ESockDescriptor* descP;
8250     ERL_NIF_TERM     sockRef, recvRef;
8251     ErlNifUInt64     eBufSz,  eCtrlSz;
8252     ssize_t          bufSz,   ctrlSz;
8253     int              flags;
8254     ERL_NIF_TERM     res;
8255     BOOLEAN_T        a1ok, a2ok;
8256 
8257     ESOCK_ASSERT( argc == 5 );
8258 
8259     SGDBG( ("SOCKET", "nif_recvmsg -> entry with argc: %d\r\n", argc) );
8260 
8261     sockRef = argv[0]; // We need this in case we send abort (to the caller)
8262     recvRef = argv[4];
8263 
8264     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
8265         return enif_make_badarg(env);
8266     }
8267 
8268     /* Extract arguments and perform preliminary validation */
8269 
8270     if ((! enif_is_ref(env, recvRef)) &&
8271         (COMPARE(recvRef, atom_zero) != 0)) {
8272         return enif_make_badarg(env);
8273     }
8274 
8275     if ((! (a1ok = GET_UINT64(env, argv[1], &eBufSz))) ||
8276         (! (a2ok = GET_UINT64(env, argv[2], &eCtrlSz))) ||
8277         (! GET_INT(env, argv[3], &flags))) {
8278         if ((! IS_INTEGER(env, argv[1])) ||
8279             (! IS_INTEGER(env, argv[2])) ||
8280             (! IS_INTEGER(env, argv[3])))
8281             return enif_make_badarg(env);
8282 
8283         if (! a1ok)
8284             return esock_make_error_integer_range(env, argv[1]);
8285         if (! a2ok)
8286             return esock_make_error_integer_range(env, argv[2]);
8287         return
8288             esock_make_error_integer_range(env, argv[3]);
8289     }
8290 
8291     bufSz  = (ssize_t) eBufSz;
8292     if (eBufSz  != (ErlNifUInt64) bufSz)
8293         return esock_make_error_integer_range(env, eBufSz);
8294 
8295     ctrlSz = (ssize_t) eCtrlSz;
8296     if (eCtrlSz != (ErlNifUInt64) ctrlSz)
8297         return esock_make_error_integer_range(env, eCtrlSz);
8298 
8299     MLOCK(descP->readMtx);
8300 
8301     SSDBG( descP,
8302            ("SOCKET", "nif_recvmsg(%T), {%d,0x%X} ->"
8303             "\r\n   recvRef: %T"
8304             "\r\n   bufSz:   %ld"
8305             "\r\n   ctrlSz:  %ld"
8306             "\r\n   flags:   0x%X"
8307             "\r\n",
8308             sockRef, descP->sock, descP->readState,
8309             recvRef, (long) bufSz, (long) ctrlSz, flags) );
8310 
8311     /* <KOLLA>
8312      *
8313      * We need to handle the case when another process tries
8314      * to receive at the same time.
8315      * If the current recv could not read its entire package
8316      * this time (resulting in an select). The read of the
8317      * other process must be made to wait until current
8318      * is done!
8319      * Basically, we need a read queue!
8320      *
8321      * A 'reading' field (boolean), which is set if we did
8322      * not manage to read the entire message and reset every
8323      * time we do.
8324      *
8325      * </KOLLA>
8326      */
8327 
8328     res = esock_recvmsg(env, descP, sockRef, recvRef, bufSz, ctrlSz, flags);
8329 
8330     SSDBG( descP, ("SOCKET", "nif_recvmsg(%T) -> done"
8331                    "\r\n", sockRef) );
8332 
8333     MUNLOCK(descP->readMtx);
8334 
8335     return res;
8336 #endif // #ifdef __WIN32__  #else
8337 }
8338 
8339 
8340 /* The (read) buffer handling *must* be optimized!
8341  * But for now we make it easy for ourselves by
8342  * allocating a binary (of the specified or default
8343  * size) and then throwing it away...
8344  */
8345 #ifndef __WIN32__
8346 static
esock_recvmsg(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef,ssize_t bufLen,ssize_t ctrlLen,int flags)8347 ERL_NIF_TERM esock_recvmsg(ErlNifEnv*       env,
8348                            ESockDescriptor* descP,
8349                            ERL_NIF_TERM     sockRef,
8350                            ERL_NIF_TERM     recvRef,
8351                            ssize_t          bufLen,
8352                            ssize_t          ctrlLen,
8353                            int              flags)
8354 {
8355     SOCKLEN_T     addrLen;
8356     ssize_t       read;
8357     int           save_errno;
8358     size_t        bufSz  = (bufLen  != 0 ? bufLen  : descP->rBufSz);
8359     size_t        ctrlSz = (ctrlLen != 0 ? ctrlLen : descP->rCtrlSz);
8360     struct msghdr msgHdr;
8361     struct iovec  iov[1];  // Shall we always use 1?
8362     ErlNifBinary  data[1]; // Shall we always use 1?
8363     ErlNifBinary  ctrl;
8364     ERL_NIF_TERM  readerCheck;
8365     ESockAddress  addr;
8366 
8367     SSDBG( descP, ("SOCKET", "esock_recvmsg {%d} -> entry with"
8368                    "\r\n   bufSz:  %lu (%ld)"
8369                    "\r\n   ctrlSz: %ld (%ld)"
8370                    "\r\n", descP->sock,
8371                    (unsigned long) bufSz, (long) bufLen,
8372                    (unsigned long) ctrlSz, (long) ctrlLen) );
8373 
8374     if (! IS_OPEN(descP->readState))
8375         return esock_make_error(env, atom_closed);
8376 
8377     /* Accept and Read uses the same select flag
8378      * so they can not be simultaneous
8379      */
8380     if (descP->currentAcceptorP != NULL)
8381         return esock_make_error_invalid(env, atom_state);
8382 
8383     /* Ensure that we either have no current reader or that we are it,
8384      * or enqueue this process if there is a current reader */
8385     if (! recv_check_reader(env, descP, recvRef, &readerCheck)) {
8386         SSDBG( descP,
8387                ("SOCKET", "esock_recv {%d} -> reader check failed: "
8388                 "\r\n   %T\r\n", descP->sock, readerCheck) );
8389         return readerCheck;
8390     }
8391 
8392     /*
8393     for (i = 0; i < sizeof(buf); i++) {
8394         ESOCK_ASSERT( ALLOC_BIN(bifSz, &buf[i]) );
8395         iov[i].iov_base = buf[i].data;
8396         iov[i].iov_len  = buf[i].size;
8397     }
8398     */
8399 
8400     /* Allocate the (msg) data buffer:
8401      */
8402     ESOCK_ASSERT( ALLOC_BIN(bufSz, &data[0]) );
8403 
8404     /* Allocate the ctrl (buffer):
8405      */
8406     ESOCK_ASSERT( ALLOC_BIN(ctrlSz, &ctrl) );
8407 
8408     ESOCK_CNT_INC(env, descP, sockRef, atom_read_tries, &descP->readTries, 1);
8409 
8410     addrLen = sizeof(addr);
8411     sys_memzero((char*) &addr,   addrLen);
8412     sys_memzero((char*) &msgHdr, sizeof(msgHdr));
8413 
8414     iov[0].iov_base = data[0].data;
8415     iov[0].iov_len  = data[0].size;
8416 
8417     msgHdr.msg_name       = &addr;
8418     msgHdr.msg_namelen    = addrLen;
8419     msgHdr.msg_iov        = iov;
8420     msgHdr.msg_iovlen     = 1; // Should use a constant or calculate...
8421     msgHdr.msg_control    = ctrl.data;
8422     msgHdr.msg_controllen = ctrl.size;
8423 
8424     read = sock_recvmsg(descP->sock, &msgHdr, flags);
8425     if (ESOCK_IS_ERROR(read))
8426         save_errno = sock_errno();
8427     else
8428         save_errno = 0; // The value does not actually matter in this case
8429 
8430     return recvmsg_check_result(env, descP, read, save_errno,
8431                                 &msgHdr,
8432                                 data,  // Needed for iov encode
8433                                 &ctrl, // Needed for ctrl header encode
8434                                 sockRef, recvRef);
8435 }
8436 #endif // #ifndef __WIN32__
8437 
8438 
8439 
8440 /* ----------------------------------------------------------------------
8441  * nif_close
8442  *
8443  * Description:
8444  * Close a (socket) file descriptor.
8445  *
8446  * Arguments:
8447  * Socket (ref) - Points to the socket descriptor.
8448  */
8449 
8450 static
nif_close(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])8451 ERL_NIF_TERM nif_close(ErlNifEnv*         env,
8452                        int                argc,
8453                        const ERL_NIF_TERM argv[])
8454 {
8455 #ifdef __WIN32__
8456     return enif_raise_exception(env, MKA(env, "notsup"));
8457 #else
8458     ESockDescriptor* descP;
8459     ERL_NIF_TERM res;
8460 
8461     ESOCK_ASSERT( argc == 1 );
8462 
8463     SGDBG( ("SOCKET", "nif_close -> entry with argc: %d\r\n", argc) );
8464 
8465     if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
8466         return enif_make_badarg(env);
8467     }
8468 
8469     MLOCK(descP->readMtx);
8470     MLOCK(descP->writeMtx);
8471 
8472     SSDBG( descP,
8473            ("SOCKET", "nif_close(%T), {%d,0x%X}\r\n",
8474             argv[0], descP->sock, descP->readState) );
8475 
8476     res = esock_close(env, descP);
8477 
8478     MUNLOCK(descP->writeMtx);
8479     MUNLOCK(descP->readMtx);
8480 
8481     SSDBG( descP, ("SOCKET", "nif_close(%T) -> done"
8482                    "\r\n   res: %T"
8483                    "\r\n", argv[0], res) );
8484 
8485     return res;
8486 #endif // #ifdef __WIN32__  #else
8487 }
8488 
8489 
8490 #ifndef __WIN32__
8491 static
esock_close(ErlNifEnv * env,ESockDescriptor * descP)8492 ERL_NIF_TERM esock_close(ErlNifEnv*       env,
8493                          ESockDescriptor* descP)
8494 {
8495     if (! IS_OPEN(descP->readState)) {
8496         /* A bit of cheeting; maybe not closed yet - do we need a queue? */
8497         return esock_make_error(env, atom_closed);
8498     }
8499 
8500     /* Store the PID of the caller,
8501      * since we need to inform it when we
8502      * (that is, the stop callback function)
8503      * completes.
8504      */
8505     ESOCK_ASSERT( enif_self(env, &descP->closerPid) != NULL );
8506 
8507     /* If the caller is not the owner; monitor the caller,
8508      * since we should complete this operation even if the caller dies
8509      * (for whatever reason).
8510      */
8511     if (COMPARE_PIDS(&descP->closerPid, &descP->ctrlPid) != 0) {
8512 
8513         ESOCK_ASSERT( MONP("esock_close_check -> closer",
8514                            env, descP,
8515                            &descP->closerPid,
8516                            &descP->closerMon) == 0 );
8517     }
8518 
8519     /* Prepare for closing the socket */
8520     descP->readState  |= ESOCK_STATE_CLOSING;
8521     descP->writeState |= ESOCK_STATE_CLOSING;
8522     if (esock_do_stop(env, descP)) {
8523         // esock_stop() has been scheduled - wait for it
8524         SSDBG( descP,
8525                ("SOCKET", "esock_close {%d} -> stop was scheduled\r\n",
8526                 descP->sock) );
8527 
8528         // Create closeRef for the close msg that esock_stop() will send
8529         descP->closeEnv = esock_alloc_env("esock_close_do - close-env");
8530         descP->closeRef = MKREF(descP->closeEnv);
8531 
8532         return esock_make_ok2(env, CP_TERM(env, descP->closeRef));
8533     } else {
8534         // The socket may be closed - tell caller to finalize
8535         SSDBG( descP,
8536                ("SOCKET",
8537                 "esock_close {%d} -> stop was called\r\n",
8538                 descP->sock) );
8539 
8540         return esock_atom_ok;
8541     }
8542 }
8543 #endif // #ifndef __WIN32__
8544 
8545 
8546 #ifndef __WIN32__
8547 /* Prepare for close - return whether stop is scheduled
8548  */
8549 static
esock_do_stop(ErlNifEnv * env,ESockDescriptor * descP)8550 BOOLEAN_T esock_do_stop(ErlNifEnv* env,
8551                         ESockDescriptor* descP) {
8552     BOOLEAN_T    ret;
8553     int          sres;
8554     ERL_NIF_TERM sockRef;
8555 
8556     sockRef = enif_make_resource(env, descP);
8557 
8558     if (IS_SELECTED(descP)) {
8559         ESOCK_ASSERT( (sres = esock_select_stop(env, descP->sock, descP))
8560                       >= 0 );
8561         if ((sres & ERL_NIF_SELECT_STOP_CALLED) != 0) {
8562             /* The socket is no longer known by the select machinery
8563              * - it may be closed
8564              */
8565             ret = FALSE;
8566         } else {
8567             ESOCK_ASSERT( (sres & ERL_NIF_SELECT_STOP_SCHEDULED) != 0 );
8568             /* esock_stop() is scheduled
8569              * - socket may be removed by esock_stop() or later
8570              */
8571             ret = TRUE;
8572         }
8573     } else {
8574         sres = 0;
8575         /* The socket has never been used in the select machinery
8576          * - it may be closed
8577          */
8578         ret = FALSE;
8579     }
8580 
8581     /* +++++++ Current and waiting Writers +++++++ */
8582 
8583     if (descP->currentWriterP != NULL) {
8584 
8585         /* We have a current Writer; was it deselected?
8586          */
8587 
8588         if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) {
8589 
8590             /* The current Writer will not get a select message
8591              * - send it an abort message
8592              */
8593 
8594             esock_stop_handle_current(env,
8595                                       "writer",
8596                                       descP, sockRef, &descP->currentWriter);
8597         }
8598 
8599         /* Inform the waiting Writers (in the same way) */
8600 
8601         SSDBG( descP,
8602                ("SOCKET",
8603                 "esock_do_stop {%d} -> handle waiting writer(s)\r\n",
8604                 descP->sock) );
8605 
8606         inform_waiting_procs(env, "writer",
8607                              descP, sockRef, &descP->writersQ, atom_closed);
8608 
8609         descP->currentWriterP = NULL;
8610     }
8611 
8612     /* +++++++ Connector +++++++
8613      * Note that there should not be Writers and a Connector
8614      * at the same time so the check for if the
8615      * current Writer/Connecter was deselected is only correct
8616      * under that assumption
8617      */
8618 
8619     if (descP->connectorP != NULL) {
8620 
8621         /* We have a Connector; was it deselected?
8622          */
8623 
8624         if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) {
8625 
8626             /* The Connector will not get a select message
8627              * - send it an abort message
8628              */
8629 
8630             esock_stop_handle_current(env,
8631                                       "connector",
8632                                       descP, sockRef, &descP->connector);
8633         }
8634 
8635         descP->connectorP = NULL;
8636     }
8637 
8638     /* +++++++ Current and waiting Readers +++++++ */
8639 
8640     if (descP->currentReaderP != NULL) {
8641 
8642         /* We have a current Reader; was it deselected?
8643          */
8644 
8645         if (sres & ERL_NIF_SELECT_READ_CANCELLED) {
8646 
8647             /* The current Reader will not get a select message
8648              * - send it an abort message
8649              */
8650 
8651             esock_stop_handle_current(env,
8652                                       "reader",
8653                                       descP, sockRef, &descP->currentReader);
8654         }
8655 
8656         /* Inform the Readers (in the same way) */
8657 
8658         SSDBG( descP,
8659                ("SOCKET",
8660                 "esock_do_stop {%d} -> handle waiting reader(s)\r\n",
8661                 descP->sock) );
8662 
8663         inform_waiting_procs(env, "writer",
8664                              descP, sockRef, &descP->readersQ, atom_closed);
8665 
8666         descP->currentReaderP = NULL;
8667     }
8668 
8669     /* +++++++ Current and waiting Acceptors +++++++
8670      *
8671      * Note that there should not be Readers and Acceptors
8672      * at the same time so the check for if the
8673      * current Reader/Acceptor was deselected is only correct
8674      * under that assumption
8675      */
8676 
8677     if (descP->currentAcceptorP != NULL) {
8678 
8679         /* We have a current Acceptor; was it deselected?
8680          */
8681 
8682         if (sres & ERL_NIF_SELECT_READ_CANCELLED) {
8683 
8684             /* The current Acceptor will not get a select message
8685              * - send it an abort message
8686              */
8687 
8688             esock_stop_handle_current(env,
8689                                       "acceptor",
8690                                       descP, sockRef, &descP->currentAcceptor);
8691         }
8692 
8693         /* Inform the waiting Acceptor (in the same way) */
8694 
8695         SSDBG( descP,
8696                ("SOCKET",
8697                 "esock_do_stop {%d} -> handle waiting acceptors(s)\r\n",
8698                 descP->sock) );
8699 
8700         inform_waiting_procs(env, "acceptor",
8701                              descP, sockRef, &descP->acceptorsQ, atom_closed);
8702 
8703         descP->currentAcceptorP = NULL;
8704     }
8705 
8706     return ret;
8707 }
8708 #endif // #ifndef __WIN32__
8709 
8710 
8711 
8712 /* ----------------------------------------------------------------------
8713  * nif_finalize_close
8714  *
8715  * Description:
8716  * Perform the actual socket close!
8717  * Note that this function is executed in a dirty scheduler.
8718  *
8719  * Arguments:
8720  * Socket (ref) - Points to the socket descriptor.
8721  */
8722 static
nif_finalize_close(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])8723 ERL_NIF_TERM nif_finalize_close(ErlNifEnv*         env,
8724                                 int                argc,
8725                                 const ERL_NIF_TERM argv[])
8726 {
8727     ESockDescriptor* descP;
8728     ERL_NIF_TERM result;
8729 #ifdef __WIN32__
8730     return enif_raise_exception(env, MKA(env, "notsup"));
8731 #else
8732 
8733     /* Extract arguments and perform preliminary validation */
8734 
8735     ESOCK_ASSERT( argc == 1 );
8736 
8737     if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
8738         return enif_make_badarg(env);
8739     }
8740 
8741     MLOCK(descP->readMtx);
8742     MLOCK(descP->writeMtx);
8743 
8744     SSDBG( descP,
8745            ("SOCKET", "nif_finalize_close(%T), {%d,0x%X}\r\n",
8746             argv[0], descP->sock, descP->readState) );
8747 
8748     result = esock_finalize_close(env, descP);
8749 
8750     SSDBG( descP, ("SOCKET", "nif_finalize_close(%T) -> done with"
8751                    "\r\n   result: %T"
8752                    "\r\n", argv[0], result) );
8753 
8754     MUNLOCK(descP->writeMtx);
8755     MUNLOCK(descP->readMtx);
8756 
8757     return result;
8758 #endif // #ifdef __WIN32__  #else
8759 }
8760 
8761 
8762 /* *** esock_finalize_close ***
8763  * Perform the final step in the socket close.
8764  */
8765 #ifndef __WIN32__
8766 static
esock_finalize_close(ErlNifEnv * env,ESockDescriptor * descP)8767 ERL_NIF_TERM esock_finalize_close(ErlNifEnv*       env,
8768                                   ESockDescriptor* descP)
8769 {
8770     int err;
8771     ErlNifPid self;
8772 #ifdef HAVE_SENDFILE
8773     HANDLE sendfileHandle;
8774 #endif
8775 
8776     ESOCK_ASSERT( enif_self(env, &self) != NULL );
8777 
8778     if (IS_CLOSED(descP->readState))
8779         return esock_make_error(env, atom_closed);
8780 
8781     if (! IS_CLOSING(descP->readState)) {
8782         // esock_close() has not been called
8783         return esock_raise_invalid(env, atom_state);
8784     }
8785 
8786     if (IS_SELECTED(descP) && (descP->closeEnv != NULL)) {
8787         // esock_stop() is scheduled but has not been called
8788         return esock_raise_invalid(env, atom_state);
8789     }
8790 
8791     if (COMPARE_PIDS(&descP->closerPid, &self) != 0) {
8792         // This process is not the closer
8793         return esock_raise_invalid(env, atom_state);
8794     }
8795 
8796     // Close the socket
8797 
8798     /* Stop monitoring the closer.
8799      * Demonitoring may fail since this is a dirty NIF
8800      * - the caller may have died already.
8801      */
8802     enif_set_pid_undefined(&descP->closerPid);
8803     if (descP->closerMon.isActive) {
8804         (void) DEMONP("esock_finalize_close -> closer",
8805                       env, descP, &descP->closerMon);
8806     }
8807 
8808     /* Stop monitoring the owner */
8809     enif_set_pid_undefined(&descP->ctrlPid);
8810     (void) DEMONP("esock_finalize_close -> ctrl",
8811                   env, descP, &descP->ctrlMon);
8812     /* Not impossible to still get a esock_down() call from a
8813      * just triggered owner monitor down
8814      */
8815 
8816 #ifdef HAVE_SENDFILE
8817     sendfileHandle = descP->sendfileHandle;
8818     descP->sendfileHandle = INVALID_HANDLE;
8819 #endif
8820 
8821     /* This nif is executed in a dirty scheduler just so that
8822      * it can "hang" (whith minumum effect on the VM) while the
8823      * kernel writes our buffers. IF we have set the linger option
8824      * for this ({true, integer() > 0}). For this to work we must
8825      * be blocking...
8826      */
8827     SET_BLOCKING(descP->sock);
8828     err = esock_close_socket(env, descP, TRUE);
8829 
8830 #ifdef HAVE_SENDFILE
8831     if (sendfileHandle != INVALID_HANDLE) {
8832         (void) close(descP->sendfileHandle);
8833     }
8834 #endif
8835 
8836     if (err != 0) {
8837         if (err == ERRNO_BLOCK) {
8838             /* Not all data in the buffers where sent,
8839              * make sure the caller gets this.
8840              */
8841             return esock_make_error(env, atom_timeout);
8842         } else {
8843             return esock_make_error_errno(env, err);
8844         }
8845     }
8846 
8847     return esock_atom_ok;
8848 }
8849 #endif // #ifndef __WIN32__
8850 
8851 
8852 #ifndef __WIN32__
esock_close_socket(ErlNifEnv * env,ESockDescriptor * descP,BOOLEAN_T unlock)8853 static int esock_close_socket(ErlNifEnv*       env,
8854                               ESockDescriptor* descP,
8855                               BOOLEAN_T        unlock) {
8856     int          err      = 0;
8857     SOCKET       sock     = descP->sock;
8858     ERL_NIF_TERM sockRef;
8859 
8860     /* This code follows Linux's advice to assume that after calling
8861      * close(2), the file descriptor may be reused, so assuming
8862      * that it can be used for anything such as retrying
8863      * to close is bad behaviour, although odd platforms
8864      * such as HP-UX requires a retry after EINTR
8865      */
8866 
8867     /* First update the state so no other thread will try
8868      * to close the socket, then we will close it,
8869      * possibly when being scheduled in during
8870      * finalize_close
8871      */
8872     descP->sock        = INVALID_SOCKET;
8873     descP->event       = INVALID_EVENT;
8874     descP->readState  |= ESOCK_STATE_CLOSED;
8875     descP->writeState |= ESOCK_STATE_CLOSED;
8876     dec_socket(descP->domain, descP->type, descP->protocol);
8877 
8878     /* +++++++ Clear the meta option +++++++ */
8879     enif_clear_env(descP->meta.env);
8880     descP->meta.ref = esock_atom_undefined;
8881 
8882     sock_close_event(descP->event);
8883     if (descP->closeOnClose) {
8884         if (unlock) {
8885             MUNLOCK(descP->writeMtx);
8886             MUNLOCK(descP->readMtx);
8887         }
8888         if (sock_close(sock) != 0)
8889             err = sock_errno();
8890         if (unlock) {
8891             MLOCK(descP->readMtx);
8892             MLOCK(descP->writeMtx);
8893         }
8894     }
8895 
8896     if (err != 0) {
8897         SSDBG( descP,
8898                ("SOCKET", "esock_close_socket {%d} -> %d\r\n",
8899                 sock, err) );
8900     }
8901 
8902     /* (maybe) Update the registry */
8903     if (descP->useReg) {
8904         sockRef = enif_make_resource(env, descP);
8905         esock_send_reg_del_msg(env, descP, sockRef);
8906     }
8907 
8908     return err;
8909 }
8910 #endif // #ifndef __WIN32__
8911 
8912 
8913 /* ----------------------------------------------------------------------
8914  * nif_shutdown
8915  *
8916  * Description:
8917  * Disable sends and/or receives on a socket.
8918  *
8919  * Arguments:
8920  * [0] Socket (ref) - Points to the socket descriptor.
8921  * [1] How          - What will be shutdown.
8922  */
8923 
8924 static
nif_shutdown(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])8925 ERL_NIF_TERM nif_shutdown(ErlNifEnv*         env,
8926                           int                argc,
8927                           const ERL_NIF_TERM argv[])
8928 {
8929 #ifdef __WIN32__
8930     return enif_raise_exception(env, MKA(env, "notsup"));
8931 #else
8932     ESockDescriptor* descP;
8933     ERL_NIF_TERM     ehow, res;
8934     int              how;
8935 
8936     ESOCK_ASSERT( argc == 2 );
8937 
8938     if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
8939         return enif_make_badarg(env);
8940     }
8941     ehow = argv[1];
8942 
8943     if (! ehow2how(ehow, &how))
8944         return esock_raise_invalid(env,
8945                                    MKT2(env, atom_how, ehow));
8946 
8947     MLOCK(descP->readMtx);
8948     MLOCK(descP->writeMtx);
8949 
8950     SSDBG( descP,
8951            ("SOCKET", "nif_shutdown(%T), {%d,0x%X} ->"
8952             "\r\n   how: %d"
8953             "\r\n",
8954             argv[0], descP->sock, descP->readState | descP->writeState,
8955             how) );
8956 
8957     res = esock_shutdown(env, descP, how);
8958 
8959     MUNLOCK(descP->writeMtx);
8960     MUNLOCK(descP->readMtx);
8961 
8962     SSDBG( descP, ("SOCKET", "nif_shutdown(%T) -> done with"
8963                    "\r\n   res: %T"
8964                    "\r\n", argv[0], res) );
8965 
8966     return res;
8967 #endif // #ifdef __WIN32__  #else
8968 }
8969 
8970 
8971 
8972 #ifndef __WIN32__
8973 static
esock_shutdown(ErlNifEnv * env,ESockDescriptor * descP,int how)8974 ERL_NIF_TERM esock_shutdown(ErlNifEnv*       env,
8975                             ESockDescriptor* descP,
8976                             int              how)
8977 {
8978     if (! IS_OPEN(descP->readState))
8979         return esock_make_error(env, atom_closed);
8980 
8981     if (sock_shutdown(descP->sock, how) == 0)
8982         return esock_atom_ok;
8983     else
8984         return esock_make_error_errno(env, sock_errno());
8985 }
8986 #endif // #ifndef __WIN32__
8987 
8988 
8989 
8990 /* ----------------------------------------------------------------------
8991  * nif_setopt
8992  *
8993  * Description:
8994  * Set socket option.
8995  * It is possible to use a native mode value where we do not use
8996  * any assumption about how to encode the value but instead
8997  * use the value's type to select encoding.
8998  *
8999  * Arguments:
9000  * Socket (ref) - Points to the socket descriptor.
9001  * Level        - Level of the socket option.
9002  * Opt          - The socket option.
9003  * Value        - Value of the socket option.
9004  * NativeValue  - If 0 Value type has to match our encoding function,
9005  *                if not 0 type selects encoding.
9006  */
9007 
9008 static
nif_setopt(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])9009 ERL_NIF_TERM nif_setopt(ErlNifEnv*         env,
9010                         int                argc,
9011                         const ERL_NIF_TERM argv[])
9012 {
9013 #ifdef __WIN32__
9014     return enif_raise_exception(env, MKA(env, "notsup"));
9015 #else
9016     ESockDescriptor* descP = NULL;
9017     int              level, opt, nativeValue;
9018     ERL_NIF_TERM     eVal;
9019 
9020     ESOCK_ASSERT( argc == 5 );
9021 
9022     SGDBG( ("SOCKET", "nif_setopt -> entry with argc: %d\r\n", argc) );
9023 
9024     /* Extract arguments and perform preliminary validation */
9025 
9026     if ((! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) ||
9027         (! GET_INT(env, argv[4], &nativeValue))) {
9028         //
9029         SGDBG( ("SOCKET", "nif_setopt -> failed initial arg check\r\n") );
9030         return enif_make_badarg(env);
9031     }
9032     if (! GET_INT(env, argv[2], &opt)) {
9033         SSDBG( descP,
9034                ("SOCKET", "nif_setopt -> failed initial arg check\r\n") );
9035         if (! IS_INTEGER(env, argv[2]))
9036             return enif_make_badarg(env);
9037         else
9038             return esock_make_error_integer_range(env, argv[2]);
9039     }
9040     eVal = argv[3];
9041 
9042     if (esock_decode_level(env, argv[1], &level)) {
9043         if (nativeValue == 0)
9044             return esock_setopt(env, descP, level, opt, eVal);
9045         else
9046             return esock_setopt_native(env, descP, level, opt, eVal);
9047     }
9048 
9049     if (COMPARE(argv[1], atom_otp) == 0) {
9050         if (nativeValue == 0) {
9051             return esock_setopt_otp(env, descP, opt, eVal);
9052         } else {
9053             SSDBG( descP, ("SOCKET", "nif_setopt -> failed arg check\r\n") );
9054             return enif_make_badarg(env);
9055         }
9056     }
9057 
9058     SGDBG( ("SOCKET", "nif_setopt -> failed arg check\r\n") );
9059 
9060     if (IS_INTEGER(env, argv[1]))
9061         return esock_make_error_integer_range(env, argv[1]);
9062     else
9063         return enif_make_badarg(env);
9064 #endif // #ifdef __WIN32__  #else
9065 }
9066 
9067 
9068 /* esock_setopt_otp - Handle OTP (level) options
9069  */
9070 #ifndef __WIN32__
9071 static
esock_setopt_otp(ErlNifEnv * env,ESockDescriptor * descP,int eOpt,ERL_NIF_TERM eVal)9072 ERL_NIF_TERM esock_setopt_otp(ErlNifEnv*       env,
9073                               ESockDescriptor* descP,
9074                               int              eOpt,
9075                               ERL_NIF_TERM     eVal)
9076 {
9077     ERL_NIF_TERM result;
9078 
9079     switch (eOpt) {
9080     case ESOCK_OPT_OTP_DEBUG:
9081         MLOCK(descP->readMtx);
9082         MLOCK(descP->writeMtx);
9083         result = esock_setopt_otp_debug(env, descP, eVal);
9084         MUNLOCK(descP->writeMtx);
9085         MUNLOCK(descP->readMtx);
9086         break;
9087 
9088     case ESOCK_OPT_OTP_IOW:
9089         MLOCK(descP->readMtx);
9090         MLOCK(descP->writeMtx);
9091         result = esock_setopt_otp_iow(env, descP, eVal);
9092         MUNLOCK(descP->writeMtx);
9093         MUNLOCK(descP->readMtx);
9094         break;
9095 
9096     case ESOCK_OPT_OTP_CTRL_PROC:
9097         MLOCK(descP->readMtx);
9098         MLOCK(descP->writeMtx);
9099         result = esock_setopt_otp_ctrl_proc(env, descP, eVal);
9100         MUNLOCK(descP->writeMtx);
9101         MUNLOCK(descP->readMtx);
9102         break;
9103 
9104     case ESOCK_OPT_OTP_RCVBUF:
9105         MLOCK(descP->readMtx);
9106         result = esock_setopt_otp_rcvbuf(env, descP, eVal);
9107         MUNLOCK(descP->readMtx);
9108         break;
9109 
9110     case ESOCK_OPT_OTP_RCVCTRLBUF:
9111         MLOCK(descP->readMtx);
9112         result = esock_setopt_otp_rcvctrlbuf(env, descP, eVal);
9113         MUNLOCK(descP->readMtx);
9114         break;
9115 
9116     case ESOCK_OPT_OTP_SNDCTRLBUF:
9117         MLOCK(descP->writeMtx);
9118         result = esock_setopt_otp_sndctrlbuf(env, descP, eVal);
9119         MUNLOCK(descP->writeMtx);
9120         break;
9121 
9122     case ESOCK_OPT_OTP_META:
9123         MLOCK(descP->writeMtx);
9124         result = esock_setopt_otp_meta(env, descP, eVal);
9125         MUNLOCK(descP->writeMtx);
9126         break;
9127 
9128     case ESOCK_OPT_OTP_USE_REGISTRY:
9129         MLOCK(descP->writeMtx);
9130         result = esock_setopt_otp_use_registry(env, descP, eVal);
9131         MUNLOCK(descP->writeMtx);
9132         break;
9133 
9134     default:
9135         MLOCK(descP->writeMtx);
9136         SSDBG( descP,
9137                ("SOCKET", "esock_setopt_otp {%d} -> invalid with"
9138                 "\r\n   eOpt: %d"
9139                 "\r\n   eVal: %T"
9140                 "\r\n", descP->sock, eOpt, eVal) );
9141         MUNLOCK(descP->writeMtx);
9142 
9143         /* This is an internal error - prim_inet gave us junk */
9144         result =
9145             esock_raise_invalid(env,
9146                                 MKT2(env,
9147                                      atom_otp_socket_option,
9148                                      MKI(env, eOpt)));
9149         break;
9150     }
9151 
9152     return result;
9153 }
9154 #endif // #ifndef __WIN32__
9155 
9156 
9157 
9158 /* esock_setopt_otp_debug - Handle the OTP (level) debug options
9159  */
9160 #ifndef __WIN32__
9161 static
esock_setopt_otp_debug(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eVal)9162 ERL_NIF_TERM esock_setopt_otp_debug(ErlNifEnv*       env,
9163                                     ESockDescriptor* descP,
9164                                     ERL_NIF_TERM     eVal)
9165 {
9166     if (! IS_OPEN(descP->writeState)) {
9167         SSDBG( descP,
9168                ("SOCKET", "esock_setopt_otp_debug {%d} -> closed\r\n",
9169                 descP->sock) );
9170         return esock_make_error(env, atom_closed);
9171     }
9172 
9173     if (! esock_decode_bool(eVal, &descP->dbg))
9174         return esock_make_invalid(env, atom_value);
9175 
9176     SSDBG( descP,
9177            ("SOCKET", "esock_setopt_otp_debug {%d} -> ok"
9178             "\r\n   eVal: %T"
9179             "\r\n", descP->sock, eVal) );
9180 
9181     return esock_atom_ok;
9182 }
9183 #endif // #ifndef __WIN32__
9184 
9185 
9186 /* esock_setopt_otp_iow - Handle the OTP (level) iow options
9187  */
9188 #ifndef __WIN32__
9189 static
esock_setopt_otp_iow(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eVal)9190 ERL_NIF_TERM esock_setopt_otp_iow(ErlNifEnv*       env,
9191                                   ESockDescriptor* descP,
9192                                   ERL_NIF_TERM     eVal)
9193 {
9194     if (! IS_OPEN(descP->writeState)) {
9195         SSDBG( descP,
9196                ("SOCKET", "esock_setopt_otp_iow {%d} -> closed\r\n",
9197                 descP->sock) );
9198         return esock_make_error(env, atom_closed);
9199     }
9200 
9201     if (! esock_decode_bool(eVal, &descP->iow))
9202       return esock_make_invalid(env, atom_value);
9203 
9204     SSDBG( descP,
9205            ("SOCKET", "esock_setopt_otp_iow {%d} -> ok"
9206             "\r\n   eVal: %T"
9207             "\r\n", descP->sock, eVal) );
9208 
9209     return esock_atom_ok;
9210 }
9211 #endif // #ifndef __WIN32__
9212 
9213 
9214 /* esock_setopt_otp_ctrl_proc - Handle the OTP (level)
9215  * controlling_process options
9216  */
9217 #ifndef __WIN32__
9218 static
esock_setopt_otp_ctrl_proc(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eVal)9219 ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv*       env,
9220                                         ESockDescriptor* descP,
9221                                         ERL_NIF_TERM     eVal)
9222 {
9223     ErlNifPid     caller, newCtrlPid;
9224     int           xres;
9225 
9226     SSDBG( descP,
9227            ("SOCKET", "esock_setopt_otp_ctrl_proc {%d} -> entry"
9228             "\r\n   eVal: %T"
9229             "\r\n", descP->sock, eVal) );
9230 
9231     if (! IS_OPEN(descP->writeState)) {
9232         SSDBG( descP,
9233                ("SOCKET", "esock_setopt_otp_ctrl_proc {%d} -> closed\r\n",
9234                 descP->sock) );
9235         return esock_make_error(env, atom_closed);
9236     }
9237 
9238     /* Ensure that caller is (current) controlling process */
9239     ESOCK_ASSERT( enif_self(env, &caller) != NULL );
9240     if (COMPARE_PIDS(&descP->ctrlPid, &caller) != 0) {
9241         SSDBG( descP, ("SOCKET",
9242                        "esock_setopt_otp_ctrl_proc -> not owner (%T)\r\n",
9243                        descP->ctrlPid) );
9244         return esock_make_error_invalid(env, esock_atom_not_owner);
9245     }
9246 
9247     /* Ensure that the new controller is a local process */
9248     if (!GET_LPID(env, eVal, &newCtrlPid)) {
9249         esock_warning_msg("Failed get pid of new controlling process\r\n");
9250         return esock_make_invalid(env, atom_value);
9251     }
9252 
9253     if ((xres = DEMONP("esock_setopt_otp_ctrl_proc -> (old) ctrl",
9254                        env, descP, &descP->ctrlMon)) != 0) {
9255         /* There is a legitimate reason for this is; the current
9256          * process was just killed from a different thread
9257          */
9258         esock_warning_msg("Failed demonitor (%d) "
9259                           "old controlling process %T (%T)\r\n",
9260                           xres, descP->ctrlPid, descP->ctrlMon);
9261     }
9262 
9263     descP->ctrlPid = newCtrlPid;
9264 
9265     if ((xres =
9266          MONP("esock_setopt_otp_ctrl_proc -> (new) ctrl",
9267                      env, descP, &descP->ctrlPid, &descP->ctrlMon)) != 0) {
9268 
9269         ESOCK_ASSERT( 0 < xres );
9270         /* Indicates that we do not have a DOWN callback,
9271          * which is preposterous
9272          */
9273 
9274         /* We know newCtrlPid is not 'undefined' so
9275          * it must be dead already
9276          * - pretend the controlling process change went well
9277          * and then the monitor went down
9278          */
9279         SSDBG( descP,
9280                ("SOCKET", "esock_setopt_otp_ctrl_proc {%d} -> DOWN"
9281                 "\r\n   xres: %d"
9282                 "\r\n", descP->sock, xres) );
9283 
9284         enif_set_pid_undefined(&descP->ctrlPid);
9285 
9286         esock_down_ctrl(env, descP, &newCtrlPid);
9287 
9288         descP->readState  |= ESOCK_STATE_CLOSING;
9289         descP->writeState |= ESOCK_STATE_CLOSING;
9290 
9291     } else {
9292         SSDBG( descP,
9293                ("SOCKET", "esock_setopt_otp_ctrl_proc {%d} -> ok"
9294                 "\r\n", descP->sock) );
9295     }
9296 
9297     return esock_atom_ok;
9298 }
9299 #endif // #ifndef __WIN32__
9300 
9301 
9302 /* esock_setopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option
9303  * The (otp) rcvbuf option is provided as:
9304  *
9305  *       BufSz :: default | pos_integer() |
9306  *           {N :: pos_integer(), Sz :: default | pos_integer()}
9307  *
9308  * Where N is the max number of reads.
9309  */
9310 #ifndef __WIN32__
9311 static
esock_setopt_otp_rcvbuf(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eVal)9312 ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv*       env,
9313                                      ESockDescriptor* descP,
9314                                      ERL_NIF_TERM     eVal)
9315 {
9316     const ERL_NIF_TERM* t;   // The array of the elements of the tuple
9317     int                 tsz; // The size of the tuple - should be 2
9318     unsigned int        n;
9319     size_t              bufSz;
9320     ssize_t             z;
9321 
9322     SSDBG( descP,
9323            ("SOCKET", "esock_setopt_otp_rcvbuf {%d} -> entry"
9324             "\r\n   eVal: %T"
9325             "\r\n", descP->sock, eVal) );
9326 
9327     if (! IS_OPEN(descP->readState)) {
9328         SSDBG( descP,
9329                ("SOCKET", "esock_setopt_otp_rcvbuf {%d} -> done closed\r\n",
9330                 descP->sock) );
9331         return esock_make_error(env, atom_closed);
9332     }
9333 
9334     if (esock_decode_bufsz(env,
9335                            eVal,
9336                            ESOCK_RECV_BUFFER_SIZE_DEFAULT,
9337                            &bufSz)) {
9338         n = 0; // Reported as an integer buffer size by getopt
9339     } else {
9340         if ((! GET_TUPLE(env, eVal, &tsz, &t)) ||
9341             (tsz != 2) ||
9342             (! GET_UINT(env, t[0], &n)) ||
9343             (n == 0) ||
9344             (! esock_decode_bufsz(env, t[1],
9345                                  ESOCK_RECV_BUFFER_SIZE_DEFAULT,
9346                                   &bufSz))) {
9347             SSDBG( descP,
9348                    ("SOCKET",
9349                     "esock_setopt_otp_rcvbuf {%d} -> done invalid\r\n",
9350                 descP->sock) );
9351             return esock_make_invalid(env, atom_value);
9352         }
9353     }
9354     // We do not want a buffer size that does not fit in ssize_t
9355     z = bufSz;
9356     if (bufSz != (size_t) z)
9357         return esock_make_invalid(env, atom_value);
9358 
9359     descP->rNum   = n;
9360     descP->rBufSz = bufSz;
9361 
9362     SSDBG( descP,
9363            ("SOCKET", "esock_setopt_otp_rcvbuf {%d} -> ok"
9364             "\r\n", descP->sock) );
9365 
9366     return esock_atom_ok;
9367 }
9368 #endif // #ifndef __WIN32__
9369 
9370 
9371 /* esock_setopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option
9372  */
9373 #ifndef __WIN32__
9374 static
esock_setopt_otp_rcvctrlbuf(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eVal)9375 ERL_NIF_TERM esock_setopt_otp_rcvctrlbuf(ErlNifEnv*       env,
9376                                          ESockDescriptor* descP,
9377                                          ERL_NIF_TERM     eVal)
9378 {
9379     size_t val;
9380 
9381     SSDBG( descP,
9382            ("SOCKET", "esock_setopt_otp_recvctrlbuf {%d} -> entry"
9383             "\r\n   eVal: %T"
9384             "\r\n", descP->sock, eVal) );
9385 
9386     if (! IS_OPEN(descP->readState)) {
9387         SSDBG( descP,
9388                ("SOCKET", "esock_setopt_otp_rcvctrlbuf {%d} -> done closed\r\n",
9389                 descP->sock) );
9390         return esock_make_error(env, atom_closed);
9391     }
9392 
9393     if (! esock_decode_bufsz(env,
9394                              eVal,
9395                              ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT,
9396                              &val)) {
9397         SSDBG( descP,
9398                ("SOCKET",
9399                 "esock_setopt_otp_rcvctrlbuf {%d} -> done invalid\r\n",
9400                 descP->sock) );
9401         return esock_make_invalid(env, atom_value);
9402     }
9403 
9404     descP->rCtrlSz = val;
9405 
9406     SSDBG( descP,
9407            ("SOCKET", "esock_setopt_otp_rcvctrlbuf {%d} -> ok"
9408             "\r\n", descP->sock) );
9409 
9410     return esock_atom_ok;
9411 }
9412 #endif // #ifndef __WIN32__
9413 
9414 
9415 /* esock_setopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option
9416  */
9417 #ifndef __WIN32__
9418 static
esock_setopt_otp_sndctrlbuf(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eVal)9419 ERL_NIF_TERM esock_setopt_otp_sndctrlbuf(ErlNifEnv*       env,
9420                                          ESockDescriptor* descP,
9421                                          ERL_NIF_TERM     eVal)
9422 {
9423     size_t val;
9424 
9425     SSDBG( descP,
9426            ("SOCKET", "esock_setopt_otp_sndvctrlbuf {%d} -> entry"
9427             "\r\n   eVal: %T"
9428             "\r\n", descP->sock, eVal) );
9429 
9430     if (! IS_OPEN(descP->writeState)) {
9431         SSDBG( descP,
9432                ("SOCKET", "esock_setopt_otp_sndctrlbuf {%d} -> done closed\r\n",
9433                 descP->sock) );
9434         return esock_make_error(env, atom_closed);
9435     }
9436 
9437     if (! esock_decode_bufsz(env,
9438                              eVal,
9439                              ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT,
9440                              &val)) {
9441         SSDBG( descP,
9442                ("SOCKET",
9443                 "esock_setopt_otp_sndctrlbuf {%d} -> done invalid\r\n",
9444                 descP->sock) );
9445         return esock_make_invalid(env, atom_value);
9446     }
9447 
9448     descP->wCtrlSz = val;
9449 
9450     SSDBG( descP,
9451            ("SOCKET", "esock_setopt_otp_sndctrlbuf {%d} -> ok"
9452             "\r\n", descP->sock) );
9453 
9454     return esock_atom_ok;
9455 }
9456 #endif // #ifndef __WIN32__
9457 
9458 
9459 /* esock_setopt_otp_meta - Handle the OTP (level) meta options
9460  */
9461 #ifndef __WIN32__
9462 static
esock_setopt_otp_meta(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eVal)9463 ERL_NIF_TERM esock_setopt_otp_meta(ErlNifEnv*       env,
9464                                    ESockDescriptor* descP,
9465                                    ERL_NIF_TERM     eVal)
9466 {
9467     ErlNifPid caller;
9468 
9469     ESOCK_ASSERT( enif_self(env, &caller) != NULL );
9470 
9471     SSDBG( descP,
9472            ("SOCKET", "esock_setopt_otp_meta {%d} -> entry"
9473             "\r\n   eVal: %T"
9474             "\r\n", descP->sock, eVal) );
9475 
9476     if (! IS_OPEN(descP->writeState)) {
9477         SSDBG( descP,
9478                ("SOCKET", "esock_setopt_otp_meta {%d} -> done closed\r\n",
9479                 descP->sock) );
9480         return esock_make_error(env, atom_closed);
9481     }
9482 
9483     if (COMPARE_PIDS(&descP->ctrlPid, &caller) != 0) {
9484         SSDBG( descP, ("SOCKET",
9485                        "esock_setopt_otp_meta -> not owner (%T)\r\n",
9486                        descP->ctrlPid) );
9487         return esock_make_error_invalid(env, esock_atom_not_owner);
9488     }
9489 
9490     enif_clear_env(descP->meta.env);
9491     descP->meta.ref = CP_TERM(descP->meta.env, eVal);
9492 
9493     SSDBG( descP,
9494            ("SOCKET", "esock_setopt_otp_meta {%d} -> ok"
9495             "\r\n", descP->sock) );
9496 
9497     return esock_atom_ok;
9498 }
9499 #endif // #ifndef __WIN32__
9500 
9501 
9502 /* esock_setopt_otp_use_registry - Handle the OTP (level) use_registry option
9503  */
9504 #ifndef __WIN32__
9505 static
esock_setopt_otp_use_registry(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eVal)9506 ERL_NIF_TERM esock_setopt_otp_use_registry(ErlNifEnv*       env,
9507 					   ESockDescriptor* descP,
9508 					   ERL_NIF_TERM     eVal)
9509 {
9510     BOOLEAN_T useReg = FALSE;
9511 
9512     if (! IS_OPEN(descP->writeState)) {
9513         SSDBG( descP,
9514                ("SOCKET", "esock_setopt_otp_use_registry {%d} -> closed\r\n",
9515                 descP->sock) );
9516         return esock_make_error(env, atom_closed);
9517     }
9518 
9519     if (! esock_decode_bool(eVal, &useReg))
9520       return esock_make_invalid(env, atom_value);
9521 
9522     /* We only allow turning this on! */
9523     if (! useReg)
9524         return esock_make_invalid(env, atom_value);
9525 
9526     if (!descP->useReg) {
9527       ERL_NIF_TERM sockRef = enif_make_resource(env, descP);
9528 
9529       descP->useReg = useReg;
9530       esock_send_reg_add_msg(env, descP, sockRef);
9531     }
9532 
9533     SSDBG( descP,
9534            ("SOCKET", "esock_setopt_otp_use_registry {%d} -> ok"
9535             "\r\n   eVal: %T"
9536             "\r\n", descP->sock, eVal) );
9537 
9538     return esock_atom_ok;
9539 }
9540 #endif // #ifndef __WIN32__
9541 
9542 
9543 /* The option has *not* been encoded. Instead it has been provided
9544  * in "native mode" (value is a binary, an integer or a boolean).
9545  */
9546 #ifndef __WIN32__
9547 static
esock_setopt_native(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9548 ERL_NIF_TERM esock_setopt_native(ErlNifEnv*       env,
9549                                  ESockDescriptor* descP,
9550                                  int              level,
9551                                  int              opt,
9552                                  ERL_NIF_TERM     eVal)
9553 {
9554     ErlNifBinary binary;
9555     int          integer;
9556     BOOLEAN_T    boolean;
9557     ERL_NIF_TERM result;
9558 
9559     MLOCK(descP->writeMtx);
9560 
9561     SSDBG( descP,
9562            ("SOCKET", "esock_setopt_native {%d} -> entry"
9563             "\r\n   level: %d"
9564             "\r\n   opt:   %d"
9565             "\r\n   eVal: %T"
9566             "\r\n", descP->sock,
9567             level, opt, eVal) );
9568 
9569     if (! IS_OPEN(descP->writeState)) {
9570         SSDBG( descP,
9571                ("SOCKET", "esock_setopt_native {%d} -> done closed\r\n",
9572                 descP->sock) );
9573 
9574         MUNLOCK(descP->writeMtx);
9575         return esock_make_error(env, atom_closed);
9576     }
9577 
9578     if (GET_BIN(env, eVal, &binary)) {
9579         result = esock_setopt_level_opt(env, descP, level, opt,
9580                                         binary.data, binary.size);
9581     } else if (GET_INT(env, eVal, &integer)) {
9582         result = esock_setopt_level_opt(env, descP, level, opt,
9583                                         &integer, sizeof(integer));
9584     } else if (esock_decode_bool(eVal, &boolean)) {
9585         integer = boolean ? 1 : 0;
9586         result = esock_setopt_level_opt(env, descP, level, opt,
9587                                         &integer, sizeof(integer));
9588     } else {
9589         result = esock_make_error_invalid(env, atom_value);
9590     }
9591 
9592     SSDBG( descP,
9593            ("SOCKET", "esock_setopt_native {%d} -> done when"
9594             "\r\n   result: %T"
9595             "\r\n", descP->sock, result) );
9596 
9597     MUNLOCK(descP->writeMtx);
9598     return result;
9599 }
9600 #endif // #ifndef __WIN32__
9601 
9602 
9603 /* esock_setopt - A "proper" level (option) has been specified,
9604  * and we have an value of known encoding
9605  */
9606 #ifndef __WIN32__
9607 static
esock_setopt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9608 ERL_NIF_TERM esock_setopt(ErlNifEnv*       env,
9609                           ESockDescriptor* descP,
9610                           int              level,
9611                           int              opt,
9612                           ERL_NIF_TERM     eVal)
9613 {
9614     ERL_NIF_TERM result;
9615     const struct ESockOpt *optP;
9616 
9617     MLOCK(descP->writeMtx);
9618 
9619     SSDBG( descP,
9620            ("SOCKET", "esock_setopt {%d} -> entry with"
9621             "\r\n   level:       %d"
9622             "\r\n   opt:        %d"
9623             "\r\n   eVal:        %T"
9624             "\r\n", descP->sock, level, opt, eVal) );
9625 
9626     if (! IS_OPEN(descP->writeState)) {
9627         SSDBG( descP,
9628                ("SOCKET", "esock_setopt {%d} -> done closed\r\n",
9629                 descP->sock) );
9630 
9631         MUNLOCK(descP->writeMtx);
9632         return esock_make_error(env, atom_closed);
9633     }
9634 
9635     optP = lookupOpt(level, opt);
9636 
9637     if (optP == NULL) {
9638 
9639         result = esock_make_invalid(env, atom_socket_option);
9640 
9641         SSDBG( descP,
9642                ("SOCKET",
9643                 "esock_setopt {%d} -> unknown option\r\n",
9644                 descP->sock) );
9645 
9646     } else if (optP->setopt == NULL) {
9647 
9648         result = esock_make_invalid(env, atom_socket_option);
9649 
9650         SSDBG( descP,
9651                ("SOCKET",
9652                 "esock_setopt {%d} -> opt not settable\r\n",
9653                 descP->sock) );
9654 
9655     } else {
9656 
9657         result = (optP->setopt)(env, descP, level, opt, eVal);
9658 
9659         SSDBG( descP,
9660                ("SOCKET", "esock_setopt {%d} -> done when"
9661                 "\r\n   result: %T"
9662                 "\r\n", descP->sock, result) );
9663     }
9664 
9665     MUNLOCK(descP->writeMtx);
9666     return result;
9667 }
9668 #endif // #ifndef __WIN32__
9669 
9670 
9671 #ifndef __WIN32__
9672 #if defined(SO_BINDTODEVICE)
9673 static
esock_setopt_so_bindtodevice(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9674 ERL_NIF_TERM esock_setopt_so_bindtodevice(ErlNifEnv*       env,
9675                                        ESockDescriptor* descP,
9676                                        int              level,
9677                                        int              opt,
9678                                        ERL_NIF_TERM     eVal)
9679 {
9680     return esock_setopt_str_opt(env, descP, level, opt, IFNAMSIZ, eVal);
9681 }
9682 #endif
9683 #endif // #ifndef __WIN32__
9684 
9685 
9686 #ifndef __WIN32__
9687 #if defined(SO_LINGER)
9688 static
esock_setopt_linger(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9689 ERL_NIF_TERM esock_setopt_linger(ErlNifEnv*       env,
9690                                  ESockDescriptor* descP,
9691                                  int              level,
9692                                  int              opt,
9693                                  ERL_NIF_TERM     eVal)
9694 {
9695     ERL_NIF_TERM  eOnOff, eLinger;
9696     BOOLEAN_T onOff;
9697     struct linger val;
9698 
9699     sys_memzero(&val, sizeof(val));
9700 
9701     if ((! GET_MAP_VAL(env, eVal, atom_onoff, &eOnOff)) ||
9702         (! GET_MAP_VAL(env, eVal, esock_atom_linger, &eLinger))) {
9703 
9704         if (COMPARE(eVal, esock_atom_abort) == 0) {
9705             val.l_onoff = 1;
9706             val.l_linger = 0;
9707             return esock_setopt_level_opt(env, descP, level, opt,
9708                                           &val, sizeof(val));
9709         } else
9710             return esock_make_invalid(env, atom_value);
9711     }
9712 
9713     if ((! esock_decode_bool(eOnOff, &onOff)) ||
9714         (! GET_INT(env, eLinger, &val.l_linger)) ||
9715         (val.l_linger < 0)) {
9716         return esock_make_invalid(env, atom_value);
9717     }
9718     val.l_onoff = onOff ? 1 : 0;
9719 
9720     return esock_setopt_level_opt(env, descP, level, opt,
9721                                   &val, sizeof(val));
9722 }
9723 #endif
9724 #endif // #ifndef __WIN32__
9725 
9726 
9727 
9728 #ifndef __WIN32__
9729 #if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
9730 
9731 /* esock_setopt_msfilter - Level IP MSFILTER option
9732  *
9733  * The value can be *either* the atom 'null' or a map of type ip_msfilter().
9734  */
9735 static
esock_setopt_msfilter(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9736 ERL_NIF_TERM esock_setopt_msfilter(ErlNifEnv*       env,
9737                                    ESockDescriptor* descP,
9738                                    int              level,
9739                                    int              opt,
9740                                    ERL_NIF_TERM     eVal)
9741 {
9742     ERL_NIF_TERM result;
9743 
9744     if (COMPARE(eVal, atom_null) == 0) {
9745         return
9746             esock_setopt_level_opt(env, descP, level, opt, NULL, 0);
9747     } else {
9748         struct ip_msfilter* msfP;
9749         Uint32              msfSz;
9750         ERL_NIF_TERM        eMultiAddr, eInterface, eFMode, eSList, elem, tail;
9751         unsigned int        slistLen, idx;
9752 
9753         if ((! GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) ||
9754             (! GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) ||
9755             (! GET_MAP_VAL(env, eVal, atom_mode, &eFMode)) ||
9756             (! GET_MAP_VAL(env, eVal, atom_slist, &eSList)))
9757             goto invalid;
9758 
9759         /* We start (decoding) with the slist, since without it we don't
9760          * really know how much (memory) to allocate.
9761          */
9762         if (! GET_LIST_LEN(env, eSList, &slistLen))
9763             goto invalid;
9764 
9765         msfSz = IP_MSFILTER_SIZE(slistLen);
9766         msfP  = MALLOC(msfSz);
9767         ESOCK_ASSERT( msfP != NULL );
9768 
9769         if ((! esock_decode_in_addr(env, eMultiAddr,
9770                                     &msfP->imsf_multiaddr)) ||
9771             (! esock_decode_in_addr(env, eInterface,
9772                                     &msfP->imsf_interface)) ||
9773             (! decode_msfilter_mode(env, eFMode,
9774                                     (Uint32*) &msfP->imsf_fmode)))
9775             goto free_invalid;
9776 
9777         /* And finally, extract the source addresses */
9778         msfP->imsf_numsrc = slistLen;
9779         for (idx = 0; idx < slistLen; idx++) {
9780             ESOCK_ASSERT( GET_LIST_ELEM(env, eSList, &elem, &tail) );
9781             if (! esock_decode_in_addr(env, elem,
9782                                        &msfP->imsf_slist[idx]))
9783                 goto free_invalid;
9784             eSList = tail;
9785         }
9786 
9787         /* And now, finally, set the option */
9788         result = esock_setopt_level_opt(env, descP, level, opt,
9789                                         msfP, msfSz);
9790 
9791         FREE(msfP);
9792         return result;
9793 
9794     free_invalid:
9795         FREE(msfP);
9796     invalid:
9797         return esock_make_invalid(env, atom_value);
9798     }
9799 
9800 }
9801 
9802 static
decode_msfilter_mode(ErlNifEnv * env,ERL_NIF_TERM eVal,Uint32 * mode)9803 BOOLEAN_T decode_msfilter_mode(ErlNifEnv*   env,
9804                                   ERL_NIF_TERM eVal,
9805                                   Uint32*      mode)
9806 {
9807     BOOLEAN_T result;
9808 
9809     if (COMPARE(eVal, atom_include) == 0) {
9810         *mode  = MCAST_INCLUDE;
9811         result = TRUE;
9812     } else if (COMPARE(eVal, atom_exclude) == 0) {
9813         *mode  = MCAST_EXCLUDE;
9814         result = TRUE;
9815     } else {
9816         result = FALSE;
9817     }
9818 
9819     return result;
9820 }
9821 
9822 #endif // #if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE)
9823 #endif // #ifndef __WIN32__
9824 
9825 
9826 /* esock_setopt_ip_mtu_discover - Level IP MTU_DISCOVER option
9827  *
9828  * The value is an atom of the type ip_pmtudisc().
9829  */
9830 #ifndef __WIN32__
9831 #if defined(IP_MTU_DISCOVER)
9832 static
esock_setopt_ip_mtu_discover(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9833 ERL_NIF_TERM esock_setopt_ip_mtu_discover(ErlNifEnv*       env,
9834                                           ESockDescriptor* descP,
9835                                           int              level,
9836                                           int              opt,
9837                                           ERL_NIF_TERM     eVal)
9838 {
9839     int            val;
9840 
9841     if (! decode_ip_pmtudisc(env, eVal, &val))
9842         return esock_make_invalid(env, atom_value);
9843     else
9844         return esock_setopt_level_opt(env, descP, level, opt,
9845                                       &val, sizeof(val));
9846 }
9847 #endif // #if defined(IP_MTU_DISCOVER)
9848 #endif // #ifndef __WIN32__
9849 
9850 
9851 
9852 /* esock_setopt_multicast_if - Level IP MULTICAST_IF option
9853  *
9854  * The value is either the atom 'any' or a 4-tuple.
9855  */
9856 #ifndef __WIN32__
9857 #if defined(IP_MULTICAST_IF)
9858 static
esock_setopt_multicast_if(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9859 ERL_NIF_TERM esock_setopt_multicast_if(ErlNifEnv*       env,
9860                                        ESockDescriptor* descP,
9861                                        int              level,
9862                                        int              opt,
9863                                        ERL_NIF_TERM     eVal)
9864 {
9865     ERL_NIF_TERM   result;
9866     struct in_addr ifAddr;
9867 
9868     if (! esock_decode_in_addr(env, eVal, &ifAddr)) {
9869         result = esock_make_invalid(env, atom_value);
9870     } else {
9871         result =
9872             esock_setopt_level_opt(env, descP, level, opt,
9873                                    &ifAddr, sizeof(ifAddr));
9874     }
9875 
9876     return result;
9877 }
9878 #endif
9879 #endif // #ifndef __WIN32__
9880 
9881 /* esock_setopt_tos - Level IP TOS option
9882  */
9883 #ifndef __WIN32__
9884 #if defined(IP_TOS)
9885 static
esock_setopt_tos(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9886 ERL_NIF_TERM esock_setopt_tos(ErlNifEnv*       env,
9887                               ESockDescriptor* descP,
9888                               int              level,
9889                               int              opt,
9890                               ERL_NIF_TERM     eVal)
9891 {
9892     ERL_NIF_TERM result;
9893     int          val;
9894 
9895     if (decode_ip_tos(env, eVal, &val)) {
9896         result =
9897             esock_setopt_level_opt(env, descP, level, opt,
9898                                    &val, sizeof(val));
9899     } else {
9900         result = esock_make_invalid(env, atom_value);
9901     }
9902 
9903     return result;
9904 }
9905 #endif
9906 #endif // #ifndef __WIN32__
9907 
9908 
9909 
9910 
9911 /* The value is a map with two attributes: multiaddr and interface.
9912  * The attribute 'multiaddr' is always a 4-tuple (IPv4 address).
9913  * The attribute 'interface' is either the atom 'any' or a 4-tuple
9914  * (IPv4 address).
9915  */
9916 #ifndef __WIN32__
9917 #if defined(IP_ADD_MEMBERSHIP) || defined(IP_DROP_MEMBERSHIP)
9918 static
esock_setopt_in_update_membership(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9919 ERL_NIF_TERM esock_setopt_in_update_membership(ErlNifEnv*       env,
9920                                                ESockDescriptor* descP,
9921                                                int              level,
9922                                                int              opt,
9923                                                ERL_NIF_TERM     eVal)
9924 {
9925     ERL_NIF_TERM   eMultiAddr, eInterface;
9926     struct ip_mreq mreq;
9927 
9928     if (! GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) {
9929         SSDBG( descP,
9930                ("SOCKET", "esock_setopt_in_update_membership -> "
9931                 "failed get multiaddr (map) attribute\r\n") );
9932         goto invalid;
9933     }
9934 
9935     if (! GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) {
9936         SSDBG( descP,
9937                ("SOCKET", "esock_setopt_in_update_membership -> "
9938                 "failed get interface (map) attribute\r\n") );
9939         goto invalid;
9940     }
9941 
9942     if (! esock_decode_in_addr(env,
9943                                eMultiAddr,
9944                                &mreq.imr_multiaddr)) {
9945         SSDBG( descP,
9946                ("SOCKET", "esock_setopt_in_update_membership -> "
9947                 "failed decode multiaddr %T\r\n", eMultiAddr) );
9948         goto invalid;
9949     }
9950 
9951     if (! esock_decode_in_addr(env,
9952                                eInterface,
9953                                &mreq.imr_interface)) {
9954         SSDBG( descP,
9955                ("SOCKET", "esock_setopt_in_update_membership -> "
9956                 "failed decode interface %T\r\n", eInterface) );
9957         goto invalid;
9958     }
9959 
9960     return esock_setopt_level_opt(env, descP, level, opt,
9961                                   &mreq, sizeof(mreq));
9962 
9963  invalid:
9964     return esock_make_invalid(env, atom_value);
9965 }
9966 #endif
9967 #endif // #ifndef __WIN32__
9968 
9969 
9970 /* The value is a map with three attributes: multiaddr, interface and
9971  * sourceaddr.
9972  * The attribute 'multiaddr' is always a 4-tuple (IPv4 address).
9973  * The attribute 'interface' is always a 4-tuple (IPv4 address).
9974  * The attribute 'sourceaddr' is always a 4-tuple (IPv4 address).
9975  * (IPv4 address).
9976  */
9977 #ifndef __WIN32__
9978 #if defined(IP_ADD_SOURCE_MEMBERSHIP) ||  \
9979     defined(IP_DROP_SOURCE_MEMBERSHIP) || \
9980     defined(IP_BLOCK_SOURCE) ||           \
9981     defined(IP_UNBLOCK_SOURCE)
9982 static
esock_setopt_in_update_source(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)9983 ERL_NIF_TERM esock_setopt_in_update_source(ErlNifEnv*       env,
9984                                            ESockDescriptor* descP,
9985                                            int              level,
9986                                            int              opt,
9987                                            ERL_NIF_TERM     eVal)
9988 {
9989     ERL_NIF_TERM          eMultiAddr, eInterface, eSourceAddr;
9990     struct ip_mreq_source mreq;
9991 
9992     if ((! GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) ||
9993         (! GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) ||
9994         (! GET_MAP_VAL(env, eVal, atom_sourceaddr, &eSourceAddr)) ||
9995         (! esock_decode_in_addr(env,
9996                                 eMultiAddr,
9997                                 &mreq.imr_multiaddr)) ||
9998         (! esock_decode_in_addr(env,
9999                                 eInterface,
10000                                 &mreq.imr_interface)) ||
10001         (! esock_decode_in_addr(env,
10002                                 eSourceAddr,
10003                                 &mreq.imr_sourceaddr)))
10004         goto invalid;
10005 
10006     return esock_setopt_level_opt(env, descP, level, opt,
10007                                   &mreq, sizeof(mreq));
10008  invalid:
10009     return esock_make_invalid(env, atom_value);
10010 }
10011 #endif
10012 #endif // #ifndef __WIN32__
10013 
10014 
10015 
10016 #if defined(HAVE_IPV6)
10017 
10018 
10019 
10020 #ifndef __WIN32__
10021 #if defined(IPV6_ADDRFORM)
10022 static
esock_setopt_addrform(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10023 ERL_NIF_TERM esock_setopt_addrform(ErlNifEnv*       env,
10024                                    ESockDescriptor* descP,
10025                                    int              level,
10026                                    int              opt,
10027                                    ERL_NIF_TERM     eVal)
10028 {
10029     int domain;
10030 
10031     SSDBG( descP,
10032            ("SOCKET", "esock_setopt_addrform -> entry with"
10033             "\r\n   eVal: %T"
10034             "\r\n", eVal) );
10035 
10036     if (esock_decode_domain(env, eVal, &domain) == 0)
10037         return esock_make_invalid(env, atom_value);
10038 
10039     SSDBG( descP, ("SOCKET",
10040                    "esock_setopt_addrform -> try set opt to %d\r\n",
10041                    domain) );
10042 
10043     return esock_setopt_level_opt(env, descP, level, opt,
10044                                   &domain, sizeof(domain));
10045 }
10046 #endif
10047 #endif // #ifndef __WIN32__
10048 
10049 
10050 
10051 /* esock_setopt_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option
10052  *
10053  * The value is an atom of the type ipv6_pmtudisc().
10054  */
10055 #ifndef __WIN32__
10056 #if defined(IPV6_MTU_DISCOVER)
10057 static
esock_setopt_ipv6_mtu_discover(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10058 ERL_NIF_TERM esock_setopt_ipv6_mtu_discover(ErlNifEnv*       env,
10059                                             ESockDescriptor* descP,
10060                                             int              level,
10061                                             int              opt,
10062                                             ERL_NIF_TERM     eVal)
10063 {
10064     int           val;
10065 
10066     if (! decode_ipv6_pmtudisc(env, eVal, &val))
10067         return esock_make_invalid(env, atom_value);
10068 
10069     return esock_setopt_level_opt(env, descP, level, opt,
10070                                   &val, sizeof(val));
10071 }
10072 #endif
10073 #endif // #ifndef __WIN32__
10074 
10075 
10076 #ifndef __WIN32__
10077 #if defined(IPV6_MULTICAST_HOPS)
10078 static
esock_setopt_hops(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10079 ERL_NIF_TERM esock_setopt_hops(ErlNifEnv*       env,
10080                                ESockDescriptor* descP,
10081                                int              level,
10082                                int              opt,
10083                                ERL_NIF_TERM     eVal)
10084 {
10085     int hops;
10086 
10087     if (! decode_hops(env, eVal, &hops))
10088         return esock_make_invalid(env, atom_value);
10089 
10090     return esock_setopt_level_opt(env, descP, level, opt,
10091                                   &hops, sizeof(hops));
10092 }
10093 #endif
10094 #endif // #ifndef __WIN32__
10095 
10096 
10097 #ifndef __WIN32__
10098 #if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP)
10099 static
esock_setopt_in6_update_membership(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10100 ERL_NIF_TERM esock_setopt_in6_update_membership(ErlNifEnv*       env,
10101                                                 ESockDescriptor* descP,
10102                                                 int              level,
10103                                                 int              opt,
10104                                                 ERL_NIF_TERM     eVal)
10105 {
10106     ERL_NIF_TERM     eMultiAddr, eInterface;
10107     struct ipv6_mreq mreq;
10108 
10109     if (! GET_MAP_VAL(env, eVal, atom_multiaddr, &eMultiAddr)) {
10110         SSDBG( descP,
10111                ("SOCKET", "esock_setopt_in6_update_membership -> "
10112                 "failed get multiaddr (map) attribute\r\n") );
10113         goto invalid;
10114     }
10115 
10116     if (! GET_MAP_VAL(env, eVal, atom_interface, &eInterface)) {
10117         SSDBG( descP,
10118                ("SOCKET", "esock_setopt_in6_update_membership -> "
10119                 "failed get interface (map) attribute\r\n") );
10120         goto invalid;
10121     }
10122 
10123     if (! esock_decode_in6_addr(env,
10124                                 eMultiAddr,
10125                                 &mreq.ipv6mr_multiaddr)) {
10126         SSDBG( descP,
10127                ("SOCKET", "esock_setopt_in6_update_membership -> "
10128                 "failed decode multiaddr %T\r\n", eMultiAddr) );
10129         goto invalid;
10130     }
10131 
10132     if (! GET_UINT(env, eInterface, &mreq.ipv6mr_interface)) {
10133         SSDBG( descP,
10134                ("SOCKET", "esock_setopt_in6_update_membership -> "
10135                 "failed decode interface %T\r\n", eInterface) );
10136         goto invalid;
10137     }
10138 
10139     return esock_setopt_level_opt(env, descP, level, opt,
10140                                   &mreq, sizeof(mreq));
10141 
10142  invalid:
10143     return esock_make_invalid(env, atom_value);
10144 }
10145 #endif
10146 #endif // #ifndef __WIN32__
10147 
10148 
10149 #endif // defined(HAVE_IPV6)
10150 
10151 
10152 
10153 
10154 /* esock_setopt_tcp_congestion - Level TCP CONGESTION option
10155  */
10156 #ifndef __WIN32__
10157 #if defined(TCP_CONGESTION)
10158 static
esock_setopt_tcp_congestion(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10159 ERL_NIF_TERM esock_setopt_tcp_congestion(ErlNifEnv*       env,
10160                                          ESockDescriptor* descP,
10161                                          int              level,
10162                                          int              opt,
10163                                          ERL_NIF_TERM     eVal)
10164 {
10165     int max = ESOCK_OPT_TCP_CONGESTION_NAME_MAX+1;
10166 
10167     return esock_setopt_str_opt(env, descP, level, opt, max, eVal);
10168 }
10169 #endif
10170 #endif // #ifndef __WIN32__
10171 
10172 
10173 
10174 #if defined(HAVE_SCTP)
10175 
10176 
10177 
10178 /* esock_setopt_sctp_associnfo - Level SCTP ASSOCINFO option
10179  */
10180 #ifndef __WIN32__
10181 #if defined(SCTP_ASSOCINFO)
10182 static
esock_setopt_sctp_associnfo(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10183 ERL_NIF_TERM esock_setopt_sctp_associnfo(ErlNifEnv*       env,
10184                                          ESockDescriptor* descP,
10185                                          int              level,
10186                                          int              opt,
10187                                          ERL_NIF_TERM     eVal)
10188 {
10189     ERL_NIF_TERM            eAssocId, eMaxRxt, eNumPeerDests;
10190     ERL_NIF_TERM            ePeerRWND, eLocalRWND, eCookieLife;
10191     struct sctp_assocparams assocParams;
10192     unsigned int            ui;
10193 
10194     SSDBG( descP,
10195            ("SOCKET", "esock_setopt_sctp_associnfo -> entry with"
10196             "\r\n   eVal: %T"
10197             "\r\n", eVal) );
10198 
10199     // It must be a map
10200     if (! IS_MAP(env, eVal))
10201         goto invalid;
10202 
10203     SSDBG( descP,
10204            ("SOCKET",
10205             "esock_setopt_sctp_associnfo -> extract attributes\r\n") );
10206 
10207     if ((! GET_MAP_VAL(env, eVal, atom_assoc_id,       &eAssocId))   ||
10208         (! GET_MAP_VAL(env, eVal, atom_asocmaxrxt,     &eMaxRxt))    ||
10209         (! GET_MAP_VAL(env, eVal, atom_number_peer_destinations,
10210                        &eNumPeerDests))                              ||
10211         (! GET_MAP_VAL(env, eVal, atom_peer_rwnd,      &ePeerRWND))  ||
10212         (! GET_MAP_VAL(env, eVal, atom_local_rwnd,     &eLocalRWND)) ||
10213         (! GET_MAP_VAL(env, eVal, atom_cookie_life,    &eCookieLife)))
10214         goto invalid;
10215 
10216     SSDBG( descP,
10217            ("SOCKET",
10218             "esock_setopt_sctp_associnfo -> decode attributes\r\n") );
10219 
10220     if (! decode_sctp_assoc_t(env, eAssocId, &assocParams.sasoc_assoc_id))
10221         goto invalid;
10222 
10223     /*
10224      * We should really make sure this is ok in erlang (to ensure that
10225      * the values (max-rxt and num-peer-dests) fits in 16-bits).
10226      * The value should be a 16-bit unsigned int...
10227      * Both sasoc_asocmaxrxt and sasoc_number_peer_destinations.
10228      */
10229 
10230     if (! GET_UINT(env, eMaxRxt, &ui))
10231         goto invalid;
10232     assocParams.sasoc_asocmaxrxt = (Uint16) ui;
10233 
10234     if (! GET_UINT(env, eNumPeerDests, &ui))
10235         goto invalid;
10236     assocParams.sasoc_number_peer_destinations = (Uint16) ui;
10237 
10238     if (! GET_UINT(env, ePeerRWND, &ui))
10239         goto invalid;
10240     assocParams.sasoc_peer_rwnd = (Uint32) ui;
10241 
10242     if (! GET_UINT(env, eLocalRWND, &ui))
10243         goto invalid;
10244     assocParams.sasoc_local_rwnd = (Uint32) ui;
10245 
10246     if (! GET_UINT(env, eCookieLife, &ui))
10247         goto invalid;
10248     assocParams.sasoc_cookie_life = (Uint32) ui;
10249 
10250     SSDBG( descP,
10251            ("SOCKET",
10252             "esock_setopt_sctp_associnfo -> set associnfo option\r\n") );
10253 
10254     return esock_setopt_level_opt(env, descP, level, opt,
10255                                   &assocParams, sizeof(assocParams));
10256 
10257  invalid:
10258     return esock_make_invalid(env, atom_value);
10259 }
10260 #endif
10261 #endif // #ifndef __WIN32__
10262 
10263 
10264 /* esock_setopt_sctp_events - Level SCTP EVENTS option
10265  */
10266 #ifndef __WIN32__
10267 #if defined(SCTP_EVENTS)
10268 static
esock_setopt_sctp_events(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10269 ERL_NIF_TERM esock_setopt_sctp_events(ErlNifEnv*       env,
10270                                       ESockDescriptor* descP,
10271                                       int              level,
10272                                       int              opt,
10273                                       ERL_NIF_TERM     eVal)
10274 {
10275     struct    sctp_event_subscribe events;
10276     BOOLEAN_T error;
10277 
10278     SSDBG( descP,
10279            ("SOCKET", "esock_setopt_sctp_events {%d} -> entry with"
10280             "\r\n   eVal: %T"
10281             "\r\n", descP->sock, eVal) );
10282 
10283     // It must be a map
10284     if (! IS_MAP(env, eVal))
10285         goto invalid;
10286 
10287     SSDBG( descP,
10288            ("SOCKET",
10289             "esock_setopt_sctp_events {%d} -> decode attributes\r\n",
10290             descP->sock) );
10291 
10292     error = FALSE;
10293 
10294     events.sctp_data_io_event =
10295         esock_setopt_sctp_event(env, eVal, atom_data_io, &error);
10296     events.sctp_association_event =
10297         esock_setopt_sctp_event(env, eVal, atom_association, &error);
10298     events.sctp_address_event =
10299         esock_setopt_sctp_event(env, eVal, atom_address, &error);
10300     events.sctp_send_failure_event =
10301         esock_setopt_sctp_event(env, eVal, atom_send_failure, &error);
10302     events.sctp_peer_error_event =
10303         esock_setopt_sctp_event(env, eVal, atom_peer_error, &error);
10304     events.sctp_shutdown_event =
10305         esock_setopt_sctp_event(env, eVal, atom_shutdown, &error);
10306     events.sctp_partial_delivery_event =
10307         esock_setopt_sctp_event(env, eVal, atom_partial_delivery, &error);
10308     events.sctp_adaptation_layer_event =
10309         esock_setopt_sctp_event(env, eVal, atom_adaptation_layer, &error);
10310 
10311 #if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_AUTHENTICATION_EVENT)
10312     events.sctp_authentication_event =
10313         esock_setopt_sctp_event(env, eVal, atom_authentication, &error);
10314 #endif
10315 
10316 #if defined(HAVE_STRUCT_SCTP_EVENT_SUBSCRIBE_SCTP_SENDER_DRY_EVENT)
10317     events.sctp_sender_dry_event =
10318         esock_setopt_sctp_event(env, eVal, atom_sender_dry, &error);
10319 #endif
10320 
10321     if (error) {
10322         goto invalid;
10323     } else {
10324         ERL_NIF_TERM result;
10325 
10326         result = esock_setopt_level_opt(env, descP, level, opt,
10327                                         &events, sizeof(events));
10328         SSDBG( descP,
10329                ("SOCKET",
10330                 "esock_setopt_sctp_events {%d} -> set events -> %T\r\n",
10331                 descP->sock, result) );
10332 
10333         return result;
10334     }
10335 
10336  invalid:
10337     SSDBG( descP,
10338            ("SOCKET",
10339             "esock_setopt_sctp_events {%d} -> invalid\r\n",
10340             descP->sock) );
10341 
10342     return esock_make_invalid(env, atom_value);
10343 }
10344 
10345 /* Return the value to make use of automatic type casting.
10346  * Set *error if something goes wrong.
10347  */
esock_setopt_sctp_event(ErlNifEnv * env,ERL_NIF_TERM eMap,ERL_NIF_TERM eKey,BOOLEAN_T * error)10348 static int esock_setopt_sctp_event(ErlNifEnv   *env,
10349                                    ERL_NIF_TERM eMap,
10350                                    ERL_NIF_TERM eKey,
10351                                    BOOLEAN_T   *error)
10352 {
10353     ERL_NIF_TERM eVal;
10354     BOOLEAN_T    val;
10355 
10356     if (GET_MAP_VAL(env, eMap, eKey, &eVal))
10357         if (esock_decode_bool(eVal, &val))
10358             return (int) val;
10359 
10360     *error = TRUE;
10361     return 0;
10362 }
10363 #endif
10364 #endif // #ifndef __WIN32__
10365 
10366 
10367 /* esock_setopt_sctp_initmsg - Level SCTP INITMSG option
10368  */
10369 #ifndef __WIN32__
10370 #if defined(SCTP_INITMSG)
10371 static
esock_setopt_sctp_initmsg(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10372 ERL_NIF_TERM esock_setopt_sctp_initmsg(ErlNifEnv*       env,
10373                                        ESockDescriptor* descP,
10374                                        int              level,
10375                                        int              opt,
10376                                        ERL_NIF_TERM     eVal)
10377 {
10378     ERL_NIF_TERM        eNumOut, eMaxIn, eMaxAttempts, eMaxInitTO;
10379     struct sctp_initmsg initMsg;
10380     unsigned int        tmp;
10381 
10382     SSDBG( descP,
10383            ("SOCKET", "esock_setopt_sctp_initmsg -> entry with"
10384             "\r\n   eVal: %T"
10385             "\r\n", eVal) );
10386 
10387     // It must be a map
10388     if (! IS_MAP(env, eVal))
10389         goto invalid;
10390 
10391     SSDBG( descP,
10392            ("SOCKET",
10393             "esock_setopt_sctp_initmsg -> extract attributes\r\n") );
10394 
10395     if ((! GET_MAP_VAL(env, eVal, atom_num_outstreams, &eNumOut)) ||
10396         (! GET_MAP_VAL(env, eVal, atom_max_instreams,  &eMaxIn)) ||
10397         (! GET_MAP_VAL(env, eVal, atom_max_attempts,   &eMaxAttempts)) ||
10398         (! GET_MAP_VAL(env, eVal, atom_max_init_timeo, &eMaxInitTO)))
10399         goto invalid;
10400 
10401     SSDBG( descP,
10402            ("SOCKET",
10403             "esock_setopt_sctp_initmsg -> decode attributes\r\n") );
10404 
10405     if (! GET_UINT(env, eNumOut, &tmp))
10406         goto invalid;
10407     initMsg.sinit_num_ostreams = (Uint16) tmp;
10408 
10409     if (! GET_UINT(env, eMaxIn, &tmp))
10410         goto invalid;
10411     initMsg.sinit_max_instreams = (Uint16) tmp;
10412 
10413     if (! GET_UINT(env, eMaxAttempts, &tmp))
10414         goto invalid;
10415     initMsg.sinit_max_attempts = (Uint16) tmp;
10416 
10417     if (! GET_UINT(env, eMaxInitTO, &tmp))
10418         goto invalid;
10419     initMsg.sinit_max_init_timeo = (Uint16) tmp;
10420 
10421     SSDBG( descP,
10422            ("SOCKET",
10423             "esock_setopt_sctp_initmsg -> set initmsg option\r\n") );
10424 
10425     return esock_setopt_level_opt(env, descP, level, opt,
10426                                   &initMsg, sizeof(initMsg));
10427 
10428  invalid:
10429     return esock_make_invalid(env, atom_value);
10430 }
10431 #endif
10432 #endif // #ifndef __WIN32__
10433 
10434 
10435 /* esock_setopt_sctp_rtoinfo - Level SCTP RTOINFO option
10436  */
10437 #ifndef __WIN32__
10438 #if defined(SCTP_RTOINFO)
10439 static
esock_setopt_sctp_rtoinfo(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10440 ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv*       env,
10441                                        ESockDescriptor* descP,
10442                                        int              level,
10443                                        int              opt,
10444                                        ERL_NIF_TERM     eVal)
10445 {
10446     ERL_NIF_TERM        eAssocId, eInitial, eMax, eMin;
10447     struct sctp_rtoinfo rtoInfo;
10448 
10449     SSDBG( descP,
10450            ("SOCKET", "esock_setopt_sctp_rtoinfo -> entry with"
10451             "\r\n   eVal: %T"
10452             "\r\n", eVal) );
10453 
10454     // It must be a map
10455     if (! IS_MAP(env, eVal))
10456         goto invalid;
10457 
10458     SSDBG( descP,
10459            ("SOCKET",
10460             "esock_setopt_sctp_rtoinfo -> extract attributes\r\n") );
10461 
10462     if ((! GET_MAP_VAL(env, eVal, atom_assoc_id, &eAssocId)) ||
10463         (! GET_MAP_VAL(env, eVal, atom_initial,  &eInitial)) ||
10464         (! GET_MAP_VAL(env, eVal, atom_max,      &eMax)) ||
10465         (! GET_MAP_VAL(env, eVal, atom_min,      &eMin)))
10466         goto invalid;
10467 
10468     SSDBG( descP,
10469            ("SOCKET",
10470             "esock_setopt_sctp_rtoinfo -> decode attributes\r\n") );
10471 
10472     if (! decode_sctp_assoc_t(env, eAssocId, &rtoInfo.srto_assoc_id))
10473         goto invalid;
10474 
10475     if ((! GET_UINT(env, eInitial, &rtoInfo.srto_initial)) ||
10476         (! GET_UINT(env, eMax, &rtoInfo.srto_max)) ||
10477         (! GET_UINT(env, eMin, &rtoInfo.srto_min)))
10478         goto invalid;
10479 
10480     SSDBG( descP,
10481            ("SOCKET",
10482             "esock_setopt_sctp_rtoinfo -> set associnfo option\r\n") );
10483 
10484     return esock_setopt_level_opt(env, descP, level, opt,
10485                                   &rtoInfo, sizeof(rtoInfo));
10486 
10487  invalid:
10488     return esock_make_invalid(env, atom_value);
10489 }
10490 #endif
10491 #endif // #ifndef __WIN32__
10492 
10493 
10494 
10495 #endif // defined(HAVE_SCTP)
10496 
10497 
10498 
10499 
10500 /* esock_setopt_bool_opt - set an option that has an (integer) bool value
10501  */
10502 #ifndef __WIN32__
10503 static
esock_setopt_bool_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10504 ERL_NIF_TERM esock_setopt_bool_opt(ErlNifEnv*       env,
10505                                    ESockDescriptor* descP,
10506                                    int              level,
10507                                    int              opt,
10508                                    ERL_NIF_TERM     eVal)
10509 {
10510     BOOLEAN_T    val;
10511     int          ival;
10512 
10513     if (! esock_decode_bool(eVal, &val))
10514         return esock_make_invalid(env, atom_value);
10515 
10516     ival = (val) ? 1 : 0;
10517     return esock_setopt_level_opt(env, descP, level, opt,
10518                                   &ival, sizeof(ival));
10519 }
10520 #endif // #ifndef __WIN32__
10521 
10522 
10523 /* esock_setopt_int_opt - set an option that has an integer value
10524  */
10525 #ifndef __WIN32__
10526 static
esock_setopt_int_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10527 ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv*       env,
10528                                   ESockDescriptor* descP,
10529                                   int              level,
10530                                   int              opt,
10531                                   ERL_NIF_TERM     eVal)
10532 {
10533     ERL_NIF_TERM result;
10534     int          val;
10535 
10536     if (GET_INT(env, eVal, &val)) {
10537         result =
10538             esock_setopt_level_opt(env, descP, level, opt,
10539                                    &val, sizeof(val));
10540     } else {
10541         result = esock_make_invalid(env, atom_value);
10542     }
10543     return result;
10544 }
10545 #endif // #ifndef __WIN32__
10546 
10547 
10548 /* esock_setopt_str_opt - set an option that has an string value
10549  */
10550 #ifndef __WIN32__
10551 #if defined(USE_SETOPT_STR_OPT)
10552 static
esock_setopt_str_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,int max,ERL_NIF_TERM eVal)10553 ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv*       env,
10554                                   ESockDescriptor* descP,
10555                                   int              level,
10556                                   int              opt,
10557                                   int              max,
10558                                   ERL_NIF_TERM     eVal)
10559 {
10560     ERL_NIF_TERM result;
10561     int          optLen;
10562     char*        val = MALLOC(max);
10563 
10564     ESOCK_ASSERT( val != NULL );
10565 
10566     if ((optLen = GET_STR(env, eVal, val, max)) > 0) {
10567         optLen--;
10568 
10569         result =
10570             esock_setopt_level_opt(env, descP, level, opt,
10571                                    val, optLen);
10572     } else {
10573         result = esock_make_invalid(env, atom_value);
10574     }
10575 
10576     FREE(val);
10577 
10578     return result;
10579 }
10580 #endif
10581 #endif // #ifndef __WIN32__
10582 
10583 
10584 /* esock_setopt_timeval_opt - set an option that has an (timeval) bool value
10585  */
10586 #ifndef __WIN32__
10587 #if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \
10588     && defined(ESOCK_USE_RCVSNDTIMEO)
10589 static
esock_setopt_timeval_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM eVal)10590 ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv*       env,
10591                                       ESockDescriptor* descP,
10592                                       int              level,
10593                                       int              opt,
10594                                       ERL_NIF_TERM     eVal)
10595 {
10596     ERL_NIF_TERM   result;
10597     struct timeval timeVal;
10598 
10599     SSDBG( descP,
10600            ("SOCKET", "esock_setopt_timeval_opt -> entry with"
10601             "\r\n   eVal: %T"
10602             "\r\n", eVal) );
10603 
10604     if (! esock_decode_timeval(env, eVal, &timeVal))
10605         return esock_make_invalid(env, atom_value);
10606 
10607     SSDBG( descP,
10608            ("SOCKET", "esock_setopt_timeval_opt -> set timeval option\r\n") );
10609 
10610     result =
10611         esock_setopt_level_opt(env, descP, level, opt,
10612                                &timeVal, sizeof(timeVal));
10613 
10614     SSDBG( descP,
10615            ("SOCKET", "esock_setopt_timeval_opt -> done with"
10616             "\r\n   result: %T"
10617             "\r\n", result) );
10618 
10619     return result;
10620 
10621 }
10622 #endif
10623 #endif // #ifndef __WIN32__
10624 
10625 
10626 #ifndef __WIN32__
esock_setopt_level_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,void * optVal,socklen_t optLen)10627 static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv*       env,
10628                                            ESockDescriptor* descP,
10629                                            int              level,
10630                                            int              opt,
10631                                            void*            optVal,
10632                                            socklen_t        optLen)
10633 {
10634     if (socket_setopt(descP->sock, level, opt, optVal, optLen))
10635         return esock_make_error_errno(env, sock_errno());
10636     else
10637         return esock_atom_ok;
10638 }
10639 #endif // #ifndef __WIN32__
10640 
10641 
10642 /* +++ socket_setopt +++
10643  *
10644  * <Per H @ Tail-f>
10645  * The original code here had problems that possibly
10646  * only occur if you abuse it for non-INET sockets, but anyway:
10647  * a) If the getsockopt for SO_PRIORITY or IP_TOS failed, the actual
10648  *    requested setsockopt was never even attempted.
10649  * b) If {get,set}sockopt for one of IP_TOS and SO_PRIORITY failed,
10650  *    but ditto for the other worked and that was actually the requested
10651  *    option, failure was still reported to erlang.
10652  * </Per H @ Tail-f>
10653  *
10654  * <PaN>
10655  * The relations between SO_PRIORITY, TOS and other options
10656  * is not what you (or at least I) would expect...:
10657  * If TOS is set after priority, priority is zeroed.
10658  * If any other option is set after tos, tos might be zeroed.
10659  * Therefore, save tos and priority. If something else is set,
10660  * restore both after setting, if  tos is set, restore only
10661  * prio and if prio is set restore none... All to keep the
10662  * user feeling socket options are independent.
10663  * </PaN>
10664  */
10665 #ifndef __WIN32__
10666 static
socket_setopt(int sock,int level,int opt,const void * optVal,const socklen_t optLen)10667 int socket_setopt(int sock, int level, int opt,
10668                   const void* optVal, const socklen_t optLen)
10669 {
10670     int res;
10671 
10672 #if  defined(IP_TOS) && defined(SOL_IP) && defined(SO_PRIORITY)
10673     int          tmpIValPRIO = 0;
10674     int          tmpIValTOS = 0;
10675     int          resPRIO;
10676     int          resTOS;
10677     SOCKOPTLEN_T tmpArgSzPRIO = sizeof(tmpIValPRIO);
10678     SOCKOPTLEN_T tmpArgSzTOS  = sizeof(tmpIValTOS);
10679 
10680     resPRIO = sock_getopt(sock, SOL_SOCKET, SO_PRIORITY,
10681                            &tmpIValPRIO, &tmpArgSzPRIO);
10682     resTOS  = sock_getopt(sock, SOL_IP, IP_TOS,
10683                           &tmpIValTOS, &tmpArgSzTOS);
10684 
10685     res = sock_setopt(sock, level, opt, optVal, optLen);
10686     if (res == 0) {
10687 
10688         /* Ok, now we *maybe* need to "maybe" restore PRIO and TOS...
10689          * maybe, possibly, ...
10690          */
10691 
10692         if (opt != SO_PRIORITY) {
10693 	    if ((opt != IP_TOS) && (resTOS == 0)) {
10694 		resTOS = sock_setopt(sock, SOL_IP, IP_TOS,
10695                                      (void *) &tmpIValTOS,
10696                                      tmpArgSzTOS);
10697                 res = resTOS;
10698             }
10699 	    if ((res == 0) && (resPRIO == 0)) {
10700 		resPRIO = sock_setopt(sock, SOL_SOCKET, SO_PRIORITY,
10701                                       &tmpIValPRIO,
10702                                       tmpArgSzPRIO);
10703 
10704                 /* Some kernels set a SO_PRIORITY by default
10705                  * that you are not permitted to reset,
10706                  * silently ignore this error condition.
10707                  */
10708 
10709                 if ((resPRIO != 0) && (sock_errno() == EPERM)) {
10710                     res = 0;
10711                 } else {
10712                     res = resPRIO;
10713 		}
10714 	    }
10715 	}
10716     }
10717 
10718 #else
10719 
10720     res = sock_setopt(sock, level, opt, optVal, optLen);
10721 
10722 #endif
10723 
10724     return res;
10725 }
10726 #endif // #ifndef __WIN32__
10727 
10728 
10729 
10730 /* ----------------------------------------------------------------------
10731  * nif_getopt
10732  *
10733  * Description:
10734  * Get socket option.
10735  * Its possible to use a ValueSpec to select
10736  * how the value should be decoded.
10737  *
10738  * Arguments:
10739  * Socket      (ref) - Points to the socket descriptor.
10740  * Level       (int) - Protocol level, encoded or native
10741  * Opt         (int) - Option, encoded or native
10742  * ValueSpec  (term) - How to decode the value [optional]
10743  */
10744 
10745 static
nif_getopt(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])10746 ERL_NIF_TERM nif_getopt(ErlNifEnv*         env,
10747                         int                argc,
10748                         const ERL_NIF_TERM argv[])
10749 {
10750 #ifdef __WIN32__
10751     return enif_raise_exception(env, MKA(env, "notsup"));
10752 #else
10753     ESockDescriptor* descP;
10754     int              level, opt;
10755 
10756     ESOCK_ASSERT( (argc == 3) || (argc == 4) );
10757 
10758     SGDBG( ("SOCKET", "nif_getopt -> entry with argc: %d\r\n", argc) );
10759 
10760     if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
10761         SGDBG( ("SOCKET", "nif_getopt -> failed initial args check\r\n") );
10762         return enif_make_badarg(env);
10763     }
10764 
10765     if (! GET_INT(env, argv[2], &opt)) {
10766         SSDBG( descP,
10767                ("SOCKET", "nif_getopt -> failed initial args check\r\n") );
10768         if (! IS_INTEGER(env, argv[2]))
10769             return enif_make_badarg(env);
10770         else
10771             return esock_make_error_integer_range(env, argv[2]);
10772     }
10773 
10774     if (esock_decode_level(env, argv[1], &level)) {
10775         if (argc == 4) {
10776             ERL_NIF_TERM valueSpec = argv[3];
10777             return esock_getopt_native(env, descP, level, opt, valueSpec);
10778         } else {
10779             return esock_getopt(env, descP, level, opt);
10780         }
10781     }
10782 
10783     if ((COMPARE(argv[1], atom_otp) == 0) &&
10784         (argc == 3)) {
10785         return esock_getopt_otp(env, descP, opt) ;
10786     }
10787 
10788     SGDBG( ("SOCKET", "nif_getopt -> failed args check\r\n") );
10789     if (IS_INTEGER(env, argv[1]))
10790         return esock_make_error_integer_range(env, argv[1]);
10791     else
10792         return enif_make_badarg(env);
10793 
10794 #endif // #ifdef __WIN32__  #else
10795 }
10796 
10797 
10798 
10799 /* esock_getopt_otp - Handle OTP (level) options
10800  */
10801 #ifndef __WIN32__
10802 static
esock_getopt_otp(ErlNifEnv * env,ESockDescriptor * descP,int eOpt)10803 ERL_NIF_TERM esock_getopt_otp(ErlNifEnv*       env,
10804                               ESockDescriptor* descP,
10805                               int              eOpt)
10806 {
10807     ERL_NIF_TERM result;
10808 
10809     SSDBG( descP,
10810            ("SOCKET", "esock_getopt_otp -> entry with"
10811             "\r\n   eOpt: %d"
10812             "\r\n", eOpt) );
10813 
10814     switch (eOpt) {
10815     case ESOCK_OPT_OTP_DEBUG:
10816         MLOCK(descP->readMtx);
10817         result = esock_getopt_otp_debug(env, descP);
10818         MUNLOCK(descP->readMtx);
10819         break;
10820 
10821     case ESOCK_OPT_OTP_IOW:
10822         MLOCK(descP->readMtx);
10823         result = esock_getopt_otp_iow(env, descP);
10824         MUNLOCK(descP->readMtx);
10825         break;
10826 
10827     case ESOCK_OPT_OTP_CTRL_PROC:
10828         MLOCK(descP->readMtx);
10829         result = esock_getopt_otp_ctrl_proc(env, descP);
10830         MUNLOCK(descP->readMtx);
10831         break;
10832 
10833     case ESOCK_OPT_OTP_RCVBUF:
10834         MLOCK(descP->readMtx);
10835         result = esock_getopt_otp_rcvbuf(env, descP);
10836         MUNLOCK(descP->readMtx);
10837         break;
10838 
10839     case ESOCK_OPT_OTP_RCVCTRLBUF:
10840         MLOCK(descP->readMtx);
10841         result = esock_getopt_otp_rcvctrlbuf(env, descP);
10842         MUNLOCK(descP->readMtx);
10843         break;
10844 
10845     case ESOCK_OPT_OTP_SNDCTRLBUF:
10846         MLOCK(descP->writeMtx);
10847         result = esock_getopt_otp_sndctrlbuf(env, descP);
10848         MUNLOCK(descP->writeMtx);
10849         break;
10850 
10851     case ESOCK_OPT_OTP_FD:
10852         MLOCK(descP->readMtx);
10853         result = esock_getopt_otp_fd(env, descP);
10854         MUNLOCK(descP->readMtx);
10855         break;
10856 
10857     case ESOCK_OPT_OTP_META:
10858         MLOCK(descP->writeMtx);
10859         result = esock_getopt_otp_meta(env, descP);
10860         MUNLOCK(descP->writeMtx);
10861         break;
10862 
10863     case ESOCK_OPT_OTP_USE_REGISTRY:
10864         MLOCK(descP->readMtx);
10865         result = esock_getopt_otp_use_registry(env, descP);
10866         MUNLOCK(descP->readMtx);
10867         break;
10868 
10869         /* *** INTERNAL *** */
10870     case ESOCK_OPT_OTP_DOMAIN:
10871         MLOCK(descP->readMtx);
10872         result = esock_getopt_otp_domain(env, descP);
10873         MUNLOCK(descP->readMtx);
10874         break;
10875 
10876 #if 0
10877     case ESOCK_OPT_OTP_TYPE:
10878         MLOCK(descP->readMtx);
10879         result = esock_getopt_otp_type(env, descP);
10880         MUNLOCK(descP->readMtx);
10881         break;
10882 
10883     case ESOCK_OPT_OTP_PROTOCOL:
10884         MLOCK(descP->readMtx);
10885         result = esock_getopt_otp_protocol(env, descP);
10886         MUNLOCK(descP->readMtx);
10887         break;
10888 
10889     case ESOCK_OPT_OTP_DTP:
10890         MLOCK(descP->readMtx);
10891         result = esock_getopt_otp_dtp(env, descP);
10892         MUNLOCK(descP->readMtx);
10893         break;
10894 #endif
10895 
10896     default:
10897         MLOCK(descP->readMtx);
10898         SSDBG( descP,
10899                ("SOCKET", "esock_getopt_otp {%d} -> invalid with"
10900                 "\r\n   eOpt: %d"
10901                 "\r\n", descP->sock, eOpt) );
10902         MUNLOCK(descP->readMtx);
10903 
10904         /* This is an internal error - prim_inet gave us junk */
10905         result =
10906             esock_raise_invalid(env,
10907                                 MKT2(env,
10908                                      atom_otp_socket_option,
10909                                      MKI(env, eOpt)));
10910         break;
10911     }
10912 
10913     return result;
10914 }
10915 #endif // #ifndef __WIN32__
10916 
10917 /* esock_getopt_otp_debug - Handle the OTP (level) debug option
10918  */
10919 #ifndef __WIN32__
10920 static
esock_getopt_otp_debug(ErlNifEnv * env,ESockDescriptor * descP)10921 ERL_NIF_TERM esock_getopt_otp_debug(ErlNifEnv*       env,
10922                                     ESockDescriptor* descP)
10923 {
10924     ERL_NIF_TERM eVal;
10925 
10926     if (! IS_OPEN(descP->readState)) {
10927         SSDBG( descP,
10928                ("SOCKET", "esock_getopt_otp_debug {%d} -> done closed\r\n",
10929                 descP->sock) );
10930         return esock_make_error(env, atom_closed);
10931     }
10932 
10933     eVal = esock_encode_bool(descP->dbg);
10934 
10935     return esock_make_ok2(env, eVal);
10936 }
10937 #endif // #ifndef __WIN32__
10938 
10939 /* esock_getopt_otp_iow - Handle the OTP (level) iow option
10940  */
10941 #ifndef __WIN32__
10942 static
esock_getopt_otp_iow(ErlNifEnv * env,ESockDescriptor * descP)10943 ERL_NIF_TERM esock_getopt_otp_iow(ErlNifEnv*       env,
10944                                   ESockDescriptor* descP)
10945 {
10946     ERL_NIF_TERM eVal;
10947 
10948     if (! IS_OPEN(descP->readState)) {
10949         SSDBG( descP,
10950                ("SOCKET", "esock_getopt_otp_iow {%d} -> done closed\r\n",
10951                 descP->sock) );
10952         return esock_make_error(env, atom_closed);
10953     }
10954 
10955     eVal = esock_encode_bool(descP->iow);
10956 
10957     SSDBG( descP,
10958            ("SOCKET", "esock_getopt_otp_iow {%d} ->"
10959             "\r\n   eVal: %T"
10960             "\r\n", descP->sock, eVal) );
10961 
10962     return esock_make_ok2(env, eVal);
10963 }
10964 #endif // #ifndef __WIN32__
10965 
10966 
10967 /* esock_getopt_otp_ctrl_proc - Handle the OTP (level) controlling_process option
10968  */
10969 #ifndef __WIN32__
10970 static
esock_getopt_otp_ctrl_proc(ErlNifEnv * env,ESockDescriptor * descP)10971 ERL_NIF_TERM esock_getopt_otp_ctrl_proc(ErlNifEnv*       env,
10972                                         ESockDescriptor* descP)
10973 {
10974     ERL_NIF_TERM eVal;
10975 
10976     if (! IS_OPEN(descP->readState)) {
10977         SSDBG( descP,
10978                ("SOCKET",
10979                 "esock_getopt_otp_ctrl_proc {%d} -> done closed\r\n",
10980                 descP->sock) );
10981         return esock_make_error(env, atom_closed);
10982     }
10983 
10984     eVal = MKPID(env, &descP->ctrlPid);
10985 
10986     SSDBG( descP,
10987            ("SOCKET", "esock_getopt_otp_ctrlProc {%d} ->"
10988             "\r\n   eVal: %T"
10989             "\r\n", descP->sock, eVal) );
10990 
10991     return esock_make_ok2(env, eVal);
10992 }
10993 #endif // #ifndef __WIN32__
10994 
10995 
10996 /* esock_getopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option
10997  */
10998 #ifndef __WIN32__
10999 static
esock_getopt_otp_rcvbuf(ErlNifEnv * env,ESockDescriptor * descP)11000 ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv*       env,
11001                                      ESockDescriptor* descP)
11002 {
11003     ERL_NIF_TERM eVal;
11004 
11005     if (! IS_OPEN(descP->readState)) {
11006         SSDBG( descP,
11007                ("SOCKET", "esock_getopt_otp_rcvbuf {%d} -> done closed\r\n",
11008                 descP->sock) );
11009         return esock_make_error(env, atom_closed);
11010     }
11011 
11012     if (descP->rNum == 0) {
11013         eVal = MKUL(env, (unsigned long) descP->rBufSz);
11014     } else {
11015         eVal = MKT2(env,
11016                     MKI(env, descP->rNum),
11017                     MKUL(env, (unsigned long) descP->rBufSz));
11018     }
11019 
11020     SSDBG( descP,
11021            ("SOCKET", "esock_getopt_otp_rcvbuf {%d} ->"
11022             "\r\n   eVal: %T"
11023             "\r\n", descP->sock, eVal) );
11024 
11025     return esock_make_ok2(env, eVal);
11026 }
11027 #endif // #ifndef __WIN32__
11028 
11029 
11030 /* esock_getopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option
11031  */
11032 #ifndef __WIN32__
11033 static
esock_getopt_otp_rcvctrlbuf(ErlNifEnv * env,ESockDescriptor * descP)11034 ERL_NIF_TERM esock_getopt_otp_rcvctrlbuf(ErlNifEnv*       env,
11035                                          ESockDescriptor* descP)
11036 {
11037     ERL_NIF_TERM eVal;
11038 
11039     if (! IS_OPEN(descP->readState)) {
11040         SSDBG( descP,
11041                ("SOCKET",
11042                 "esock_getopt_otp_rcvctrlbuf {%d} -> done closed\r\n",
11043                 descP->sock) );
11044         return esock_make_error(env, atom_closed);
11045     }
11046 
11047     eVal = MKUL(env, (unsigned long) descP->rCtrlSz);
11048 
11049     SSDBG( descP,
11050            ("SOCKET", "esock_getopt_otp_rcvctrlbuf {%d} ->"
11051             "\r\n   eVal: %T"
11052             "\r\n", descP->sock, eVal) );
11053 
11054     return esock_make_ok2(env, eVal);
11055 }
11056 #endif // #ifndef __WIN32__
11057 
11058 
11059 /* esock_getopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option
11060  */
11061 #ifndef __WIN32__
11062 static
esock_getopt_otp_sndctrlbuf(ErlNifEnv * env,ESockDescriptor * descP)11063 ERL_NIF_TERM esock_getopt_otp_sndctrlbuf(ErlNifEnv*       env,
11064                                          ESockDescriptor* descP)
11065 {
11066     ERL_NIF_TERM eVal;
11067 
11068     if (! IS_OPEN(descP->writeState)) {
11069         SSDBG( descP,
11070                ("SOCKET",
11071                 "esock_getopt_otp_sndctrlbuf {%d} -> done closed\r\n",
11072                 descP->sock) );
11073         return esock_make_error(env, atom_closed);
11074     }
11075 
11076     eVal = MKUL(env, (unsigned long) descP->wCtrlSz);
11077 
11078     SSDBG( descP,
11079            ("SOCKET", "esock_getopt_otp_sndctrlbuf {%d} ->"
11080             "\r\n   eVal: %T"
11081             "\r\n", descP->sock, eVal) );
11082 
11083     return esock_make_ok2(env, eVal);
11084 }
11085 #endif // #ifndef __WIN32__
11086 
11087 
11088 /* esock_getopt_otp_fd - Handle the OTP (level) fd option
11089  */
11090 #ifndef __WIN32__
11091 static
esock_getopt_otp_fd(ErlNifEnv * env,ESockDescriptor * descP)11092 ERL_NIF_TERM esock_getopt_otp_fd(ErlNifEnv*       env,
11093                                  ESockDescriptor* descP)
11094 {
11095     ERL_NIF_TERM eVal;
11096 
11097     if (! IS_OPEN(descP->readState)) {
11098         SSDBG( descP,
11099                ("SOCKET", "esock_getopt_otp_debug {%d} -> done closed\r\n",
11100                 descP->sock) );
11101         return esock_make_error(env, atom_closed);
11102     }
11103 
11104     eVal = MKI(env, descP->sock);
11105 
11106     SSDBG( descP,
11107            ("SOCKET", "esock_getopt_otp_fd {%d} ->"
11108             "\r\n   eVal: %T"
11109             "\r\n", descP->sock, eVal) );
11110 
11111     return esock_make_ok2(env, eVal);
11112 }
11113 #endif // #ifndef __WIN32__
11114 
11115 
11116 /* esock_getopt_otp_meta - Handle the OTP (level) meta option
11117  */
11118 #ifndef __WIN32__
11119 static
esock_getopt_otp_meta(ErlNifEnv * env,ESockDescriptor * descP)11120 ERL_NIF_TERM esock_getopt_otp_meta(ErlNifEnv*       env,
11121                                    ESockDescriptor* descP)
11122 {
11123     ERL_NIF_TERM eVal;
11124 
11125     if (! IS_OPEN(descP->writeState)) {
11126         SSDBG( descP,
11127                ("SOCKET", "esock_getopt_otp_meta {%d} -> done closed\r\n",
11128                 descP->sock) );
11129         return esock_make_error(env, atom_closed);
11130     }
11131 
11132     eVal = CP_TERM(env, descP->meta.ref);
11133 
11134     SSDBG( descP,
11135            ("SOCKET", "esock_getopt_otp_meta {%d} ->"
11136             "\r\n   eVal: %T"
11137             "\r\n", descP->sock, eVal) );
11138 
11139     return esock_make_ok2(env, eVal);
11140 }
11141 #endif // #ifndef __WIN32__
11142 
11143 
11144 /* esock_getopt_otp_use_registry - Handle the OTP (level) use_registry option
11145  */
11146 #ifndef __WIN32__
11147 static
esock_getopt_otp_use_registry(ErlNifEnv * env,ESockDescriptor * descP)11148 ERL_NIF_TERM esock_getopt_otp_use_registry(ErlNifEnv*       env,
11149                                            ESockDescriptor* descP)
11150 {
11151     ERL_NIF_TERM eVal = esock_encode_bool(descP->useReg);
11152 
11153     return esock_make_ok2(env, eVal);
11154 }
11155 #endif
11156 
11157 
11158 /*
11159  * esock_getopt_otp_domain - Handle the OTP (level) domain option
11160  */
11161 #ifndef __WIN32__
11162 static
esock_getopt_otp_domain(ErlNifEnv * env,ESockDescriptor * descP)11163 ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv*       env,
11164                                      ESockDescriptor* descP)
11165 {
11166     ERL_NIF_TERM domain, result;
11167 
11168     if (! IS_OPEN(descP->readState)) {
11169         SSDBG( descP,
11170                ("SOCKET", "esock_getopt_otp_domain {%d} -> done closed\r\n",
11171                 descP->sock) );
11172         return esock_make_error(env, atom_closed);
11173     }
11174 
11175     esock_encode_domain(env, descP->domain, &domain);
11176     result = esock_make_ok2(env, domain);
11177 
11178     SSDBG( descP,
11179            ("SOCKET", "esock_getopt_otp_domain {%d} ->"
11180             "\r\n   result: %T"
11181             "\r\n", descP->sock, result) );
11182 
11183     return result;
11184 }
11185 #endif // #ifndef __WIN32__
11186 
11187 
11188 
11189 #if 0
11190 
11191 /*
11192  * esock_getopt_otp_type - Handle the OTP (level) type options.
11193  */
11194 #ifndef __WIN32__
11195 static
11196 ERL_NIF_TERM esock_getopt_otp_type(ErlNifEnv*       env,
11197                                    ESockDescriptor* descP)
11198 {
11199     ERL_NIF_TERM type, result;
11200 
11201     if (! IS_OPEN(descP->readState)) {
11202         SSDBG( descP,
11203                ("SOCKET", "esock_getopt_otp_type {%d} -> done closed\r\n",
11204                 descP->sock) );
11205         return esock_make_error(env, atom_closed);
11206     }
11207 
11208     esock_encode_type(env, descP->type, &type);
11209     result = esock_make_ok2(env, type);
11210 
11211     SSDBG( descP,
11212            ("SOCKET", "esock_getopt_otp_type {%d} ->"
11213             "\r\n   result: %T"
11214             "\r\n", descP->sock, result) );
11215 
11216     return result;
11217 }
11218 #endif // #ifndef __WIN32__
11219 
11220 
11221 /*
11222  * esock_getopt_otp_protocol - Handle the OTP (level) protocol options.
11223  */
11224 #ifndef __WIN32__
11225 static
11226 ERL_NIF_TERM esock_getopt_otp_protocol(ErlNifEnv*       env,
11227                                        ESockDescriptor* descP)
11228 {
11229     ERL_NIF_TERM protocol, result;
11230 
11231     if (! IS_OPEN(descP->readState)) {
11232         SSDBG( descP,
11233                ("SOCKET", "esock_getopt_otp_protocol {%d} -> done closed\r\n",
11234                 descP->sock) );
11235         return esock_make_error(env, atom_closed);
11236     }
11237 
11238     protocol = MKI(env, descP->protocol);
11239     result = esock_make_ok2(env, protocol);
11240 
11241     SSDBG( descP,
11242            ("SOCKET", "esock_getopt_otp_protocol {%d} ->"
11243             "\r\n   result: %T"
11244             "\r\n", descP->sock, result) );
11245 
11246     return result;
11247 }
11248 #endif // #ifndef __WIN32__
11249 
11250 
11251 /*
11252  * esock_getopt_otp_dtp - Handle the OTP (level) type options.
11253  */
11254 #ifndef __WIN32__
11255 static
11256 ERL_NIF_TERM esock_getopt_otp_dtp(ErlNifEnv*       env,
11257                                    ESockDescriptor* descP)
11258 {
11259     ERL_NIF_TERM domain, type, protocol, dtp, result;
11260 
11261     if (! IS_OPEN(descP->readState)) {
11262         SSDBG( descP,
11263                ("SOCKET", "esock_getopt_otp_dtp {%d} -> done closed\r\n",
11264                 descP->sock) );
11265         return esock_make_error(env, atom_closed);
11266     }
11267 
11268     esock_encode_domain(env, descP->domain, &domain);
11269     esock_encode_type(env, descP->type, &type);
11270     protocol = MKI(env, descP->protocol);
11271     dtp = MKT3(env, domain, type, protocol);
11272     result = esock_make_ok2(env, dtp);
11273 
11274     SSDBG( descP,
11275            ("SOCKET", "esock_getopt_otp_dtp {%d} ->"
11276             "\r\n   result: %T"
11277             "\r\n", descP->sock, result) );
11278 
11279     return result;
11280 }
11281 #endif // #ifndef __WIN32__
11282 
11283 
11284 #endif // #if 0
11285 
11286 
11287 /* How to decode the value is specified with valueSpec
11288  */
11289 #ifndef __WIN32__
11290 static
esock_getopt_native(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ERL_NIF_TERM valueSpec)11291 ERL_NIF_TERM esock_getopt_native(ErlNifEnv*       env,
11292                                  ESockDescriptor* descP,
11293                                  int              level,
11294                                  int              opt,
11295                                  ERL_NIF_TERM     valueSpec)
11296 {
11297     ERL_NIF_TERM result;
11298     SOCKOPTLEN_T valueSz;
11299     int          sz;
11300     ErlNifBinary bin;
11301 
11302     MLOCK(descP->readMtx);
11303 
11304     SSDBG( descP,
11305            ("SOCKET", "esock_getopt_native {%d} -> entry"
11306             "\r\n   level: %d"
11307             "\r\n   opt: %d"
11308             "\r\n   valueSpec: %T"
11309             "\r\n", descP->sock,
11310             level, opt, valueSpec) );
11311 
11312     if (! IS_OPEN(descP->readState)) {
11313         SSDBG( descP,
11314                ("SOCKET", "esock_getopt_native {%d} -> done closed\r\n",
11315                 descP->sock) );
11316         MUNLOCK(descP->readMtx);
11317         return esock_make_error(env, atom_closed);
11318     }
11319 
11320     /* <KOLLA>
11321      * We could make it possible to specify more types,
11322      * such as string, NUL terminated or not, etc...
11323      * </KOLLA>
11324      */
11325 
11326     if (GET_INT(env, valueSpec, &sz)) {
11327         valueSz = (SOCKOPTLEN_T) sz;
11328         if ((int) valueSz == sz) {
11329             SSDBG( descP,
11330                    ("SOCKET", "esock_getopt_native {%d} -> binary size"
11331                     "\r\n   valueSz: %d"
11332                     "\r\n", descP->sock, sz) );
11333             result =
11334                 esock_getopt_size_opt(env, descP, level, opt, valueSz);
11335         } else {
11336             result = esock_make_invalid(env, atom_value);
11337         }
11338     } else if (COMPARE(valueSpec, atom_integer) == 0) {
11339         SSDBG( descP,
11340                ("SOCKET", "esock_getopt_native {%d} -> integer"
11341                 "\r\n", descP->sock) );
11342         result = esock_getopt_int_opt(env, descP, level, opt);
11343     } else if (COMPARE(valueSpec, atom_boolean) == 0) {
11344         SSDBG( descP,
11345                ("SOCKET", "esock_getopt_native {%d} -> boolean"
11346                 "\r\n", descP->sock) );
11347         result = esock_getopt_bool_opt(env, descP, level, opt);
11348     } else if (enif_inspect_binary(env, valueSpec, &bin)) {
11349         SSDBG( descP,
11350                ("SOCKET", "esock_getopt_native {%d} -> binary"
11351                 "\r\n   size: %lu"
11352                 "\r\n", descP->sock, (unsigned long) bin.size) );
11353         result = esock_getopt_bin_opt(env, descP, level, opt, &bin);
11354     } else {
11355         result = esock_make_invalid(env, atom_value);
11356     }
11357 
11358     SSDBG( descP,
11359            ("SOCKET", "esock_getopt_native {%d} -> done when"
11360             "\r\n   result: %T"
11361             "\r\n", descP->sock, result) );
11362 
11363     MUNLOCK(descP->readMtx);
11364     return result;
11365 }
11366 #endif // #ifndef __WIN32__
11367 
11368 
11369 /* esock_getopt - An option that we know how to decode
11370  */
11371 #ifndef __WIN32__
11372 static
esock_getopt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11373 ERL_NIF_TERM esock_getopt(ErlNifEnv*       env,
11374                           ESockDescriptor* descP,
11375                           int              level,
11376                           int              opt)
11377 {
11378     ERL_NIF_TERM result;
11379     const struct ESockOpt *optP;
11380 
11381     MLOCK(descP->readMtx);
11382 
11383     SSDBG( descP,
11384            ("SOCKET", "esock_getopt {%d} -> entry with"
11385             "\r\n   level:       %d"
11386             "\r\n   opt:        %d"
11387             "\r\n", descP->sock, level, opt) );
11388 
11389     if (! IS_OPEN(descP->readState)) {
11390         SSDBG( descP,
11391                ("SOCKET", "esock_getopt {%d} -> done closed\r\n",
11392                 descP->sock) );
11393         MUNLOCK(descP->readMtx);
11394         return esock_make_error(env, atom_closed);
11395     }
11396 
11397     optP = lookupOpt(level, opt);
11398 
11399     if (optP == NULL) {
11400 
11401         result = esock_make_invalid(env, atom_socket_option);
11402 
11403         SSDBG( descP,
11404                ("SOCKET", "esock_getopt {%d} -> unknown option\r\n",
11405                 descP->sock) );
11406 
11407     } else if (optP->getopt == NULL) {
11408 
11409         result = esock_make_invalid(env, atom_socket_option);
11410 
11411         SSDBG( descP,
11412                ("SOCKET", "esock_getopt {%d} -> opt not gettable\r\n",
11413                 descP->sock) );
11414 
11415     } else {
11416 
11417         result = (optP->getopt)(env, descP, level, opt);
11418 
11419         SSDBG( descP,
11420                ("SOCKET", "esock_getopt {%d} -> done when"
11421                 "\r\n   result: %T"
11422                 "\r\n", descP->sock, result) );
11423     }
11424 
11425     MUNLOCK(descP->readMtx);
11426     return result;
11427 }
11428 #endif // #ifndef __WIN32__
11429 
11430 
11431 #ifndef __WIN32__
11432 #if defined(SO_BINDTODEVICE)
11433 static
esock_getopt_so_bindtodevice(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11434 ERL_NIF_TERM esock_getopt_so_bindtodevice(ErlNifEnv*       env,
11435                                           ESockDescriptor* descP,
11436                                           int              level,
11437                                           int              opt)
11438 {
11439     return esock_getopt_str_opt(env, descP, level, opt, IFNAMSIZ+1, FALSE);
11440 }
11441 #endif
11442 #endif // #ifndef __WIN32__
11443 
11444 
11445 #ifndef __WIN32__
11446 #if defined(SO_DOMAIN)
11447 static
esock_getopt_sock_domain(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11448 ERL_NIF_TERM esock_getopt_sock_domain(ErlNifEnv*       env,
11449                                       ESockDescriptor* descP,
11450                                       int              level,
11451                                       int              opt)
11452 {
11453     int val;
11454     ERL_NIF_TERM result;
11455 
11456     if (! esock_getopt_int(descP->sock, level, opt, &val)) {
11457         result = esock_make_error_errno(env, sock_errno());
11458     } else {
11459         ERL_NIF_TERM domain;
11460         esock_encode_domain(env, val, &domain);
11461         result = esock_make_ok2(env, domain);
11462     }
11463 
11464     return result;
11465 }
11466 #endif
11467 #endif // #ifndef __WIN32__
11468 
11469 
11470 #ifndef __WIN32__
11471 #if defined(SO_LINGER)
11472 static
esock_getopt_linger(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11473 ERL_NIF_TERM esock_getopt_linger(ErlNifEnv*       env,
11474                                  ESockDescriptor* descP,
11475                                  int              level,
11476                                  int              opt)
11477 {
11478     ERL_NIF_TERM  result;
11479     struct linger val;
11480     SOCKOPTLEN_T  valSz = sizeof(val);
11481     int           res;
11482 
11483     sys_memzero((void *) &val, sizeof(val));
11484 
11485     res = sock_getopt(descP->sock, level, opt, &val, &valSz);
11486 
11487     if (res != 0) {
11488         result = esock_make_error_errno(env, sock_errno());
11489     } else {
11490         ERL_NIF_TERM
11491             lOnOff = ((val.l_onoff != 0) ? atom_true : atom_false),
11492             lSecs  = MKI(env, val.l_linger),
11493             keys[] = {atom_onoff, esock_atom_linger},
11494             vals[] = {lOnOff, lSecs},
11495             linger;
11496         size_t numKeys = NUM(keys);
11497 
11498         ESOCK_ASSERT( numKeys == NUM(vals) );
11499         ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &linger) );
11500 
11501         result = esock_make_ok2(env, linger);
11502     }
11503 
11504     return result;
11505 }
11506 #endif
11507 #endif // #ifndef __WIN32__
11508 
11509 
11510 
11511 #ifndef __WIN32__
11512 #if defined(SO_TYPE)
11513 static
esock_getopt_sock_type(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11514 ERL_NIF_TERM esock_getopt_sock_type(ErlNifEnv*       env,
11515                                     ESockDescriptor* descP,
11516                                     int              level,
11517                                     int              opt)
11518 {
11519     ERL_NIF_TERM result;
11520     int          val;
11521 
11522     if (! esock_getopt_int(descP->sock, level, opt, &val)) {
11523         result = esock_make_error_errno(env, sock_errno());
11524     } else {
11525         ERL_NIF_TERM type;
11526         esock_encode_type(env, val, &type);
11527         result = esock_make_ok2(env, type);
11528     }
11529 
11530     return result;
11531 }
11532 #endif
11533 #endif // #ifndef __WIN32__
11534 
11535 
11536 #ifndef __WIN32__
11537 #if defined(SO_PROTOCOL)
11538 static
esock_getopt_sock_protocol(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11539 ERL_NIF_TERM esock_getopt_sock_protocol(ErlNifEnv*       env,
11540                                         ESockDescriptor* descP,
11541                                         int              level,
11542                                         int              opt)
11543 {
11544     ERL_NIF_TERM result;
11545     int          val;
11546 
11547     if (! esock_getopt_int(descP->sock, level, opt, &val)) {
11548         result = esock_make_error_errno(env, sock_errno());
11549     } else {
11550         ERL_NIF_TERM protocol;
11551 
11552         protocol =
11553 #ifdef AF_LOCAL
11554             /* For AF_LOCAL, the protocols table value for 0 is wrong */
11555             (val == 0) && (descP->domain == AF_LOCAL) ?
11556             esock_atom_default :
11557             /* It is correct for AF_INET and hopefully for AF_INET6,
11558              * but for future additions it is an open question
11559              */
11560 #endif
11561             MKI(env, val);
11562 
11563         result = esock_make_ok2(env, protocol);
11564     }
11565 
11566     return result;
11567 }
11568 #endif
11569 #endif // #ifndef __WIN32__
11570 
11571 
11572 #ifndef __WIN32__
11573 /* esock_getopt_ip_mtu_discover - Level IP MTU_DISCOVER option
11574  */
11575 #if defined(IP_MTU_DISCOVER)
11576 static
esock_getopt_ip_mtu_discover(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11577 ERL_NIF_TERM esock_getopt_ip_mtu_discover(ErlNifEnv*       env,
11578 					  ESockDescriptor* descP,
11579 					  int              level,
11580 					  int              opt)
11581 {
11582     ERL_NIF_TERM result;
11583     int          mtuDisc;
11584 
11585     if (! esock_getopt_int(descP->sock, level, opt, &mtuDisc)) {
11586         result = esock_make_error_errno(env, sock_errno());
11587     } else {
11588         ERL_NIF_TERM eMtuDisc;
11589         encode_ip_pmtudisc(env, mtuDisc, &eMtuDisc);
11590         result = esock_make_ok2(env, eMtuDisc);
11591     }
11592 
11593     return result;
11594 
11595 }
11596 #endif
11597 #endif // #ifndef __WIN32__
11598 
11599 
11600 /* esock_getopt_multicast_if - Level IP MULTICAST_IF option
11601  */
11602 #ifndef __WIN32__
11603 #if defined(IP_MULTICAST_IF)
11604 static
esock_getopt_multicast_if(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11605 ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv*       env,
11606                                        ESockDescriptor* descP,
11607                                        int              level,
11608                                        int              opt)
11609 {
11610     ERL_NIF_TERM   result;
11611     ERL_NIF_TERM   eAddr;
11612     struct in_addr ifAddr;
11613     SOCKOPTLEN_T   ifAddrSz = sizeof(ifAddr);
11614     int            res;
11615 
11616     sys_memzero((void *) &ifAddr, ifAddrSz);
11617 
11618     res = sock_getopt(descP->sock, level, opt, &ifAddr, &ifAddrSz);
11619 
11620     if (res != 0) {
11621         result = esock_make_error_errno(env, sock_errno());
11622     } else {
11623         esock_encode_in_addr(env, &ifAddr, &eAddr);
11624         result = esock_make_ok2(env, eAddr);
11625     }
11626 
11627     return result;
11628 
11629 }
11630 #endif
11631 #endif // #ifndef __WIN32__
11632 
11633 
11634 /* esock_getopt_tos - Level IP TOS option
11635  */
11636 #ifndef __WIN32__
11637 #if defined(IP_TOS)
11638 static
esock_getopt_tos(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11639 ERL_NIF_TERM esock_getopt_tos(ErlNifEnv*       env,
11640                               ESockDescriptor* descP,
11641                               int              level,
11642                               int              opt)
11643 {
11644     ERL_NIF_TERM result;
11645     int          val = 0;
11646 
11647     if (! esock_getopt_int(descP->sock, level, opt, &val)) {
11648         result = esock_make_error_errno(env, sock_errno());
11649     } else {
11650         result = esock_make_ok2(env, encode_ip_tos(env, val));
11651     }
11652 
11653     return result;
11654 }
11655 #endif
11656 #endif // #ifndef __WIN32__
11657 
11658 
11659 #if defined(HAVE_IPV6)
11660 
11661 
11662 /* esock_getopt_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option
11663  */
11664 #ifndef __WIN32__
11665 #if defined(IPV6_MTU_DISCOVER)
11666 static
esock_getopt_ipv6_mtu_discover(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11667 ERL_NIF_TERM esock_getopt_ipv6_mtu_discover(ErlNifEnv*       env,
11668                                             ESockDescriptor* descP,
11669                                             int              level,
11670                                             int              opt)
11671 {
11672     ERL_NIF_TERM  result;
11673     int           mtuDisc;
11674 
11675     if (! esock_getopt_int(descP->sock, level, opt, &mtuDisc)) {
11676         result = esock_make_error_errno(env, sock_errno());
11677     } else {
11678         ERL_NIF_TERM eMtuDisc;
11679         encode_ipv6_pmtudisc(env, mtuDisc, &eMtuDisc);
11680         result = esock_make_ok2(env, eMtuDisc);
11681     }
11682 
11683     return result;
11684 
11685 }
11686 #endif
11687 #endif // #ifndef __WIN32__
11688 
11689 
11690 #endif // defined(HAVE_IPV6)
11691 
11692 
11693 
11694 /* esock_getopt_tcp_congestion - Level TCP CONGESTION option
11695  */
11696 #ifndef __WIN32__
11697 #if defined(TCP_CONGESTION)
11698 static
esock_getopt_tcp_congestion(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11699 ERL_NIF_TERM esock_getopt_tcp_congestion(ErlNifEnv*       env,
11700                                          ESockDescriptor* descP,
11701                                          int              level,
11702                                          int              opt)
11703 {
11704     int max = ESOCK_OPT_TCP_CONGESTION_NAME_MAX+1;
11705 
11706     return esock_getopt_str_opt(env, descP, level, opt, max, TRUE);
11707 }
11708 #endif
11709 #endif // #ifndef __WIN32__
11710 
11711 
11712 
11713 #if defined(HAVE_SCTP)
11714 
11715 
11716 
11717 /* esock_getopt_sctp_associnfo - Level SCTP ASSOCINFO option
11718  *
11719  * <KOLLA>
11720  *
11721  * We should really specify which association this relates to,
11722  * as it is now we get assoc-id = 0. If this socket is an
11723  * association (and not an endpoint) then it will have an
11724  * assoc id. But since the sctp support at present is "limited",
11725  * we leave it for now.
11726  * What do we do if this is an endpoint? Invalid op? Or just leave
11727  * it for the OS?
11728  *
11729  * </KOLLA>
11730  */
11731 #ifndef __WIN32__
11732 #if defined(SCTP_ASSOCINFO)
11733 static
esock_getopt_sctp_associnfo(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11734 ERL_NIF_TERM esock_getopt_sctp_associnfo(ErlNifEnv*       env,
11735                                          ESockDescriptor* descP,
11736                                          int              level,
11737                                          int              opt)
11738 {
11739     ERL_NIF_TERM            result;
11740     struct sctp_assocparams val;
11741     SOCKOPTLEN_T            valSz = sizeof(val);
11742     int                     res;
11743 
11744     sys_memzero((char*) &val, valSz);
11745     res = sock_getopt(descP->sock, level, opt, &val, &valSz);
11746 
11747     if (res != 0) {
11748         result =  esock_make_error_errno(env, sock_errno());
11749     } else {
11750         ERL_NIF_TERM eAssocParams;
11751         ERL_NIF_TERM keys[]  = {atom_assoc_id,
11752                                 atom_asocmaxrxt,
11753                                 atom_number_peer_destinations,
11754                                 atom_peer_rwnd,
11755                                 atom_local_rwnd,
11756                                 atom_cookie_life};
11757         ERL_NIF_TERM vals[]  = {encode_sctp_assoc_t(env, val.sasoc_assoc_id),
11758                                 MKUI(env, val.sasoc_asocmaxrxt),
11759                                 MKUI(env, val.sasoc_number_peer_destinations),
11760                                 MKUI(env, val.sasoc_peer_rwnd),
11761                                 MKUI(env, val.sasoc_local_rwnd),
11762                                 MKUI(env, val.sasoc_cookie_life)};
11763         size_t numKeys        = NUM(keys);
11764 
11765         ESOCK_ASSERT( numKeys == NUM(vals) );
11766         ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &eAssocParams) );
11767         result = esock_make_ok2(env, eAssocParams);
11768     }
11769 
11770     return result;
11771 }
11772 #endif
11773 #endif // #ifndef __WIN32__
11774 
11775 
11776 
11777 
11778 /* esock_getopt_sctp_initmsg - Level SCTP INITMSG option
11779  *
11780  */
11781 #ifndef __WIN32__
11782 #if defined(SCTP_INITMSG)
11783 static
esock_getopt_sctp_initmsg(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11784 ERL_NIF_TERM esock_getopt_sctp_initmsg(ErlNifEnv*       env,
11785                                        ESockDescriptor* descP,
11786                                        int              level,
11787                                        int              opt)
11788 {
11789     ERL_NIF_TERM        result;
11790     struct sctp_initmsg val;
11791     SOCKOPTLEN_T        valSz = sizeof(val);
11792     int                 res;
11793 
11794     sys_memzero((char*) &val, valSz);
11795     res = sock_getopt(descP->sock, level, opt, &val, &valSz);
11796 
11797     if (res != 0) {
11798         result = esock_make_error_errno(env, sock_errno());
11799     } else {
11800         ERL_NIF_TERM eInitMsg;
11801         ERL_NIF_TERM keys[]  = {atom_num_outstreams, atom_max_instreams,
11802                                 atom_max_attempts, atom_max_init_timeo};
11803         ERL_NIF_TERM vals[]  = {MKUI(env, val.sinit_num_ostreams),
11804                                 MKUI(env, val.sinit_max_instreams),
11805                                 MKUI(env, val.sinit_max_attempts),
11806                                 MKUI(env, val.sinit_max_init_timeo)};
11807         unsigned int numKeys = NUM(keys);
11808         unsigned int numVals = NUM(vals);
11809 
11810         ESOCK_ASSERT( numKeys == numVals );
11811         ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &eInitMsg) );
11812         result = esock_make_ok2(env, eInitMsg);
11813     }
11814 
11815     return result;
11816 }
11817 #endif
11818 #endif // #ifndef __WIN32__
11819 
11820 
11821 /* esock_getopt_sctp_rtoinfo - Level SCTP ASSOCINFO option
11822  *
11823  * <KOLLA>
11824  *
11825  * We should really specify which association this relates to,
11826  * as it is now we get assoc-id = 0. If this socket is an
11827  * association (and not an endpoint) then it will have an
11828  * assoc id (we can assume). But since the sctp support at
11829  * present is "limited", we leave it for now.
11830  * What do we do if this is an endpoint? Invalid op?
11831  *
11832  * </KOLLA>
11833  */
11834 #ifndef __WIN32__
11835 #if defined(SCTP_RTOINFO)
11836 static
esock_getopt_sctp_rtoinfo(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11837 ERL_NIF_TERM esock_getopt_sctp_rtoinfo(ErlNifEnv*       env,
11838                                        ESockDescriptor* descP,
11839                                        int              level,
11840                                        int              opt)
11841 {
11842     ERL_NIF_TERM        result;
11843     struct sctp_rtoinfo val;
11844     SOCKOPTLEN_T        valSz = sizeof(val);
11845     int                 res;
11846 
11847     sys_memzero((char*) &val, valSz);
11848     res = sock_getopt(descP->sock, level, opt, &val, &valSz);
11849 
11850     if (res != 0) {
11851         result = esock_make_error_errno(env, sock_errno());
11852     } else {
11853         ERL_NIF_TERM eRTOInfo;
11854         ERL_NIF_TERM keys[]  = {atom_assoc_id, atom_initial, atom_max, atom_min};
11855         ERL_NIF_TERM vals[]  = {encode_sctp_assoc_t(env, val.srto_assoc_id),
11856                                 MKUI(env, val.srto_initial),
11857                                 MKUI(env, val.srto_max),
11858                                 MKUI(env, val.srto_min)};
11859         unsigned int numKeys = NUM(keys);
11860         unsigned int numVals = NUM(vals);
11861 
11862         ESOCK_ASSERT( numKeys == numVals );
11863         ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &eRTOInfo) );
11864         result = esock_make_ok2(env, eRTOInfo);
11865     }
11866 
11867     return result;
11868 }
11869 #endif
11870 #endif // #ifndef __WIN32__
11871 
11872 
11873 
11874 #endif // defined(HAVE_SCTP)
11875 
11876 
11877 
11878 /* esock_getopt_bool_opt - get an (integer) bool option
11879  */
11880 #ifndef __WIN32__
11881 static
esock_getopt_bool_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11882 ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv*       env,
11883                                    ESockDescriptor* descP,
11884                                    int              level,
11885                                    int              opt)
11886 {
11887     ERL_NIF_TERM result;
11888     int          val = 0;
11889 
11890     if (! esock_getopt_int(descP->sock, level, opt, &val)) {
11891         result = esock_make_error_errno(env, sock_errno());
11892     } else {
11893         ERL_NIF_TERM bval = ((val) ? atom_true : atom_false);
11894 
11895         result = esock_make_ok2(env, bval);
11896     }
11897     return result;
11898 }
11899 #endif // #ifndef __WIN32__
11900 
11901 
11902 /* esock_getopt_int_opt - get an integer option
11903  */
11904 #ifndef __WIN32__
11905 static
esock_getopt_int_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)11906 ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv*       env,
11907                                   ESockDescriptor* descP,
11908                                   int              level,
11909                                   int              opt)
11910 {
11911     int val;
11912 
11913     if (! esock_getopt_int(descP->sock, level, opt, &val))
11914         return esock_make_error_errno(env, sock_errno());
11915 
11916     return esock_make_ok2(env, MKI(env, val));
11917 }
11918 #endif // #ifndef __WIN32__
11919 
11920 
11921 
11922 /* esock_getopt_int - get an integer option
11923  */
11924 #ifndef __WIN32__
11925 static
esock_getopt_int(SOCKET sock,int level,int opt,int * valP)11926 BOOLEAN_T esock_getopt_int(SOCKET           sock,
11927                            int              level,
11928                            int              opt,
11929                            int*             valP)
11930 {
11931     int          val = 0;
11932     SOCKOPTLEN_T valSz = sizeof(val);
11933 
11934     if (sock_getopt(sock, level, opt, &val, &valSz) != 0)
11935         return FALSE;
11936 
11937     *valP = val;
11938     return TRUE;
11939 }
11940 #endif // #ifndef __WIN32__
11941 
11942 
11943 
11944 #ifndef __WIN32__
11945 static
esock_getopt_size_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,SOCKOPTLEN_T valueSz)11946 ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv*       env,
11947                                    ESockDescriptor* descP,
11948                                    int              level,
11949                                    int              opt,
11950                                    SOCKOPTLEN_T     valueSz)
11951 {
11952     ERL_NIF_TERM result;
11953     int          res;
11954 
11955     if (valueSz == 0) {
11956         res = sock_getopt(descP->sock, level, opt, NULL, NULL);
11957         if (res != 0)
11958             result = esock_make_error_errno(env, sock_errno());
11959         else
11960             result = esock_atom_ok;
11961     } else {
11962         SOCKOPTLEN_T vsz = valueSz;
11963         ErlNifBinary val;
11964 
11965         ESOCK_ASSERT( ALLOC_BIN(vsz, &val) );
11966         sys_memzero(val.data, val.size);
11967         res = sock_getopt(descP->sock, level, opt, val.data, &vsz);
11968         if (res != 0) {
11969             result = esock_make_error_errno(env, sock_errno());
11970             FREE_BIN(&val);
11971         } else {
11972 
11973             /* Did we use all of the buffer? */
11974             if (vsz == val.size) {
11975                 result = esock_make_ok2(env, MKBIN(env, &val));
11976 
11977             } else {
11978 
11979                 ERL_NIF_TERM tmp;
11980 
11981                 tmp = MKBIN(env, &val);
11982                 tmp = MKSBIN(env, tmp, 0, vsz);
11983 
11984                 result = esock_make_ok2(env, tmp);
11985             }
11986         }
11987     }
11988 
11989     return result;
11990 }
11991 #endif // #ifndef __WIN32__
11992 
11993 
11994 #ifndef __WIN32__
11995 static
esock_getopt_bin_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,ErlNifBinary * binP)11996 ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv*       env,
11997                                    ESockDescriptor* descP,
11998                                    int              level,
11999                                    int              opt,
12000                                    ErlNifBinary*    binP)
12001 {
12002     ERL_NIF_TERM result;
12003     int          res;
12004     SOCKOPTLEN_T vsz;
12005     ErlNifBinary val;
12006 
12007     vsz = (SOCKOPTLEN_T) binP->size;
12008     if (SZT(vsz) != binP->size) {
12009         result = esock_make_error_invalid(env, atom_data_size);
12010     } else {
12011         ESOCK_ASSERT( ALLOC_BIN(vsz, &val) );
12012         sys_memcpy(val.data, binP->data, vsz);
12013         res = sock_getopt(descP->sock, level, opt, val.data, &vsz);
12014         if (res != 0) {
12015             result = esock_make_error_errno(env, sock_errno());
12016             FREE_BIN(&val);
12017         } else {
12018 
12019             /* Did we use all of the buffer? */
12020             if (vsz == val.size) {
12021                 result = esock_make_ok2(env, MKBIN(env, &val));
12022 
12023             } else {
12024 
12025                 ERL_NIF_TERM tmp;
12026 
12027                 tmp = MKBIN(env, &val);
12028                 tmp = MKSBIN(env, tmp, 0, vsz);
12029 
12030                 result = esock_make_ok2(env, tmp);
12031             }
12032         }
12033     }
12034 
12035     return result;
12036 }
12037 #endif // #ifndef __WIN32__
12038 
12039 
12040 /* esock_getopt_timeval_opt - get an timeval option
12041  */
12042 #ifndef __WIN32__
12043 #if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \
12044     && defined(ESOCK_USE_RCVSNDTIMEO)
12045 static
esock_getopt_timeval_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)12046 ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv*       env,
12047                                       ESockDescriptor* descP,
12048                                       int              level,
12049                                       int              opt)
12050 {
12051     ERL_NIF_TERM   result;
12052     struct timeval val;
12053     SOCKOPTLEN_T   valSz = sizeof(val);
12054     int            res;
12055 
12056     sys_memzero((char*) &val, valSz);
12057     res = sock_getopt(descP->sock, level, opt, &val, &valSz);
12058 
12059     if (res != 0) {
12060         result = esock_make_error_errno(env, sock_errno());
12061     } else {
12062         ERL_NIF_TERM eTimeVal;
12063 
12064         esock_encode_timeval(env, &val, &eTimeVal);
12065         result = esock_make_ok2(env, eTimeVal);
12066     }
12067 
12068     return result;
12069 }
12070 #endif
12071 #endif // #ifndef __WIN32__
12072 
12073 
12074 #ifndef __WIN32__
12075 #if defined(IP_PKTOPTIONS) || defined(IPV6_PKTOPTIONS)
12076 
12077 /* Calculate CMSG_NXTHDR without having a struct msghdr*.
12078  * CMSG_LEN only caters for alignment for start of data.
12079  * To get how much to advance we need to use CMSG_SPACE
12080  * on the payload length.  To get the payload length we
12081  * take the calculated cmsg->cmsg_len and subtract the
12082  * header length.  To get the header length we use
12083  * the pointer difference from the cmsg start pointer
12084  * to the CMSG_DATA(cmsg) pointer.
12085  *
12086  * Some platforms (seen on ppc Linux 2.6.29-3.ydl61.3)
12087  * may return 0 as the cmsg_len if the cmsg is to be ignored.
12088  */
12089 #define LEN_CMSG_DATA(__CMSG__)                                             \
12090     ((__CMSG__)->cmsg_len < sizeof (struct cmsghdr) ? 0 :                   \
12091      (__CMSG__)->cmsg_len - ((char*)CMSG_DATA(__CMSG__) - (char*)(__CMSG__)))
12092 #define NEXT_CMSG_HDR(__CMSG__)                                              \
12093     ((struct cmsghdr*)(((char*)(__CMSG__)) + CMSG_SPACE(LEN_CMSG_DATA(__CMSG__))))
12094 
12095 static
esock_getopt_pktoptions(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt)12096 ERL_NIF_TERM esock_getopt_pktoptions(ErlNifEnv*       env,
12097 				     ESockDescriptor* descP,
12098 				     int              level,
12099 				     int              opt)
12100 {
12101   ERL_NIF_TERM result, ePktOpts;
12102   int          res;
12103   ErlNifBinary cmsgs;
12104   SOCKOPTLEN_T sz       = (SOCKOPTLEN_T) descP->rCtrlSz;
12105   SocketTArray cmsghdrs = TARRAY_CREATE(16);
12106   ERL_NIF_TERM ctrlBuf;
12107 
12108   ESOCK_ASSERT( ALLOC_BIN(sz, &cmsgs) );
12109 
12110   sys_memzero(cmsgs.data, cmsgs.size);
12111   sz  = cmsgs.size; // Make no assumption about the size
12112   res = sock_getopt(descP->sock, level, opt, cmsgs.data, &sz);
12113 
12114   if (res != 0) {
12115     result = esock_make_error_errno(env, sock_errno());
12116   } else {
12117     struct cmsghdr* currentP;
12118     struct cmsghdr* endOfBuf;
12119 
12120     ctrlBuf = MKBIN(env, &cmsgs); // The *entire* binary
12121 
12122     for (endOfBuf = (struct cmsghdr*)(cmsgs.data + cmsgs.size),
12123 	 currentP = (struct cmsghdr*)(cmsgs.data);
12124 	 (currentP != NULL) && (currentP < endOfBuf);
12125 	 currentP = NEXT_CMSG_HDR(currentP)) {
12126       unsigned char* dataP   = UCHARP(CMSG_DATA(currentP));
12127       size_t         dataPos = dataP - cmsgs.data;
12128       size_t         dataLen = (UCHARP(currentP) + currentP->cmsg_len) - dataP;
12129 
12130       SSDBG( descP,
12131 	     ("SOCKET", "esock_getopt_pktoptions {%d} -> cmsg header data: "
12132 	      "\r\n   currentP: 0x%lX"
12133 	      "\r\n         level: %d"
12134 	      "\r\n         data:  %d"
12135 	      "\r\n         len:   %d [0x%lX]"
12136 	      "\r\n   dataP:    0x%lX"
12137 	      "\r\n   dataPos:  %d"
12138 	      "\r\n   dataLen:  %d [0x%lX]"
12139 	      "\r\n", descP->sock,
12140 	      currentP,
12141 	      currentP->cmsg_level,
12142 	      currentP->cmsg_type,
12143 	      currentP->cmsg_len, currentP->cmsg_len,
12144 	      dataP,
12145 	      dataPos,
12146 	      dataLen, dataLen) );
12147 
12148       /*
12149        * Check that this is within the allocated buffer...
12150        * The 'next control message header' is a bit adhoc,
12151        * so this check is a bit...
12152        */
12153       if ((dataPos > cmsgs.size) ||
12154 	  (dataLen > cmsgs.size) ||
12155 	  ((dataPos + dataLen) > ((size_t)endOfBuf))) {
12156 	break;
12157       } else {
12158 	ERL_NIF_TERM cmsgHdr;
12159 	ERL_NIF_TERM keys[] = {esock_atom_level,
12160 			       esock_atom_type,
12161 			       esock_atom_data,
12162 			       atom_value};
12163 	ERL_NIF_TERM vals[NUM(keys)];
12164 	size_t       numKeys = NUM(keys);
12165 	BOOLEAN_T    haveValue;
12166 
12167 	vals[0] = esock_encode_level(env, currentP->cmsg_level);
12168 	vals[2] = MKSBIN(env, ctrlBuf, dataPos, dataLen);
12169 
12170 	haveValue = encode_cmsg(env,
12171 				currentP->cmsg_level,
12172 				currentP->cmsg_type,
12173 				dataP, dataLen, &vals[1], &vals[3]);
12174 
12175 	SSDBG( descP,
12176 	       ("SOCKET", "esock_getopt_pktoptions {%d} -> "
12177 		"\r\n   %T: %T"
12178 		"\r\n   %T: %T"
12179 		"\r\n   %T: %T"
12180 		"\r\n", descP->sock,
12181 		keys[0], vals[0], keys[1], vals[1], keys[2], vals[2]) );
12182 
12183 	if (haveValue) {
12184 	  SSDBG( descP,
12185 		 ("SOCKET", "esock_getopt_pktoptions {%d} -> "
12186 		  "\r\n   %T: %T"
12187 		  "\r\n", descP->sock, keys[3], vals[3]) );
12188 	}
12189 
12190 	/* Guard agains cut-and-paste errors */
12191 	ESOCK_ASSERT( numKeys == NUM(vals) );
12192 
12193 	/* Make control message header map */
12194 	ESOCK_ASSERT( MKMA(env, keys, vals,
12195 			   numKeys - (haveValue ? 0 : 1), &cmsgHdr) );
12196 
12197 	SSDBG( descP,
12198 	       ("SOCKET", "esock_getopt_pktoptions {%d} -> header processed: "
12199 		"\r\n   %T"
12200 		"\r\n", descP->sock, cmsgHdr) );
12201 
12202 	/* And finally add it to the list... */
12203 	TARRAY_ADD(cmsghdrs, cmsgHdr);
12204       }
12205     }
12206 
12207     SSDBG( descP,
12208            ("SOCKET", "esock_getopt_pktoptions {%d} -> "
12209 	    "cmsg headers processed when"
12210             "\r\n   TArray Size: %d"
12211             "\r\n", descP->sock, TARRAY_SZ(cmsghdrs)) );
12212 
12213     /* The tarray is populated - convert it to a list */
12214     TARRAY_TOLIST(cmsghdrs, env, &ePktOpts);
12215 
12216     result = esock_make_ok2(env, ePktOpts);
12217   }
12218 
12219   FREE_BIN(&cmsgs);
12220 
12221   return result;
12222 }
12223 #endif
12224 #endif // #ifndef __WIN32__
12225 
12226 
12227 
12228 
12229 /* esock_getopt_str_opt - get a string option
12230  *
12231  * We provide the max size of the string. This is the
12232  * size of the buffer we allocate for the value.
12233  * The actual size of the (read) value will be communicated
12234  * in the valSz variable.
12235  */
12236 #ifndef __WIN32__
12237 #if defined(USE_GETOPT_STR_OPT)
12238 static
esock_getopt_str_opt(ErlNifEnv * env,ESockDescriptor * descP,int level,int opt,int max,BOOLEAN_T stripNUL)12239 ERL_NIF_TERM esock_getopt_str_opt(ErlNifEnv*       env,
12240                                   ESockDescriptor* descP,
12241                                   int              level,
12242                                   int              opt,
12243                                   int              max,
12244                                   BOOLEAN_T        stripNUL)
12245 {
12246     ERL_NIF_TERM result;
12247     char*        val   = MALLOC(max);
12248     SOCKOPTLEN_T valSz = max;
12249     int          res;
12250 
12251     ESOCK_ASSERT( val != NULL );
12252 
12253     sys_memzero(val, max);
12254     res = sock_getopt(descP->sock, level, opt, val, &valSz);
12255 
12256     if (res != 0) {
12257         result = esock_make_error_errno(env, sock_errno());
12258     } else {
12259         if (stripNUL &&
12260             valSz > 0 &&
12261             val[valSz - 1] == '\0') valSz--;
12262 
12263         result = esock_make_ok2(env, MKSL(env, val, valSz));
12264     }
12265     FREE(val);
12266 
12267     return result;
12268 }
12269 #endif // if defined(USE_GETOPT_STR_OPT)
12270 #endif // #ifndef __WIN32__
12271 
12272 
12273 
12274 /* ----------------------------------------------------------------------
12275  * nif_sockname - get socket name
12276  *
12277  * Description:
12278  * Returns the current address to which the socket is bound.
12279  *
12280  * Arguments:
12281  * Socket (ref) - Points to the socket descriptor.
12282  */
12283 
12284 static
nif_sockname(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])12285 ERL_NIF_TERM nif_sockname(ErlNifEnv*         env,
12286                           int                argc,
12287                           const ERL_NIF_TERM argv[])
12288 {
12289 #ifdef __WIN32__
12290     return enif_raise_exception(env, MKA(env, "notsup"));
12291 #else
12292     ESockDescriptor* descP;
12293     ERL_NIF_TERM     res;
12294 
12295     ESOCK_ASSERT( argc == 1 );
12296 
12297     SGDBG( ("SOCKET", "nif_sockname -> entry with argc: %d\r\n", argc) );
12298 
12299     /* Extract arguments and perform preliminary validation */
12300 
12301     if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
12302         return enif_make_badarg(env);
12303     }
12304 
12305     MLOCK(descP->readMtx);
12306 
12307     SSDBG( descP,
12308            ("SOCKET", "nif_sockname(%T) {%d}"
12309             "\r\n", argv[0], descP->sock) );
12310 
12311     res = esock_sockname(env, descP);
12312 
12313     SSDBG( descP,
12314            ("SOCKET", "nif_sockname(%T) {%d} -> done with res = %T\r\n",
12315             argv[0], descP->sock, res) );
12316 
12317     MUNLOCK(descP->readMtx);
12318 
12319     return res;
12320 #endif // #ifdef __WIN32__  #else
12321 }
12322 
12323 
12324 
12325 #ifndef __WIN32__
12326 static
esock_sockname(ErlNifEnv * env,ESockDescriptor * descP)12327 ERL_NIF_TERM esock_sockname(ErlNifEnv*       env,
12328                             ESockDescriptor* descP)
12329 {
12330     ESockAddress  sa;
12331     ESockAddress* saP = &sa;
12332     SOCKLEN_T     sz  = sizeof(ESockAddress);
12333 
12334     if (! IS_OPEN(descP->readState))
12335         return esock_make_error(env, atom_closed);
12336 
12337     sys_memzero((char*) saP, sz);
12338     if (sock_name(descP->sock, (struct sockaddr*) saP, &sz) < 0) {
12339         return esock_make_error_errno(env, sock_errno());
12340     } else {
12341         ERL_NIF_TERM esa;
12342 
12343         esock_encode_sockaddr(env, saP, sz, &esa);
12344         return esock_make_ok2(env, esa);
12345     }
12346 }
12347 #endif // #ifndef __WIN32__
12348 
12349 
12350 
12351 
12352 /* ----------------------------------------------------------------------
12353  * nif_peername - get name of the connected peer socket
12354  *
12355  * Description:
12356  * Returns the address of the peer connected to the socket.
12357  *
12358  * Arguments:
12359  * Socket (ref) - Points to the socket descriptor.
12360  */
12361 
12362 static
nif_peername(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])12363 ERL_NIF_TERM nif_peername(ErlNifEnv*         env,
12364                           int                argc,
12365                           const ERL_NIF_TERM argv[])
12366 {
12367 #ifdef __WIN32__
12368     return enif_raise_exception(env, MKA(env, "notsup"));
12369 #else
12370     ESockDescriptor* descP;
12371     ERL_NIF_TERM     res;
12372 
12373     ESOCK_ASSERT( argc == 1 );
12374 
12375     SGDBG( ("SOCKET", "nif_peername -> entry with argc: %d\r\n", argc) );
12376 
12377     /* Extract arguments and perform preliminary validation */
12378 
12379     if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) {
12380         return enif_make_badarg(env);
12381     }
12382 
12383     MLOCK(descP->readMtx);
12384 
12385     SSDBG( descP,
12386            ("SOCKET", "nif_peername(%T) {%d}"
12387             "\r\n", argv[0], descP->sock) );
12388 
12389     res = esock_peername(env, descP);
12390 
12391     SSDBG( descP,
12392            ("SOCKET", "nif_peername(%T) {%d} -> done with res = %T\r\n",
12393             argv[0], descP->sock, res) );
12394 
12395     MUNLOCK(descP->readMtx);
12396 
12397     return res;
12398 #endif // #ifdef __WIN32__  #else
12399 }
12400 
12401 
12402 
12403 #ifndef __WIN32__
12404 static
esock_peername(ErlNifEnv * env,ESockDescriptor * descP)12405 ERL_NIF_TERM esock_peername(ErlNifEnv*       env,
12406                             ESockDescriptor* descP)
12407 {
12408     ESockAddress  sa;
12409     ESockAddress* saP = &sa;
12410     SOCKLEN_T     sz  = sizeof(ESockAddress);
12411 
12412     if (! IS_OPEN(descP->readState))
12413         return esock_make_error(env, atom_closed);
12414 
12415     sys_memzero((char*) saP, sz);
12416     if (sock_peer(descP->sock, (struct sockaddr*) saP, &sz) < 0) {
12417         return esock_make_error_errno(env, sock_errno());
12418     } else {
12419         ERL_NIF_TERM esa;
12420 
12421         esock_encode_sockaddr(env, saP, sz, &esa);
12422         return esock_make_ok2(env, esa);
12423     }
12424 }
12425 #endif // #ifndef __WIN32__
12426 
12427 
12428 
12429 /* ----------------------------------------------------------------------
12430  * nif_cancel
12431  *
12432  * Description:
12433  * Cancel a previous select!
12434  *
12435  * Arguments:
12436  * Socket    (ref)  - Points to the socket descriptor.
12437  * Operation (atom) - What kind of operation (accept, send, ...) is to be cancelled
12438  * Ref       (ref)  - Unique id for the operation
12439  */
12440 static
nif_cancel(ErlNifEnv * env,int argc,const ERL_NIF_TERM argv[])12441 ERL_NIF_TERM nif_cancel(ErlNifEnv*         env,
12442                         int                argc,
12443                         const ERL_NIF_TERM argv[])
12444 {
12445 #ifdef __WIN32__
12446     return enif_raise_exception(env, MKA(env, "notsup"));
12447 #else
12448     ESockDescriptor* descP;
12449     ERL_NIF_TERM     op, sockRef, opRef;
12450 
12451     ESOCK_ASSERT( argc == 3 );
12452 
12453     SGDBG( ("SOCKET", "nif_cancel -> entry with argc: %d\r\n", argc) );
12454 
12455     /* Extract arguments and perform preliminary validation */
12456 
12457     sockRef = argv[0];
12458     if (! ESOCK_GET_RESOURCE(env, sockRef, (void**) &descP)) {
12459         return enif_make_badarg(env);
12460     }
12461     op    = argv[1];
12462     opRef = argv[2];
12463     if ((! IS_ATOM(env, op)) ||
12464         (! enif_is_ref(env, opRef))) {
12465         return enif_make_badarg(env);
12466     }
12467 
12468     return esock_cancel(env, descP, op, sockRef, opRef);
12469 
12470 #endif // #ifdef __WIN32__  #else
12471 }
12472 
12473 
12474 #ifndef __WIN32__
12475 static
esock_cancel(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM op,ERL_NIF_TERM sockRef,ERL_NIF_TERM opRef)12476 ERL_NIF_TERM esock_cancel(ErlNifEnv*       env,
12477                           ESockDescriptor* descP,
12478                           ERL_NIF_TERM     op,
12479                           ERL_NIF_TERM     sockRef,
12480                           ERL_NIF_TERM     opRef)
12481 {
12482     int cmp;
12483 
12484     /* <KOLLA>
12485      *
12486      * Do we really need all these variants? Should it not be enough with:
12487      *
12488      *     connect | accept | send | recv
12489      *
12490      * </KOLLA>
12491      */
12492 
12493     /* Hand crafted binary search */
12494     if ((cmp = COMPARE(op, esock_atom_recvmsg)) == 0)
12495         return esock_cancel_recv(env, descP, sockRef, opRef);
12496     if (cmp < 0) {
12497         if ((cmp = COMPARE(op, esock_atom_recv)) == 0)
12498             return esock_cancel_recv(env, descP, sockRef, opRef);
12499         if (cmp < 0) {
12500             if (COMPARE(op, esock_atom_connect) == 0)
12501                 return esock_cancel_connect(env, descP, opRef);
12502             if (COMPARE(op, esock_atom_accept) == 0)
12503                 return esock_cancel_accept(env, descP, sockRef, opRef);
12504         } else {
12505             if (COMPARE(op, esock_atom_recvfrom) == 0)
12506                 return esock_cancel_recv(env, descP, sockRef, opRef);
12507         }
12508     } else {
12509         if ((cmp = COMPARE(op, esock_atom_sendmsg)) == 0)
12510             return esock_cancel_send(env, descP, sockRef, opRef);
12511         if (cmp < 0) {
12512             if (COMPARE(op, esock_atom_send) == 0)
12513                 return esock_cancel_send(env, descP, sockRef, opRef);
12514             if (COMPARE(op, atom_sendfile) == 0)
12515                 return esock_cancel_send(env, descP, sockRef, opRef);
12516         } else {
12517             if (COMPARE(op, esock_atom_sendto) == 0)
12518                 return esock_cancel_send(env, descP, sockRef, opRef);
12519         }
12520     }
12521 
12522     {
12523         ERL_NIF_TERM result;
12524         const char *reason;
12525 
12526         MLOCK(descP->readMtx);
12527         MLOCK(descP->writeMtx);
12528 
12529         if (! IS_OPEN(descP->readState)) {
12530             result = esock_make_error(env, atom_closed);
12531             reason = "closed";
12532         } else {
12533             result = enif_make_badarg(env);
12534             reason = "badarg";
12535         }
12536 
12537         SSDBG( descP,
12538                ("SOCKET", "esock_cancel(%T), {%d,0x%X} -> %s"
12539                 "\r\n",
12540                 sockRef,  descP->sock,
12541                 descP->readState | descP->writeState, reason) );
12542 
12543         MUNLOCK(descP->writeMtx);
12544         MUNLOCK(descP->readMtx);
12545 
12546         return result;
12547     }
12548 }
12549 #endif // #ifndef __WIN32__
12550 
12551 
12552 
12553 /* *** esock_cancel_connect ***
12554  *
12555  *
12556  */
12557 #ifndef __WIN32__
12558 static
esock_cancel_connect(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM opRef)12559 ERL_NIF_TERM esock_cancel_connect(ErlNifEnv*       env,
12560                                   ESockDescriptor* descP,
12561                                   ERL_NIF_TERM     opRef)
12562 {
12563     ERL_NIF_TERM res;
12564     ErlNifPid self;
12565 
12566     ESOCK_ASSERT( enif_self(env, &self) != NULL );
12567 
12568     MLOCK(descP->writeMtx);
12569 
12570     if (! IS_OPEN(descP->writeState)) {
12571 
12572         res = esock_make_error(env, atom_closed);
12573 
12574     } else if ((descP->connectorP == NULL) ||
12575                (COMPARE_PIDS(&self, &descP->connector.pid) != 0) ||
12576                (COMPARE(opRef, descP->connector.ref) != 0)) {
12577 
12578         res = esock_atom_not_found;
12579 
12580     } else {
12581 
12582         res = esock_cancel_write_select(env, descP, opRef);
12583         requestor_release("esock_cancel_connect",
12584                           env, descP, &descP->connector);
12585         descP->connectorP = NULL;
12586         descP->writeState &= ~ESOCK_STATE_CONNECTING;
12587     }
12588 
12589     SSDBG( descP,
12590            ("SOCKET",
12591             "esock_cancel_connect {%d,0x%X} ->"
12592             "\r\n   opRef: %T"
12593             "\r\n   res: %T"
12594             "\r\n",
12595             descP->sock, descP->writeState,
12596             opRef, res) );
12597 
12598     MUNLOCK(descP->writeMtx);
12599 
12600     return res;
12601 }
12602 #endif // #ifndef __WIN32__
12603 
12604 
12605 /* *** esock_cancel_accept ***
12606  *
12607  * We have two different cases:
12608  *   *) Its the current acceptor
12609  *      Cancel the select!
12610  *      We need to activate one of the waiting acceptors.
12611  *   *) Its one of the acceptors ("waiting") in the queue
12612  *      Simply remove the acceptor from the queue.
12613  *
12614  */
12615 #ifndef __WIN32__
12616 static
esock_cancel_accept(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM opRef)12617 ERL_NIF_TERM esock_cancel_accept(ErlNifEnv*       env,
12618                                  ESockDescriptor* descP,
12619                                  ERL_NIF_TERM     sockRef,
12620                                  ERL_NIF_TERM     opRef)
12621 {
12622     ERL_NIF_TERM res;
12623 
12624     MLOCK(descP->readMtx);
12625 
12626     SSDBG( descP,
12627            ("SOCKET",
12628             "esock_cancel_accept(%T), {%d,0x%X} ->"
12629             "\r\n   opRef: %T"
12630             "\r\n   %s"
12631             "\r\n",
12632             sockRef,  descP->sock, descP->readState,
12633             opRef,
12634             ((descP->currentAcceptorP == NULL)
12635              ? "without acceptor" : "with acceptor")) );
12636 
12637     if (! IS_OPEN(descP->readState)) {
12638 
12639         res = esock_make_error(env, atom_closed);
12640 
12641     } else if (descP->currentAcceptorP == NULL) {
12642 
12643         res = esock_atom_not_found;
12644 
12645     } else {
12646         ErlNifPid self;
12647 
12648         ESOCK_ASSERT( enif_self(env, &self) != NULL );
12649 
12650         if (COMPARE_PIDS(&self, &descP->currentAcceptor.pid) == 0) {
12651             if (COMPARE(opRef, descP->currentAcceptor.ref) == 0)
12652                 res = esock_cancel_accept_current(env, descP, sockRef);
12653             else
12654                 res = esock_atom_not_found;
12655         } else {
12656             res = esock_cancel_accept_waiting(env, descP, opRef, &self);
12657         }
12658     }
12659 
12660     SSDBG( descP,
12661            ("SOCKET", "esock_cancel_accept(%T) -> done with result:"
12662             "\r\n   %T"
12663             "\r\n", sockRef, res) );
12664 
12665     MUNLOCK(descP->readMtx);
12666 
12667     return res;
12668 }
12669 #endif // #ifndef __WIN32__
12670 
12671 
12672 /* The current acceptor process has an ongoing select we first must
12673  * cancel. Then we must re-activate the "first" (the first
12674  * in the acceptor queue).
12675  */
12676 #ifndef __WIN32__
12677 static
esock_cancel_accept_current(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef)12678 ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv*       env,
12679                                          ESockDescriptor* descP,
12680                                          ERL_NIF_TERM     sockRef)
12681 {
12682     ERL_NIF_TERM res;
12683 
12684     ESOCK_ASSERT( DEMONP("esock_cancel_accept_current -> current acceptor",
12685                          env, descP, &descP->currentAcceptor.mon) == 0);
12686     res = esock_cancel_read_select(env, descP, descP->currentAcceptor.ref);
12687 
12688     SSDBG( descP,
12689            ("SOCKET",
12690             "esock_cancel_accept_current(%T) {%d} -> cancel res: %T"
12691             "\r\n", sockRef, descP->sock, res) );
12692 
12693     if (!activate_next_acceptor(env, descP, sockRef)) {
12694 
12695         SSDBG( descP,
12696                ("SOCKET",
12697                 "esock_cancel_accept_current(%T) {%d} -> "
12698                 "no more acceptors\r\n",
12699                 sockRef, descP->sock) );
12700 
12701         descP->readState &= ~ESOCK_STATE_ACCEPTING;
12702 
12703         descP->currentAcceptorP = NULL;
12704     }
12705 
12706     return res;
12707 }
12708 #endif // #ifndef __WIN32__
12709 
12710 
12711 /* These processes have not performed a select, so we can simply
12712  * remove them from the acceptor queue.
12713  */
12714 #ifndef __WIN32__
12715 static
esock_cancel_accept_waiting(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM opRef,const ErlNifPid * selfP)12716 ERL_NIF_TERM esock_cancel_accept_waiting(ErlNifEnv*       env,
12717                                          ESockDescriptor* descP,
12718                                          ERL_NIF_TERM     opRef,
12719                                          const ErlNifPid* selfP)
12720 {
12721     /* unqueue request from (acceptor) queue */
12722 
12723     if (acceptor_unqueue(env, descP, &opRef, selfP)) {
12724         return esock_atom_ok;
12725     } else {
12726         return esock_atom_not_found;
12727     }
12728 }
12729 #endif // #ifndef __WIN32__
12730 
12731 
12732 
12733 /* *** esock_cancel_send ***
12734  *
12735  * Cancel a send operation.
12736  * Its either the current writer or one of the waiting writers.
12737  */
12738 #ifndef __WIN32__
12739 static
esock_cancel_send(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM opRef)12740 ERL_NIF_TERM esock_cancel_send(ErlNifEnv*       env,
12741                                ESockDescriptor* descP,
12742                                ERL_NIF_TERM     sockRef,
12743                                ERL_NIF_TERM     opRef)
12744 {
12745     ERL_NIF_TERM res;
12746 
12747     MLOCK(descP->writeMtx);
12748 
12749     SSDBG( descP,
12750            ("SOCKET",
12751             "esock_cancel_send(%T), {%d,0x%X} -> entry with"
12752             "\r\n   opRef: %T"
12753             "\r\n   %s"
12754             "\r\n",
12755             sockRef,  descP->sock, descP->writeState,
12756             opRef,
12757             ((descP->currentWriterP == NULL)
12758              ? "without writer" : "with writer")) );
12759 
12760     if (! IS_OPEN(descP->writeState)) {
12761 
12762         res = esock_make_error(env, atom_closed);
12763 
12764     } else if (descP->currentWriterP == NULL) {
12765 
12766         res = esock_atom_not_found;
12767 
12768     } else {
12769         ErlNifPid self;
12770 
12771         ESOCK_ASSERT( enif_self(env, &self) != NULL );
12772 
12773         if (COMPARE_PIDS(&self, &descP->currentWriter.pid) == 0) {
12774             if (COMPARE(opRef, descP->currentWriter.ref) == 0)
12775                 res = esock_cancel_send_current(env, descP, sockRef);
12776             else
12777                 res = esock_atom_not_found;
12778         } else {
12779             res = esock_cancel_send_waiting(env, descP, opRef, &self);
12780         }
12781     }
12782 
12783     SSDBG( descP,
12784            ("SOCKET", "esock_cancel_send(%T) {%d} -> done with result:"
12785             "\r\n   %T"
12786             "\r\n", sockRef, descP->sock, res) );
12787 
12788     MUNLOCK(descP->writeMtx);
12789 
12790     return res;
12791 }
12792 #endif // #ifndef __WIN32__
12793 
12794 
12795 
12796 /* The current writer process has an ongoing select we first must
12797  * cancel. Then we must re-activate the "first" (the first
12798  * in the writer queue).
12799  */
12800 #ifndef __WIN32__
12801 static
esock_cancel_send_current(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef)12802 ERL_NIF_TERM esock_cancel_send_current(ErlNifEnv*       env,
12803                                        ESockDescriptor* descP,
12804                                        ERL_NIF_TERM     sockRef)
12805 {
12806     ERL_NIF_TERM res;
12807 
12808     ESOCK_ASSERT( DEMONP("esock_cancel_send_current -> current writer",
12809                          env, descP, &descP->currentWriter.mon) == 0);
12810     res = esock_cancel_write_select(env, descP, descP->currentWriter.ref);
12811 
12812     SSDBG( descP,
12813            ("SOCKET", "esock_cancel_send_current(%T) {%d} -> cancel res: %T"
12814             "\r\n", sockRef, descP->sock, res) );
12815 
12816     if (!activate_next_writer(env, descP, sockRef)) {
12817         SSDBG( descP,
12818                ("SOCKET",
12819                 "esock_cancel_send_current(%T) {%d} -> no more writers"
12820                 "\r\n", sockRef, descP->sock) );
12821 
12822         descP->currentWriterP = NULL;
12823     }
12824 
12825     return res;
12826 }
12827 #endif // #ifndef __WIN32__
12828 
12829 
12830 /* These processes have not performed a select, so we can simply
12831  * remove them from the writer queue.
12832  */
12833 #ifndef __WIN32__
12834 static
esock_cancel_send_waiting(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM opRef,const ErlNifPid * selfP)12835 ERL_NIF_TERM esock_cancel_send_waiting(ErlNifEnv*       env,
12836                                        ESockDescriptor* descP,
12837                                        ERL_NIF_TERM     opRef,
12838                                        const ErlNifPid* selfP)
12839 {
12840     /* unqueue request from (writer) queue */
12841 
12842     if (writer_unqueue(env, descP, &opRef, selfP)) {
12843         return esock_atom_ok;
12844     } else {
12845         return esock_atom_not_found;
12846     }
12847 }
12848 #endif // #ifndef __WIN32__
12849 
12850 
12851 
12852 /* *** esock_cancel_recv ***
12853  *
12854  * Cancel a read operation.
12855  * Its either the current reader or one of the waiting readers.
12856  */
12857 #ifndef __WIN32__
12858 static
esock_cancel_recv(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM opRef)12859 ERL_NIF_TERM esock_cancel_recv(ErlNifEnv*       env,
12860                                ESockDescriptor* descP,
12861                                ERL_NIF_TERM     sockRef,
12862                                ERL_NIF_TERM     opRef)
12863 {
12864     ERL_NIF_TERM res;
12865 
12866     MLOCK(descP->readMtx);
12867 
12868     SSDBG( descP,
12869            ("SOCKET",
12870             "esock_cancel_recv(%T), {%d,0x%X} -> entry with"
12871             "\r\n   opRef: %T"
12872             "\r\n   %s"
12873             "\r\n",
12874             sockRef,  descP->sock, descP->readState,
12875             opRef,
12876             ((descP->currentReaderP == NULL)
12877              ? "without reader" : "with reader")) );
12878 
12879     if (! IS_OPEN(descP->readState)) {
12880 
12881         res = esock_make_error(env, atom_closed);
12882 
12883     } else if (descP->currentReaderP == NULL) {
12884 
12885         res =  esock_atom_not_found;
12886 
12887     } else {
12888         ErlNifPid self;
12889 
12890         ESOCK_ASSERT( enif_self(env, &self) != NULL );
12891 
12892         if (COMPARE_PIDS(&self, &descP->currentReader.pid) == 0) {
12893             if (COMPARE(opRef, descP->currentReader.ref) == 0)
12894                 res = esock_cancel_recv_current(env, descP, sockRef);
12895             else
12896                 res =  esock_atom_not_found;
12897         } else {
12898             res = esock_cancel_recv_waiting(env, descP, opRef, &self);
12899         }
12900     }
12901 
12902     SSDBG( descP,
12903            ("SOCKET", "esock_cancel_recv(%T) {%d} -> done with result:"
12904             "\r\n   %T"
12905             "\r\n", sockRef, descP->sock, res) );
12906 
12907     MUNLOCK(descP->readMtx);
12908 
12909     return res;
12910 }
12911 #endif // #ifndef __WIN32__
12912 
12913 
12914 /* The current reader process has an ongoing select we first must
12915  * cancel. Then we must re-activate the "first" (the first
12916  * in the reader queue).
12917  */
12918 #ifndef __WIN32__
12919 static
esock_cancel_recv_current(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef)12920 ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv*       env,
12921                                        ESockDescriptor* descP,
12922                                        ERL_NIF_TERM     sockRef)
12923 {
12924     ERL_NIF_TERM res;
12925 
12926     ESOCK_ASSERT( DEMONP("esock_cancel_recv_current -> current reader",
12927                          env, descP, &descP->currentReader.mon) == 0);
12928     res = esock_cancel_read_select(env, descP, descP->currentReader.ref);
12929 
12930     SSDBG( descP,
12931            ("SOCKET", "esock_cancel_recv_current(%T) {%d} -> cancel res: %T"
12932             "\r\n", sockRef, descP->sock, res) );
12933 
12934     if (! activate_next_reader(env, descP, sockRef)) {
12935         SSDBG( descP,
12936                ("SOCKET",
12937                 "esock_cancel_recv_current(%T) {%d} -> no more readers"
12938                 "\r\n", sockRef, descP->sock) );
12939 
12940         descP->currentReaderP = NULL;
12941     }
12942 
12943     return res;
12944 }
12945 #endif // #ifndef __WIN32__
12946 
12947 
12948 /* These processes have not performed a select, so we can simply
12949  * remove them from the reader queue.
12950  */
12951 #ifndef __WIN32__
12952 static
esock_cancel_recv_waiting(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM opRef,const ErlNifPid * selfP)12953 ERL_NIF_TERM esock_cancel_recv_waiting(ErlNifEnv*       env,
12954                                        ESockDescriptor* descP,
12955                                        ERL_NIF_TERM     opRef,
12956                                        const ErlNifPid* selfP)
12957 {
12958     /* unqueue request from (reader) queue */
12959 
12960     if (reader_unqueue(env, descP, &opRef, selfP)) {
12961         return esock_atom_ok;
12962     } else {
12963         return esock_atom_not_found;
12964     }
12965 }
12966 #endif // #ifndef __WIN32__
12967 
12968 
12969 
12970 #ifndef __WIN32__
12971 static
esock_cancel_read_select(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM opRef)12972 ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv*       env,
12973                                       ESockDescriptor* descP,
12974                                       ERL_NIF_TERM     opRef)
12975 {
12976     return esock_cancel_mode_select(env, descP, opRef,
12977                                     ERL_NIF_SELECT_READ,
12978                                     ERL_NIF_SELECT_READ_CANCELLED);
12979 }
12980 #endif // #ifndef __WIN32__
12981 
12982 
12983 #ifndef __WIN32__
12984 static
esock_cancel_write_select(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM opRef)12985 ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv*       env,
12986                                   ESockDescriptor* descP,
12987                                   ERL_NIF_TERM     opRef)
12988 {
12989     return esock_cancel_mode_select(env, descP, opRef,
12990                                     ERL_NIF_SELECT_WRITE,
12991                                     ERL_NIF_SELECT_WRITE_CANCELLED);
12992 }
12993 #endif // #ifndef __WIN32__
12994 
12995 
12996 #ifndef __WIN32__
12997 static
esock_cancel_mode_select(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM opRef,int smode,int rmode)12998 ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv*       env,
12999                                       ESockDescriptor* descP,
13000                                       ERL_NIF_TERM     opRef,
13001                                       int              smode,
13002                                       int              rmode)
13003 {
13004     /* Assumes cancelling only one mode */
13005 
13006     int selectRes = esock_select_cancel(env, descP->sock, smode, descP);
13007 
13008     if (selectRes >= 0) {
13009         /* Success */
13010         if ((selectRes & rmode) != 0) {
13011             /* Was cancelled */
13012             return esock_atom_ok;
13013         } else {
13014             /* Has already sent the message */
13015             return esock_atom_select_sent;
13016         }
13017     } else {
13018         /* Stopped? */
13019         SSDBG( descP,
13020                ("SOCKET",
13021                 "esock_cancel_mode_select {%d} -> failed: %d (0x%lX)"
13022                 "\r\n", descP->sock, selectRes, selectRes) );
13023 
13024         return esock_atom_not_found;
13025     }
13026 }
13027 #endif // #ifndef __WIN32__
13028 
13029 
13030 
13031 /* ----------------------------------------------------------------------
13032  *  U t i l i t y   F u n c t i o n s
13033  * ----------------------------------------------------------------------
13034  */
13035 
13036 /* *** send_check_writer ***
13037  *
13038  * Checks if we have a current writer and if that is us.
13039  * If not (current writer), then we must be made to wait
13040  * for our turn. This is done by pushing us unto the writer queue.
13041  */
13042 #ifndef __WIN32__
13043 static
send_check_writer(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM ref,ERL_NIF_TERM * checkResult)13044 BOOLEAN_T send_check_writer(ErlNifEnv*       env,
13045                             ESockDescriptor* descP,
13046                             ERL_NIF_TERM     ref,
13047                             ERL_NIF_TERM*    checkResult)
13048 {
13049     if (descP->currentWriterP != NULL) {
13050         ErlNifPid caller;
13051 
13052         ESOCK_ASSERT( enif_self(env, &caller) != NULL );
13053 
13054         if (COMPARE_PIDS(&descP->currentWriter.pid, &caller) != 0) {
13055             /* Not the "current writer", so (maybe) push onto queue */
13056 
13057             SSDBG( descP,
13058                    ("SOCKET",
13059                     "send_check_writer {%d} -> not (current) writer"
13060                     "\r\n   ref: %T"
13061                     "\r\n", descP->sock, ref) );
13062 
13063             if (! writer_search4pid(env, descP, &caller)) {
13064                 writer_push(env, descP, caller, ref);
13065                 *checkResult = atom_select;
13066             } else {
13067                 /* Writer already in queue */
13068                 *checkResult = esock_raise_invalid(env, atom_state);
13069             }
13070 
13071             SSDBG( descP,
13072                    ("SOCKET",
13073                     "send_check_writer {%d} -> queue (push) result: %T\r\n"
13074                     "\r\n   ref: %T"
13075                     "\r\n", descP->sock, *checkResult, ref) );
13076 
13077             return FALSE;
13078         }
13079     }
13080 
13081     // Does not actually matter in this case, but ...
13082     *checkResult = esock_atom_ok;
13083 
13084     return TRUE;
13085 }
13086 #endif // #ifndef __WIN32__
13087 
13088 
13089 /* *** send_check_result ***
13090  *
13091  * Check the result of a socket send (send, sendto and sendmsg) call.
13092  * If a "complete" send has been made, the next (waiting) writer will be
13093  * scheduled (if there is one).
13094  * If we did not manage to send the entire package, make another select,
13095  * so that we can be informed when we can make another try (to send the rest),
13096  * and return with the amount we actually managed to send (its up to the caller
13097  * (that is the erlang code) to figure out hust much is left to send).
13098  * If the write fail, we give up and return with the appropriate error code.
13099  *
13100  * What about the remaining writers!!
13101  *
13102  */
13103 #ifndef __WIN32__
13104 static
send_check_result(ErlNifEnv * env,ESockDescriptor * descP,ssize_t send_result,ssize_t dataSize,BOOLEAN_T dataInTail,ERL_NIF_TERM sockRef,ERL_NIF_TERM sendRef)13105 ERL_NIF_TERM send_check_result(ErlNifEnv*       env,
13106                                ESockDescriptor* descP,
13107                                ssize_t          send_result,
13108                                ssize_t          dataSize,
13109                                BOOLEAN_T        dataInTail,
13110                                ERL_NIF_TERM     sockRef,
13111                                ERL_NIF_TERM     sendRef)
13112 {
13113     ERL_NIF_TERM res;
13114     BOOLEAN_T    send_error;
13115     int          err;
13116 
13117     send_error = ESOCK_IS_ERROR(send_result);
13118     err = send_error ? sock_errno() : 0;
13119 
13120     SSDBG( descP,
13121            ("SOCKET", "send_check_result(%T) {%d} -> entry with"
13122             "\r\n   send_result:  %ld"
13123             "\r\n   dataSize:     %ld"
13124             "\r\n   err:          %d"
13125             "\r\n   sendRef:      %T"
13126             "\r\n", sockRef, descP->sock,
13127             (long) send_result, (long) dataSize, err, sendRef) );
13128 
13129     if (send_error) {
13130         /* Some kind of send failure - check what kind */
13131         if ((err != EAGAIN) && (err != EINTR)) {
13132             res = send_check_fail(env, descP, err, sockRef);
13133         } else {
13134             /* Ok, try again later */
13135 
13136             SSDBG( descP,
13137                    ("SOCKET",
13138                     "send_check_result(%T) {%d} -> try again"
13139                     "\r\n", sockRef, descP->sock) );
13140 
13141             res = send_check_retry(env, descP, -1, sockRef, sendRef);
13142         }
13143     } else {
13144         ssize_t written = send_result;
13145         ESOCK_ASSERT( dataSize >= written );
13146 
13147         if (written < dataSize) {
13148             /* Not the entire package */
13149             SSDBG( descP,
13150                    ("SOCKET",
13151                     "send_check_result(%T) {%d} -> "
13152                     "not entire package written (%d of %d)"
13153                     "\r\n", sockRef, descP->sock,
13154                     written, dataSize) );
13155 
13156             res = send_check_retry(env, descP, written, sockRef, sendRef);
13157         } else if (dataInTail) {
13158             /* Not the entire package */
13159             SSDBG( descP,
13160                    ("SOCKET",
13161                     "send_check_result(%T) {%d} -> "
13162                     "not entire package written (%d but data in tail)"
13163                     "\r\n", sockRef, descP->sock,
13164                     written) );
13165 
13166             res =
13167                 send_check_retry(env, descP, written, sockRef,
13168                                  esock_atom_iov);
13169         } else {
13170             res = send_check_ok(env, descP, written, sockRef);
13171         }
13172     }
13173 
13174     SSDBG( descP,
13175            ("SOCKET",
13176             "send_check_result(%T) {%d} -> done:"
13177             "\r\n   res: %T"
13178             "\r\n", sockRef, descP->sock,
13179             res) );
13180 
13181     return res;
13182 }
13183 #endif // #ifndef __WIN32__
13184 
13185 
13186 /* *** send_check_ok ***
13187  *
13188  * Processing done upon successful send.
13189  */
13190 #ifndef __WIN32__
13191 static
send_check_ok(ErlNifEnv * env,ESockDescriptor * descP,ssize_t written,ERL_NIF_TERM sockRef)13192 ERL_NIF_TERM send_check_ok(ErlNifEnv*       env,
13193                            ESockDescriptor* descP,
13194                            ssize_t          written,
13195                            ERL_NIF_TERM     sockRef)
13196 {
13197     ESOCK_CNT_INC(env, descP, sockRef,
13198                   atom_write_pkg, &descP->writePkgCnt, 1);
13199     ESOCK_CNT_INC(env, descP, sockRef,
13200                   atom_write_byte, &descP->writeByteCnt, written);
13201     descP->writePkgMaxCnt += written;
13202     if (descP->writePkgMaxCnt > descP->writePkgMax)
13203         descP->writePkgMax = descP->writePkgMaxCnt;
13204     descP->writePkgMaxCnt = 0;
13205 
13206     SSDBG( descP,
13207            ("SOCKET", "send_check_ok(%T) {%d} -> "
13208             "everything written (%ld) - done\r\n",
13209             sockRef, descP->sock, written) );
13210 
13211     if (descP->currentWriterP != NULL) {
13212         ESOCK_ASSERT( DEMONP("send_check_ok -> current writer",
13213                              env, descP, &descP->currentWriter.mon) == 0);
13214     }
13215     /*
13216      * Ok, this write is done maybe activate the next (if any)
13217      */
13218     if (!activate_next_writer(env, descP, sockRef)) {
13219 
13220         SSDBG( descP,
13221                ("SOCKET", "send_check_ok(%T) {%d} -> no more writers\r\n",
13222                 sockRef, descP->sock) );
13223 
13224         descP->currentWriterP = NULL;
13225     }
13226 
13227     return esock_atom_ok;
13228 }
13229 #endif // #ifndef __WIN32__
13230 
13231 
13232 /* *** send_check_failure ***
13233  *
13234  * Processing done upon failed send.
13235  * An actual failure - we (and everyone waiting) give up.
13236  */
13237 #ifndef __WIN32__
13238 static
send_check_fail(ErlNifEnv * env,ESockDescriptor * descP,int saveErrno,ERL_NIF_TERM sockRef)13239 ERL_NIF_TERM send_check_fail(ErlNifEnv*       env,
13240                              ESockDescriptor* descP,
13241                              int              saveErrno,
13242                              ERL_NIF_TERM     sockRef)
13243 {
13244     ERL_NIF_TERM reason;
13245 
13246     ESOCK_CNT_INC(env, descP, sockRef, atom_write_fails, &descP->writeFails, 1);
13247 
13248     SSDBG( descP, ("SOCKET", "send_check_fail(%T) {%d} -> error: %d\r\n",
13249                    sockRef, descP->sock, saveErrno) );
13250 
13251     reason = MKA(env, erl_errno_id(saveErrno));
13252 
13253     if (saveErrno != EINVAL) {
13254 
13255         /*
13256          * We assume that anything other then einval (invalid input)
13257          * is basically fatal (=> all waiting sends are aborted)
13258          */
13259 
13260         if (descP->currentWriterP != NULL) {
13261 
13262             requestor_release("send_check_fail",
13263                               env, descP, &descP->currentWriter);
13264 
13265             send_error_waiting_writers(env, descP, sockRef, reason);
13266 
13267             descP->currentWriterP = NULL;
13268         }
13269     }
13270     return esock_make_error(env, reason);
13271 }
13272 #endif // #ifndef __WIN32__
13273 
13274 
13275 /* *** send_error_waiting_writers ***
13276  *
13277  * Process all waiting writers when a fatal error has occured.
13278  * All waiting writers will be "aborted", that is a
13279  * nif_abort message will be sent (with ref and reason).
13280  */
13281 #ifndef __WIN32__
13282 static
send_error_waiting_writers(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM reason)13283 void send_error_waiting_writers(ErlNifEnv*       env,
13284                                 ESockDescriptor* descP,
13285                                 ERL_NIF_TERM     sockRef,
13286                                 ERL_NIF_TERM     reason)
13287 {
13288     ESockRequestor req;
13289 
13290     req.env = NULL; /* read by writer_pop before free */
13291     while (writer_pop(env, descP, &req)) {
13292         SSDBG( descP,
13293                ("SOCKET",
13294                 "send_error_waiting_writers(%T) {%d} -> abort"
13295                 "\r\n   pid:    %T"
13296                 "\r\n   reason: %T"
13297                 "\r\n",
13298                 sockRef, descP->sock, &req.pid, reason) );
13299 
13300         esock_send_abort_msg(env, descP, sockRef, &req, reason);
13301 
13302         (void) DEMONP("send_error_waiting_writers -> pop'ed writer",
13303                       env, descP, &req.mon);
13304     }
13305 }
13306 #endif // #ifndef __WIN32__
13307 
13308 
13309 /* *** send_check_retry ***
13310  *
13311  * Processing done upon uncomplete or blocked send.
13312  *
13313  * We failed to write the *entire* packet (anything less
13314  * then size of the packet, which is 0 <= written < sizeof
13315  * packet, so schedule the rest for later.
13316  */
13317 #ifndef __WIN32__
13318 static
send_check_retry(ErlNifEnv * env,ESockDescriptor * descP,ssize_t written,ERL_NIF_TERM sockRef,ERL_NIF_TERM sendRef)13319 ERL_NIF_TERM send_check_retry(ErlNifEnv*       env,
13320                               ESockDescriptor* descP,
13321                               ssize_t          written,
13322                               ERL_NIF_TERM     sockRef,
13323                               ERL_NIF_TERM     sendRef)
13324 {
13325     int          sres;
13326     ERL_NIF_TERM res;
13327 
13328     SSDBG( descP,
13329            ("SOCKET",
13330             "send_check_retry(%T) {%d} -> %ld"
13331             "\r\n", sockRef, descP->sock, (long) written) );
13332 
13333     if (written >= 0) {
13334         descP->writePkgMaxCnt += written;
13335 
13336         if (descP->type != SOCK_STREAM) {
13337             /* Partial write for packet oriented socket
13338              * - done with packet
13339              */
13340             if (descP->writePkgMaxCnt > descP->writePkgMax)
13341                 descP->writePkgMax = descP->writePkgMaxCnt;
13342             descP->writePkgMaxCnt = 0;
13343 
13344             ESOCK_CNT_INC(env, descP, sockRef,
13345                           atom_write_pkg, &descP->writePkgCnt, 1);
13346             ESOCK_CNT_INC(env, descP, sockRef,
13347                           atom_write_byte, &descP->writeByteCnt, written);
13348 
13349             if (descP->currentWriterP != NULL) {
13350                 ESOCK_ASSERT( DEMONP("send_check_retry -> current writer",
13351                                      env, descP,
13352                                      &descP->currentWriter.mon) == 0);
13353             }
13354             /*
13355              * Ok, this write is done maybe activate the next (if any)
13356              */
13357             if (!activate_next_writer(env, descP, sockRef)) {
13358 
13359                 SSDBG( descP,
13360                        ("SOCKET",
13361                         "send_check_retry(%T) {%d} -> no more writers\r\n",
13362                         sockRef, descP->sock) );
13363 
13364                 descP->currentWriterP = NULL;
13365             }
13366 
13367             return esock_make_ok2(env, MKI64(env, written));
13368         } /* else partial write for stream socket */
13369     } /* else send would have blocked */
13370 
13371     /* Register this process as current writer */
13372 
13373     if (descP->currentWriterP == NULL) {
13374         /* Register writer as current */
13375 
13376         ESOCK_ASSERT( enif_self(env, &descP->currentWriter.pid) != NULL );
13377         ESOCK_ASSERT( MONP("send_check_retry -> current writer",
13378                            env, descP,
13379                            &descP->currentWriter.pid,
13380                            &descP->currentWriter.mon) == 0 );
13381         ESOCK_ASSERT( descP->currentWriter.env == NULL );
13382 
13383         descP->currentWriter.env = esock_alloc_env("current-writer");
13384         descP->currentWriter.ref =
13385             CP_TERM(descP->currentWriter.env, sendRef);
13386         descP->currentWriterP = &descP->currentWriter;
13387     } else {
13388         /* Overwrite current writer registration */
13389         enif_clear_env(descP->currentWriter.env);
13390         descP->currentWriter.ref = CP_TERM(descP->currentWriter.env, sendRef);
13391     }
13392 
13393     if (COMPARE(sendRef, esock_atom_iov) == 0) {
13394         ESOCK_ASSERT( written >= 0 );
13395         /* IOV iteration - do not select */
13396         return MKT2(env, esock_atom_iov, MKI64(env, written));
13397     }
13398 
13399     /* Select write for this process */
13400 
13401     sres = esock_select_write(env, descP->sock, descP, NULL, sockRef, sendRef);
13402 
13403     if (sres < 0) {
13404         ERL_NIF_TERM reason;
13405 
13406         /* Internal select error */
13407         ESOCK_ASSERT( DEMONP("send_check_retry - select error",
13408                              env, descP, &descP->currentWriter.mon) == 0);
13409 
13410         /* Fail all queued writers */
13411         reason = MKT2(env, atom_select_write, MKI(env, sres));
13412         requestor_release("send_check_retry - select error",
13413                           env, descP, &descP->currentWriter);
13414         send_error_waiting_writers(env, descP, sockRef, reason);
13415         descP->currentWriterP = NULL;
13416 
13417         res =
13418             enif_raise_exception(env,
13419                                  MKT2(env, atom_select_write,
13420                                       MKI(env, sres)));
13421 
13422     } else {
13423         ESOCK_CNT_INC(env, descP, sockRef, atom_write_waits,
13424                       &descP->writeWaits, 1);
13425 
13426         descP->writeState |= ESOCK_STATE_SELECTED;
13427 
13428         if (written >= 0) {
13429             /* Partial write success */
13430             res = MKT2(env, atom_select, MKI64(env, written));
13431         } else {
13432             /* No write - try again */
13433             res = atom_select;
13434         }
13435     }
13436 
13437     return res;
13438 }
13439 #endif // #ifndef __WIN32__
13440 
13441 
13442 /* *** recv_check_reader ***
13443  *
13444  * Checks if we have a current reader and if that is us. If not,
13445  * then we must be made to wait for our turn. This is done by pushing
13446  * us unto the reader queue.
13447  * Note that we do *not* actually initiate the currentReader structure
13448  * here, since we do not actually know yet if we need to! We do that in
13449  * the [recv|recvfrom|recvmsg]_check_result function.
13450  */
13451 #ifndef __WIN32__
13452 static
recv_check_reader(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM ref,ERL_NIF_TERM * checkResult)13453 BOOLEAN_T recv_check_reader(ErlNifEnv*       env,
13454                             ESockDescriptor* descP,
13455                             ERL_NIF_TERM     ref,
13456                             ERL_NIF_TERM*    checkResult)
13457 {
13458     if (descP->currentReaderP != NULL) {
13459         ErlNifPid caller;
13460 
13461         ESOCK_ASSERT( enif_self(env, &caller) != NULL );
13462 
13463         if (COMPARE_PIDS(&descP->currentReader.pid, &caller) != 0) {
13464             /* Not the "current reader", so (maybe) push onto queue */
13465 
13466             SSDBG( descP,
13467                    ("SOCKET",
13468                     "recv_check_reader {%d} -> not (current) reader"
13469                     "\r\n   ref: %T"
13470                     "\r\n", descP->sock, ref) );
13471 
13472             if (! reader_search4pid(env, descP, &caller)) {
13473                 if (COMPARE(ref, atom_zero) == 0)
13474                     goto done_ok;
13475                 reader_push(env, descP, caller, ref);
13476                 *checkResult = atom_select;
13477             } else {
13478                 /* Reader already in queue */
13479                 *checkResult = esock_raise_invalid(env, atom_state);
13480             }
13481 
13482             SSDBG( descP,
13483                    ("SOCKET",
13484                     "recv_check_reader {%d} -> queue (push) result: %T\r\n",
13485                     descP->sock, *checkResult) );
13486 
13487             return FALSE;
13488         }
13489     }
13490 
13491  done_ok:
13492     // Does not actually matter in this case, but ...
13493     *checkResult = esock_atom_ok;
13494     return TRUE;
13495 }
13496 #endif // #ifndef __WIN32__
13497 
13498 
13499 /* *** recv_init_current_reader ***
13500  *
13501  * Initiate (maybe) the currentReader structure of the descriptor.
13502  * Including monitoring the calling process.
13503  */
13504 #ifndef __WIN32__
13505 static
recv_init_current_reader(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM recvRef)13506 void recv_init_current_reader(ErlNifEnv*       env,
13507                               ESockDescriptor* descP,
13508                               ERL_NIF_TERM     recvRef)
13509 {
13510     if (descP->currentReaderP == NULL) {
13511 
13512         ESOCK_ASSERT( enif_self(env, &descP->currentReader.pid) != NULL );
13513 
13514         ESOCK_ASSERT( MONP("recv_init_current_reader -> current reader",
13515                            env, descP,
13516                            &descP->currentReader.pid,
13517                            &descP->currentReader.mon) == 0);
13518         ESOCK_ASSERT(!descP->currentReader.env);
13519 
13520         descP->currentReader.env = esock_alloc_env("current-reader");
13521         descP->currentReader.ref =
13522             CP_TERM(descP->currentReader.env, recvRef);
13523         descP->currentReaderP = &descP->currentReader;
13524     } else {
13525 
13526         /*
13527          * This is a retry:
13528          * We have done, for instance, recv(Sock, X), but only received Y < X.
13529          * We then call recv again with size = X-Y. So, we then get a new ref.
13530          *
13531          * Make use of the existing environment
13532          */
13533 
13534         enif_clear_env(descP->currentReader.env);
13535         descP->currentReader.ref = CP_TERM(descP->currentReader.env, recvRef);
13536     }
13537 }
13538 #endif // #ifndef __WIN32__
13539 
13540 
13541 /* *** recv_update_current_reader ***
13542  *
13543  * Demonitors the current reader process and pop's the reader queue.
13544  * If there is a waiting (reader) process, then it will be assigned
13545  * as the new current reader and a new (read) select will be done.
13546  */
13547 #ifndef __WIN32__
13548 static void
recv_update_current_reader(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef)13549 recv_update_current_reader(ErlNifEnv*       env,
13550                            ESockDescriptor* descP,
13551                            ERL_NIF_TERM     sockRef)
13552 {
13553     if (descP->currentReaderP != NULL) {
13554 
13555         ESOCK_ASSERT( DEMONP("recv_update_current_reader",
13556                              env, descP, &descP->currentReader.mon) == 0);
13557 
13558         if (! activate_next_reader(env, descP, sockRef)) {
13559 
13560             SSDBG( descP,
13561                    ("SOCKET",
13562                     "recv_update_current_reader(%T) {%d} -> no more readers\r\n",
13563                     sockRef, descP->sock) );
13564 
13565             descP->currentReaderP = NULL;
13566         }
13567     }
13568 }
13569 #endif // #ifndef __WIN32__
13570 
13571 
13572 /* *** recv_error_current_reader ***
13573  *
13574  * Process the current reader and any waiting readers
13575  * when a read (fatal) error has occured.
13576  * All waiting readers will be "aborted", that is a
13577  * nif_abort message will be sent (with ref and reason).
13578  */
13579 #ifndef __WIN32__
13580 static
recv_error_current_reader(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM reason)13581 void recv_error_current_reader(ErlNifEnv*       env,
13582                                ESockDescriptor* descP,
13583                                ERL_NIF_TERM     sockRef,
13584                                ERL_NIF_TERM     reason)
13585 {
13586     if (descP->currentReaderP != NULL) {
13587         ESockRequestor req;
13588 
13589         requestor_release("recv_error_current_reader",
13590                           env, descP, &descP->currentReader);
13591 
13592         req.env = NULL; /* read by reader_pop before free */
13593         while (reader_pop(env, descP, &req)) {
13594 
13595             SSDBG( descP,
13596                    ("SOCKET", "recv_error_current_reader(%T) {%d} -> abort"
13597                     "\r\n   pid:   %T"
13598                     "\r\n   reason %T"
13599                     "\r\n", sockRef, descP->sock,
13600                     req.pid, reason) );
13601 
13602             esock_send_abort_msg(env, descP, sockRef, &req, reason);
13603 
13604             ESOCK_ASSERT( DEMONP("recv_error_current_reader -> pop'ed reader",
13605                                  env, descP, &req.mon) == 0);
13606         }
13607 
13608         descP->currentReaderP = NULL;
13609     }
13610 }
13611 #endif // #ifndef __WIN32__
13612 
13613 
13614 /* *** recv_check_result ***
13615  *
13616  * Process the result of a call to recv.
13617  */
13618 #ifndef __WIN32__
13619 static
recv_check_result(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,ssize_t toRead,int saveErrno,ErlNifBinary * bufP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)13620 ERL_NIF_TERM recv_check_result(ErlNifEnv*       env,
13621                                ESockDescriptor* descP,
13622                                ssize_t          read,
13623                                ssize_t          toRead,
13624                                int              saveErrno,
13625                                ErlNifBinary*    bufP,
13626                                ERL_NIF_TERM     sockRef,
13627                                ERL_NIF_TERM     recvRef)
13628 {
13629     ERL_NIF_TERM res;
13630 
13631     SSDBG( descP,
13632            ("SOCKET", "recv_check_result(%T) {%d} -> entry with"
13633             "\r\n   read:      %ld"
13634             "\r\n   toRead:    %ld"
13635             "\r\n   saveErrno: %d"
13636             "\r\n   recvRef:   %T"
13637             "\r\n", sockRef, descP->sock,
13638             (long) read, (long) toRead, saveErrno, recvRef) );
13639 
13640 
13641     /* <KOLLA>
13642      *
13643      * We need to handle read = 0 for other type(s) (DGRAM) when
13644      * its actually valid to read 0 bytes.
13645      *
13646      * </KOLLA>
13647      */
13648 
13649     if ((read == 0) && (descP->type == SOCK_STREAM)) {
13650         ERL_NIF_TERM reason = atom_closed;
13651         res = esock_make_error(env, reason);
13652 
13653         ESOCK_CNT_INC(env, descP, sockRef,
13654                       atom_read_fails, &descP->readFails, 1);
13655 
13656         /*
13657          * When a stream socket peer has performed an orderly shutdown,
13658          * the return value will be 0 (the traditional "end-of-file" return).
13659          *
13660          * *We* do never actually try to read 0 bytes!
13661          *
13662          * We must also notify any waiting readers!
13663          */
13664 
13665         recv_error_current_reader(env, descP, sockRef, reason);
13666 
13667         FREE_BIN(bufP);
13668 
13669     } else {
13670 
13671         /* There is a special case: If the provided 'to read' value is
13672          * zero (0) (only for type =/= stream).
13673          * That means that we read as much as we can, using the default
13674          * read buffer size.
13675          */
13676 
13677         if (bufP->size == read) {
13678 
13679             /* +++ We filled the buffer +++ */
13680 
13681             SSDBG( descP,
13682                    ("SOCKET",
13683                     "recv_check_result(%T) {%d} -> [%lu] filled the buffer\r\n",
13684                     sockRef, descP->sock, (unsigned long) bufP->size) );
13685 
13686             res = recv_check_full(env, descP, read, toRead, bufP,
13687                                   sockRef, recvRef);
13688 
13689         } else if (read < 0) {
13690 
13691             /* +++ Error handling +++ */
13692 
13693             res = recv_check_fail(env, descP, saveErrno, bufP, NULL,
13694                                   sockRef, recvRef);
13695 
13696         } else {
13697 
13698             /* +++ We did not fill the buffer +++ */
13699 
13700             SSDBG( descP,
13701                    ("SOCKET",
13702                     "recv_check_result(%T) {%d} -> [%lu] "
13703                     "did not fill the buffer (%ld)\r\n",
13704                     sockRef, descP->sock, (unsigned long) bufP->size,
13705                     (long) read) );
13706 
13707             res = recv_check_partial(env, descP, read, toRead, bufP,
13708                                      sockRef, recvRef);
13709         }
13710     }
13711 
13712     return res;
13713 }
13714 #endif // #ifndef __WIN32__
13715 
13716 
13717 /* *** recv_check_full ***
13718  *
13719  * This function is called if we filled the allocated buffer.
13720  * But are we done yet?
13721  *
13722  * toRead = 0 means: Give me everything you have => maybe
13723  * toRead > 0 means: Yes
13724  */
13725 #ifndef __WIN32__
13726 static
recv_check_full(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,ssize_t toRead,ErlNifBinary * bufP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)13727 ERL_NIF_TERM recv_check_full(ErlNifEnv*       env,
13728                              ESockDescriptor* descP,
13729                              ssize_t          read,
13730                              ssize_t          toRead,
13731                              ErlNifBinary*    bufP,
13732                              ERL_NIF_TERM     sockRef,
13733                              ERL_NIF_TERM     recvRef)
13734 {
13735     ERL_NIF_TERM res;
13736 
13737     if ((toRead == 0) &&
13738         (descP->type == SOCK_STREAM)) {
13739 
13740         /* +++ Give us everything you have got =>     *
13741          *     (maybe) needs to continue          +++ */
13742 
13743         /* Send up each chunk of data for each of the read
13744          * and let the erlang code assemble it: {more, Bin}
13745          * (when complete it should return {ok, Bin}).
13746          * We need to read atleast one more time to be sure if its
13747          * done...
13748          *
13749          * Also, we need to check if the rNumCnt has reached its max (rNum),
13750          * in which case we will assume the read to be done!
13751          */
13752 
13753         SSDBG( descP,
13754                ("SOCKET", "recv_check_full(%T) {%d} -> shall we continue reading?"
13755                 "\r\n   read:    %ld"
13756                 "\r\n   rNum:    %u"
13757                 "\r\n   rNumCnt: %u"
13758                 "\r\n", sockRef, descP->sock,
13759                 (unsigned long) read, descP->rNum, descP->rNumCnt) );
13760 
13761         res = recv_check_full_maybe_done(env, descP, read, bufP,
13762                                          sockRef, recvRef);
13763 
13764     } else {
13765 
13766         /* +++ We got exactly as much as we requested => We are done +++ */
13767 
13768         SSDBG( descP,
13769                ("SOCKET",
13770                 "recv_check_full(%T) {%d} -> [%ld] "
13771                 "we got exactly what we could fit\r\n",
13772                 sockRef, descP->sock, (long) toRead) );
13773 
13774         res = recv_check_full_done(env, descP, read, bufP, sockRef);
13775 
13776     }
13777 
13778     return res;
13779 
13780 }
13781 #endif // #ifndef __WIN32__
13782 
13783 
13784 /* *** recv_check_full_maybe_done ***
13785  *
13786  * Send up each chunk of data for each of the read
13787  * and let the erlang code assemble it: {more, Bin}
13788  * (when complete it should return {ok, Bin}).
13789  * We need to read at least one more time to be sure if its
13790  * done...
13791  *
13792  * Also, we need to check if the rNumCnt has reached its max (rNum),
13793  * in which case we will assume the read to be done!
13794  */
13795 #ifndef __WIN32__
13796 static
recv_check_full_maybe_done(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,ErlNifBinary * bufP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)13797 ERL_NIF_TERM recv_check_full_maybe_done(ErlNifEnv*       env,
13798                                         ESockDescriptor* descP,
13799                                         ssize_t          read,
13800                                         ErlNifBinary*    bufP,
13801                                         ERL_NIF_TERM     sockRef,
13802                                         ERL_NIF_TERM     recvRef)
13803 {
13804     ESOCK_CNT_INC(env, descP, sockRef,
13805                   atom_read_byte, &descP->readByteCnt, read);
13806     descP->readPkgMaxCnt += read;
13807 
13808     descP->rNumCnt++;
13809     if (descP->rNumCnt >= descP->rNum) {
13810 
13811         descP->rNumCnt = 0;
13812 
13813         ESOCK_CNT_INC(env, descP, sockRef,
13814                       atom_read_pkg, &descP->readPkgCnt, 1);
13815         if (descP->readPkgMaxCnt > descP->readPkgMax)
13816             descP->readPkgMax = descP->readPkgMaxCnt;
13817         descP->readPkgMaxCnt = 0;
13818 
13819         recv_update_current_reader(env, descP, sockRef);
13820 
13821         /* This transfers "ownership" of the *allocated* binary to an
13822          * erlang term (no need for an explicit free).
13823          */
13824 
13825         return esock_make_ok2(env, MKBIN(env, bufP));
13826 
13827     }
13828 
13829     /* Yes, we *do* need to continue reading */
13830 
13831     recv_init_current_reader(env, descP, recvRef);
13832 
13833     /* This transfers "ownership" of the *allocated* binary to an
13834      * erlang term (no need for an explicit free).
13835      */
13836 
13837     SSDBG( descP,
13838            ("SOCKET",
13839             "recv_check_full_maybe_done(%T) {%d} -> [%lu] "
13840             "we are done for now - read more\r\n",
13841             sockRef, descP->sock, (unsigned long)bufP->size) );
13842 
13843     return MKT2(env, esock_atom_more, MKBIN(env, bufP));
13844 }
13845 #endif // #ifndef __WIN32__
13846 
13847 
13848 /* *** recv_check_full_done ***
13849  *
13850  * A successful recv and we filled the buffer.
13851  */
13852 #ifndef __WIN32__
13853 static
recv_check_full_done(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,ErlNifBinary * bufP,ERL_NIF_TERM sockRef)13854 ERL_NIF_TERM recv_check_full_done(ErlNifEnv*       env,
13855                                   ESockDescriptor* descP,
13856                                   ssize_t          read,
13857                                   ErlNifBinary*    bufP,
13858                                   ERL_NIF_TERM     sockRef)
13859 {
13860     ERL_NIF_TERM data;
13861 
13862     ESOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1);
13863     ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte,
13864                   &descP->readByteCnt, read);
13865 
13866     descP->readPkgMaxCnt += read;
13867     if (descP->readPkgMaxCnt > descP->readPkgMax)
13868         descP->readPkgMax = descP->readPkgMaxCnt;
13869     descP->readPkgMaxCnt = 0;
13870 
13871     recv_update_current_reader(env, descP, sockRef);
13872 
13873     /* This transfers "ownership" of the *allocated* binary to an
13874      * erlang term (no need for an explicit free).
13875      */
13876     data = MKBIN(env, bufP);
13877 
13878     return esock_make_ok2(env, data);
13879 }
13880 #endif // #ifndef __WIN32__
13881 
13882 
13883 /* *** recv_check_fail ***
13884  *
13885  * Handle recv failure.
13886  */
13887 #ifndef __WIN32__
13888 static
recv_check_fail(ErlNifEnv * env,ESockDescriptor * descP,int saveErrno,ErlNifBinary * buf1P,ErlNifBinary * buf2P,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)13889 ERL_NIF_TERM recv_check_fail(ErlNifEnv*       env,
13890                              ESockDescriptor* descP,
13891                              int              saveErrno,
13892                              ErlNifBinary*    buf1P,
13893                              ErlNifBinary*    buf2P,
13894                              ERL_NIF_TERM     sockRef,
13895                              ERL_NIF_TERM     recvRef)
13896 {
13897     ERL_NIF_TERM res;
13898 
13899     FREE_BIN(buf1P);
13900     if (buf2P != NULL) FREE_BIN(buf2P);
13901 
13902     if (saveErrno == ECONNRESET)  {
13903 
13904         /* +++ Oops - closed +++ */
13905 
13906         SSDBG( descP,
13907                ("SOCKET",
13908                 "recv_check_fail(%T) {%d} -> econnreset: closed"
13909                 "\r\n   recvRef: %T"
13910                 "\r\n", sockRef, descP->sock, recvRef) );
13911 
13912         // This is a bit overkill (to count here), but just in case...
13913         ESOCK_CNT_INC(env, descP, sockRef, atom_read_fails,
13914                       &descP->readFails, 1);
13915 
13916         res = recv_check_fail_econnreset(env, descP, sockRef, recvRef);
13917 
13918     } else if ((saveErrno == ERRNO_BLOCK) ||
13919                (saveErrno == EAGAIN)) {
13920 
13921         SSDBG( descP,
13922                ("SOCKET",
13923                 "recv_check_fail(%T) {%d} -> eagain"
13924                 "\r\n   recvRef: %T"
13925                 "\r\n", sockRef, descP->sock, recvRef) );
13926 
13927         if (COMPARE(recvRef, atom_zero) == 0)
13928             res = esock_atom_ok;
13929         else
13930             res = recv_check_retry(env, descP, sockRef, recvRef);
13931 
13932     } else {
13933 
13934         SSDBG( descP,
13935                ("SOCKET",
13936                 "recv_check_fail(%T) {%d} -> errno: %d\r\n"
13937                 "\r\n   recvRef: %T"
13938                 "\r\n", sockRef, descP->sock, saveErrno, recvRef) );
13939 
13940         ESOCK_CNT_INC(env, descP, sockRef, atom_read_fails,
13941                       &descP->readFails, 1);
13942 
13943         res = recv_check_fail_gen(env, descP, saveErrno, sockRef);
13944     }
13945 
13946     return res;
13947 }
13948 #endif // #ifndef __WIN32__
13949 
13950 
13951 /* *** recv_check_fail_econnreset ***
13952  *
13953  * We detected that the socket was closed wile reading.
13954  * Inform current and waiting readers.
13955  */
13956 #ifndef __WIN32__
13957 static
recv_check_fail_econnreset(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)13958 ERL_NIF_TERM recv_check_fail_econnreset(ErlNifEnv*       env,
13959                                         ESockDescriptor* descP,
13960                                         ERL_NIF_TERM     sockRef,
13961                                         ERL_NIF_TERM     recvRef)
13962 {
13963     ERL_NIF_TERM reason = MKA(env, erl_errno_id(ECONNRESET));
13964     ERL_NIF_TERM res = esock_make_error(env, reason);
13965 
13966     /* <KOLLA>
13967      *
13968      * IF THE CURRENT PROCESS IS *NOT* THE CONTROLLING
13969      * PROCESS, WE NEED TO INFORM IT!!!
13970      *
13971      * ALL WAITING PROCESSES MUST ALSO GET THE ERROR!!
13972      * HANDLED BY THE STOP (CALLBACK) FUNCTION?
13973      *
13974      * SINCE THIS IS A REMOTE CLOSE, WE DON'T NEED TO WAIT
13975      * FOR OUTPUT TO BE WRITTEN (NO ONE WILL READ), JUST
13976      * ABORT THE SOCKET REGARDLESS OF LINGER???
13977      *
13978      * </KOLLA>
13979      */
13980 
13981     recv_error_current_reader(env, descP, sockRef, reason);
13982 
13983     return res;
13984 }
13985 #endif // #ifndef __WIN32__
13986 
13987 
13988 /* *** recv_check_retry ***
13989  *
13990  * The recv call would have blocked, so retry.
13991  */
13992 #ifndef __WIN32__
13993 static
recv_check_retry(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)13994 ERL_NIF_TERM recv_check_retry(ErlNifEnv*       env,
13995                               ESockDescriptor* descP,
13996                               ERL_NIF_TERM     sockRef,
13997                               ERL_NIF_TERM     recvRef)
13998 {
13999     int          sres;
14000     ERL_NIF_TERM res;
14001 
14002     descP->rNumCnt = 0;
14003     recv_init_current_reader(env, descP, recvRef);
14004 
14005     SSDBG( descP,
14006            ("SOCKET",
14007             "recv_check_retry(%T) {%d} -> SELECT for more"
14008             "\r\n   recvRef: %T"
14009             "\r\n", sockRef, descP->sock, recvRef) );
14010 
14011     if ((sres = esock_select_read(env, descP->sock, descP, NULL,
14012                                   sockRef, recvRef)) < 0) {
14013         /* Unlikely that any next reader will have better luck,
14014          * but why not give them a shot - the queue will be cleared
14015          */
14016         recv_update_current_reader(env, descP, sockRef);
14017 
14018         res =
14019             enif_raise_exception(env,
14020                                  MKT2(env, atom_select_read,
14021                                       MKI(env, sres)));
14022     } else {
14023         descP->readState |= ESOCK_STATE_SELECTED;
14024         res = atom_select;
14025     }
14026 
14027     return res;
14028 }
14029 #endif // #ifndef __WIN32__
14030 
14031 
14032 /* *** recv_check_fail_gen ***
14033  *
14034  * The recv call had a "general" failure.
14035  */
14036 #ifndef __WIN32__
14037 static
recv_check_fail_gen(ErlNifEnv * env,ESockDescriptor * descP,int saveErrno,ERL_NIF_TERM sockRef)14038 ERL_NIF_TERM recv_check_fail_gen(ErlNifEnv*       env,
14039                                  ESockDescriptor* descP,
14040                                  int              saveErrno,
14041                                  ERL_NIF_TERM     sockRef)
14042 {
14043     ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno));
14044 
14045     recv_error_current_reader(env, descP, sockRef, reason);
14046 
14047     return esock_make_error(env, reason);
14048 }
14049 #endif // #ifndef __WIN32__
14050 
14051 
14052 /* *** recv_check_partial ***
14053  *
14054  * Handle a sucessful recv which only partly filled the specified buffer.
14055  */
14056 #ifndef __WIN32__
14057 static
recv_check_partial(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,ssize_t toRead,ErlNifBinary * bufP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)14058 ERL_NIF_TERM recv_check_partial(ErlNifEnv*       env,
14059                                 ESockDescriptor* descP,
14060                                 ssize_t          read,
14061                                 ssize_t          toRead,
14062                                 ErlNifBinary*    bufP,
14063                                 ERL_NIF_TERM     sockRef,
14064                                 ERL_NIF_TERM     recvRef)
14065 {
14066     ERL_NIF_TERM res;
14067 
14068     if ((toRead == 0) ||
14069         (descP->type != SOCK_STREAM) ||
14070         (COMPARE(recvRef, atom_zero) == 0)) {
14071 
14072         /* +++ We got it all, but since we      +++
14073          * +++ did not fill the buffer, we      +++
14074          * +++ must split it into a sub-binary. +++
14075          */
14076 
14077         SSDBG( descP,
14078                ("SOCKET",
14079                 "recv_check_partial(%T) {%d} -> [%ld] split buffer"
14080                 "\r\n   recvRef: %T"
14081                 "\r\n", sockRef, descP->sock, (long) toRead,
14082                 recvRef) );
14083 
14084         res = recv_check_partial_done(env, descP, read, bufP, sockRef);
14085 
14086     } else {
14087         /* A stream socket with specified read size
14088          * and not a polling read, we got a partial read
14089          * - return a select result to initiate a retry
14090          */
14091 
14092         SSDBG( descP,
14093                ("SOCKET",
14094                 "recv_check_partial(%T) {%d} -> [%ld]"
14095                 " only part of message - expect more"
14096                 "\r\n   recvRef: %T"
14097                 "\r\n", sockRef, descP->sock, (long) toRead,
14098                 recvRef) );
14099 
14100         res =
14101             recv_check_partial_part(env, descP, read,
14102                                     bufP, sockRef, recvRef);
14103     }
14104 
14105     return res;
14106 }
14107 #endif // #ifndef __WIN32__
14108 
14109 
14110 /* *** recv_check_partial_done ***
14111  *
14112  * A successful but only partial recv, which fulfilled the required read.
14113  */
14114 #ifndef __WIN32__
14115 static
recv_check_partial_done(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,ErlNifBinary * bufP,ERL_NIF_TERM sockRef)14116 ERL_NIF_TERM recv_check_partial_done(ErlNifEnv*       env,
14117                                      ESockDescriptor* descP,
14118                                      ssize_t          read,
14119                                      ErlNifBinary*    bufP,
14120                                      ERL_NIF_TERM     sockRef)
14121 {
14122     ERL_NIF_TERM data;
14123 
14124     descP->rNumCnt = 0;
14125     ESOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1);
14126     ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte,
14127                   &descP->readByteCnt, read);
14128 
14129     descP->readPkgMaxCnt += read;
14130     if (descP->readPkgMaxCnt > descP->readPkgMax)
14131         descP->readPkgMax = descP->readPkgMaxCnt;
14132     descP->readPkgMaxCnt = 0;
14133 
14134     recv_update_current_reader(env, descP, sockRef);
14135 
14136     /* This transfers "ownership" of the *allocated* binary to an
14137      * erlang term (no need for an explicit free).
14138      */
14139     data = MKBIN(env, bufP);
14140     data = MKSBIN(env, data, 0, read);
14141 
14142     SSDBG( descP,
14143            ("SOCKET", "recv_check_partial_done(%T) {%d} -> [%ld] done\r\n",
14144             sockRef, descP->sock, (long) read) );
14145 
14146     return esock_make_ok2(env, data);
14147 }
14148 #endif // #ifndef __WIN32__
14149 
14150 
14151 /* *** recv_check_partial_part ***
14152  *
14153  * A successful but only partial recv, which only partly fulfilled
14154  * the required read.
14155  */
14156 #ifndef __WIN32__
14157 static
recv_check_partial_part(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,ErlNifBinary * bufP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)14158 ERL_NIF_TERM recv_check_partial_part(ErlNifEnv*       env,
14159                                      ESockDescriptor* descP,
14160                                      ssize_t          read,
14161                                      ErlNifBinary*    bufP,
14162                                      ERL_NIF_TERM     sockRef,
14163                                      ERL_NIF_TERM     recvRef)
14164 {
14165     ERL_NIF_TERM res;
14166     int          sres;
14167 
14168     ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte,
14169                   &descP->readByteCnt, read);
14170 
14171     recv_init_current_reader(env, descP, recvRef);
14172 
14173     /* SELECT for more data */
14174 
14175     sres = esock_select_read(env, descP->sock, descP, NULL,
14176                              sockRef, recvRef);
14177     if (sres < 0) {
14178         /* Unlikely that any next reader will have better luck,
14179          * but why not give them a shot - the queue will be cleared
14180          */
14181         recv_update_current_reader(env, descP, sockRef);
14182 
14183         res =
14184             enif_raise_exception(env,
14185                                  MKT2(env, atom_select_read,
14186                                       MKI(env, sres)));
14187     } else {
14188         ERL_NIF_TERM data;
14189 
14190         descP->readState |= ESOCK_STATE_SELECTED;
14191 	data = MKBIN(env, bufP);
14192 	data = MKSBIN(env, data, 0, read);
14193 	res = MKT2(env, atom_select, data);
14194     }
14195 
14196     /* This transfers "ownership" of the *allocated* binary to an
14197      * erlang term (no need for an explicit free).
14198      */
14199     return res;
14200 }
14201 #endif // #ifndef __WIN32__
14202 
14203 
14204 
14205 
14206 /* The recvfrom function delivers one (1) message. If our buffer
14207  * is too small, the message will be truncated. So, regardless
14208  * if we filled the buffer or not, we have got what we are going
14209  * to get regarding this message.
14210  */
14211 #ifndef __WIN32__
14212 static
recvfrom_check_result(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,int saveErrno,ErlNifBinary * bufP,ESockAddress * fromAddrP,SOCKLEN_T fromAddrLen,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)14213 ERL_NIF_TERM recvfrom_check_result(ErlNifEnv*       env,
14214                                    ESockDescriptor* descP,
14215                                    ssize_t          read,
14216                                    int              saveErrno,
14217                                    ErlNifBinary*    bufP,
14218                                    ESockAddress*    fromAddrP,
14219                                    SOCKLEN_T        fromAddrLen,
14220                                    ERL_NIF_TERM     sockRef,
14221                                    ERL_NIF_TERM     recvRef)
14222 {
14223     ERL_NIF_TERM data, res;
14224 
14225     SSDBG( descP,
14226            ("SOCKET", "recvfrom_check_result(%T) {%d} -> entry with"
14227             "\r\n   read:      %ld"
14228             "\r\n   saveErrno: %d"
14229             "\r\n   recvRef:   %T"
14230             "\r\n", sockRef, descP->sock,
14231             (long) read, saveErrno, recvRef) );
14232 
14233     /* <KOLLA>
14234      *
14235      * We need to handle read = 0 for non_stream socket type(s) when
14236      * its actually valid to read 0 bytes.
14237      *
14238      * </KOLLA>
14239      */
14240 
14241     if ((read == 0) && (descP->type == SOCK_STREAM)) {
14242 
14243         /*
14244          * When a stream socket peer has performed an orderly shutdown,
14245          * the return value will be 0 (the traditional "end-of-file" return).
14246          *
14247          * *We* do never actually try to read 0 bytes!
14248          */
14249 
14250         ESOCK_CNT_INC(env, descP, sockRef,
14251                       atom_read_fails, &descP->readFails, 1);
14252 
14253         FREE_BIN(bufP);
14254 
14255         return esock_make_error(env, atom_closed);
14256     }
14257 
14258     if (read < 0) {
14259 
14260         /* +++ Error handling +++ */
14261 
14262         res = recv_check_fail(env, descP, saveErrno, bufP, NULL,
14263                               sockRef, recvRef);
14264 
14265     } else {
14266 
14267         /* +++ We sucessfully got a message - time to encode the address +++ */
14268 
14269         ERL_NIF_TERM eSockAddr;
14270 
14271         esock_encode_sockaddr(env,
14272                               fromAddrP, fromAddrLen,
14273                               &eSockAddr);
14274 
14275         if (read == bufP->size) {
14276 
14277             data = MKBIN(env, bufP);
14278 
14279         } else {
14280 
14281             /* +++ We got a chunk of data but +++
14282              * +++ since we did not fill the  +++
14283              * +++ buffer, we must split it   +++
14284              * +++ into a sub-binary.         +++
14285              */
14286 
14287             data = MKBIN(env, bufP);
14288             data = MKSBIN(env, data, 0, read);
14289         }
14290 
14291         ESOCK_CNT_INC(env, descP, sockRef, atom_read_pkg,
14292                       &descP->readPkgCnt, 1);
14293         ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte,
14294                       &descP->readByteCnt, read);
14295 
14296         recv_update_current_reader(env, descP, sockRef);
14297 
14298         res = esock_make_ok2(env, MKT2(env, eSockAddr, data));
14299 
14300     }
14301 
14302     return res;
14303 
14304 }
14305 #endif // #ifndef __WIN32__
14306 
14307 
14308 
14309 /* *** recvmsg_check_result ***
14310  *
14311  * The recvmsg function delivers one (1) message. If our buffer
14312  * is to small, the message will be truncated. So, regardless
14313  * if we filled the buffer or not, we have got what we are going
14314  * to get regarding this message.
14315  */
14316 #ifndef __WIN32__
14317 static
recvmsg_check_result(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,int saveErrno,struct msghdr * msgHdrP,ErlNifBinary * dataBufP,ErlNifBinary * ctrlBufP,ERL_NIF_TERM sockRef,ERL_NIF_TERM recvRef)14318 ERL_NIF_TERM recvmsg_check_result(ErlNifEnv*       env,
14319                                   ESockDescriptor* descP,
14320                                   ssize_t          read,
14321                                   int              saveErrno,
14322                                   struct msghdr*   msgHdrP,
14323                                   ErlNifBinary*    dataBufP,
14324                                   ErlNifBinary*    ctrlBufP,
14325                                   ERL_NIF_TERM     sockRef,
14326                                   ERL_NIF_TERM     recvRef)
14327 {
14328     ERL_NIF_TERM res;
14329 
14330     SSDBG( descP,
14331            ("SOCKET", "recvmsg_check_result(%T) {%d} -> entry with"
14332             "\r\n   read:      %ld"
14333             "\r\n   saveErrno: %d"
14334             "\r\n   recvRef:   %T"
14335             "\r\n", sockRef, descP->sock,
14336             (long) read, saveErrno, recvRef) );
14337 
14338 
14339     /* <KOLLA>
14340      *
14341      * We need to handle read = 0 for non_stream socket type(s) when
14342      * its actually valid to read 0 bytes.
14343      *
14344      * </KOLLA>
14345      */
14346 
14347     if ((read == 0) && (descP->type == SOCK_STREAM)) {
14348 
14349         /*
14350          * When a stream socket peer has performed an orderly shutdown,
14351          * the return value will be 0 (the traditional "end-of-file" return).
14352          *
14353          * *We* do never actually try to read 0 bytes!
14354          */
14355 
14356         ESOCK_CNT_INC(env, descP, sockRef,
14357                       atom_read_fails, &descP->readFails, 1);
14358 
14359         FREE_BIN(dataBufP); FREE_BIN(ctrlBufP);
14360 
14361         return esock_make_error(env, atom_closed);
14362     }
14363 
14364 
14365     if (read < 0) {
14366 
14367         /* +++ Error handling +++ */
14368 
14369         res = recv_check_fail(env, descP, saveErrno, dataBufP, ctrlBufP,
14370                               sockRef, recvRef);
14371 
14372     } else {
14373 
14374         /* +++ We sucessfully got a message - time to encode it +++ */
14375 
14376         res = recvmsg_check_msg(env, descP, read, msgHdrP,
14377                                 dataBufP, ctrlBufP, sockRef);
14378 
14379     }
14380 
14381     return res;
14382 
14383 }
14384 #endif // #ifndef __WIN32__
14385 
14386 
14387 /* *** recvmsg_check_msg ***
14388  *
14389  * We successfully read one message. Time to process.
14390  */
14391 #ifndef __WIN32__
14392 static
recvmsg_check_msg(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,struct msghdr * msgHdrP,ErlNifBinary * dataBufP,ErlNifBinary * ctrlBufP,ERL_NIF_TERM sockRef)14393 ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv*       env,
14394                                ESockDescriptor* descP,
14395                                ssize_t          read,
14396                                struct msghdr*   msgHdrP,
14397                                ErlNifBinary*    dataBufP,
14398                                ErlNifBinary*    ctrlBufP,
14399                                ERL_NIF_TERM     sockRef)
14400 {
14401     ERL_NIF_TERM eMsg;
14402 
14403     /*
14404      * <KOLLA>
14405      *
14406      * The return value of recvmsg is the *total* number of bytes
14407      * that where successfully read. This data has been put into
14408      * the *IO vector*.
14409      *
14410      * </KOLLA>
14411      */
14412 
14413     encode_msg(env, descP,
14414                read, msgHdrP, dataBufP, ctrlBufP,
14415                &eMsg);
14416 
14417     SSDBG( descP,
14418            ("SOCKET", "recvmsg_check_result(%T) {%d} -> ok\r\n",
14419             sockRef, descP->sock) );
14420 
14421     ESOCK_CNT_INC(env, descP, sockRef, atom_read_pkg, &descP->readPkgCnt, 1);
14422     ESOCK_CNT_INC(env, descP, sockRef, atom_read_byte,
14423                   &descP->readByteCnt, read);
14424 
14425     recv_update_current_reader(env, descP, sockRef);
14426 
14427     return esock_make_ok2(env, eMsg);
14428 }
14429 #endif // #ifndef __WIN32__
14430 
14431 
14432 /* +++ encode_msg +++
14433  *
14434  * Encode a msg() (recvmsg). In erlang its represented as
14435  * a map, which has a specific set of attributes:
14436  *
14437  *     addr (source address) - sockaddr()
14438  *     iov                   - [binary()]
14439  *     ctrl                  - [cmsg()]
14440  *     flags                 - msg_flags()
14441  */
14442 #ifndef __WIN32__
14443 static
encode_msg(ErlNifEnv * env,ESockDescriptor * descP,ssize_t read,struct msghdr * msgHdrP,ErlNifBinary * dataBufP,ErlNifBinary * ctrlBufP,ERL_NIF_TERM * eSockAddr)14444 void encode_msg(ErlNifEnv*       env,
14445                 ESockDescriptor* descP,
14446                 ssize_t          read,
14447                 struct msghdr*   msgHdrP,
14448                 ErlNifBinary*    dataBufP,
14449                 ErlNifBinary*    ctrlBufP,
14450                 ERL_NIF_TERM*    eSockAddr)
14451 {
14452     ERL_NIF_TERM addr, iov, ctrl, flags;
14453 
14454     SSDBG( descP,
14455            ("SOCKET", "encode_msg {%d} -> entry with"
14456             "\r\n   read: %ld"
14457             "\r\n", descP->sock, (long) read) );
14458 
14459     /* The address is not used if we are connected (unless, maybe,
14460      * family is 'local'), so check (length = 0) before we try to encodel
14461      */
14462     if (msgHdrP->msg_namelen != 0) {
14463         esock_encode_sockaddr(env,
14464                               (ESockAddress*) msgHdrP->msg_name,
14465                               msgHdrP->msg_namelen,
14466                               &addr);
14467     } else {
14468         addr = esock_atom_undefined;
14469     }
14470 
14471     SSDBG( descP,
14472            ("SOCKET", "encode_msg {%d} -> encode iov"
14473             "\r\n   msg_iovlen: %lu"
14474             "\r\n",
14475             descP->sock,
14476             (unsigned long) msgHdrP->msg_iovlen) );
14477 
14478     esock_encode_iov(env, read,
14479                      msgHdrP->msg_iov, msgHdrP->msg_iovlen, dataBufP,
14480                      &iov);
14481 
14482     SSDBG( descP,
14483            ("SOCKET",
14484             "encode_msg {%d} -> try encode cmsgs\r\n",
14485             descP->sock) );
14486 
14487     encode_cmsgs(env, descP, ctrlBufP, msgHdrP, &ctrl);
14488 
14489     SSDBG( descP,
14490            ("SOCKET",
14491             "encode_msg {%d} -> try encode flags\r\n",
14492             descP->sock) );
14493 
14494     encode_msg_flags(env, descP, msgHdrP->msg_flags, &flags);
14495 
14496     SSDBG( descP,
14497            ("SOCKET", "encode_msg {%d} -> components encoded:"
14498             "\r\n   addr:  %T"
14499             "\r\n   ctrl:  %T"
14500             "\r\n   flags: %T"
14501             "\r\n", descP->sock, addr, ctrl, flags) );
14502 
14503     {
14504         ERL_NIF_TERM keys[]  = {esock_atom_iov,
14505                                 esock_atom_ctrl,
14506                                 esock_atom_flags,
14507                                 esock_atom_addr};
14508         ERL_NIF_TERM vals[]  = {iov, ctrl, flags, addr};
14509         size_t       numKeys = NUM(keys);
14510 
14511         ESOCK_ASSERT( numKeys == NUM(vals) );
14512 
14513         SSDBG( descP,
14514                ("SOCKET",
14515                 "encode_msg {%d} -> create map\r\n",
14516                 descP->sock) );
14517 
14518         if (msgHdrP->msg_namelen == 0)
14519             numKeys--; // No addr
14520         ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eSockAddr) );
14521 
14522         SSDBG( descP,
14523                ("SOCKET",
14524                 "encode_msg {%d}-> map encoded\r\n",
14525                 descP->sock) );
14526     }
14527 
14528     SSDBG( descP,
14529            ("SOCKET", "encode_msg {%d} -> done\r\n", descP->sock) );
14530 }
14531 #endif // #ifndef __WIN32__
14532 
14533 
14534 
14535 /* +++ encode_cmsgs +++
14536  *
14537  * Encode a list of cmsg(). There can be 0 or more cmsghdr "blocks".
14538  *
14539  * Our "problem" is that we have no idea how many control messages
14540  * we have.
14541  *
14542  * The cmsgHdrP arguments points to the start of the control data buffer,
14543  * an actual binary. Its the only way to create sub-binaries. So, what we
14544  * need to continue processing this is to turn that into an binary erlang
14545  * term (which can then in turn be turned into sub-binaries).
14546  *
14547  * We need the cmsgBufP (even though cmsgHdrP points to it) to be able
14548  * to create sub-binaries (one for each cmsg hdr).
14549  *
14550  * The TArray (term array) is created with the size of 128, which should
14551  * be enough. But if its not, then it will be automatically realloc'ed during
14552  * add. Once we are done adding hdr's to it, we convert the tarray to a list.
14553  */
14554 #ifndef __WIN32__
14555 static
encode_cmsgs(ErlNifEnv * env,ESockDescriptor * descP,ErlNifBinary * cmsgBinP,struct msghdr * msgHdrP,ERL_NIF_TERM * eCMsg)14556 void encode_cmsgs(ErlNifEnv*       env,
14557                   ESockDescriptor* descP,
14558                   ErlNifBinary*    cmsgBinP,
14559                   struct msghdr*   msgHdrP,
14560                   ERL_NIF_TERM*    eCMsg)
14561 {
14562     ERL_NIF_TERM    ctrlBuf  = MKBIN(env, cmsgBinP); // The *entire* binary
14563     SocketTArray    cmsghdrs = TARRAY_CREATE(128);
14564     struct cmsghdr* firstP   = CMSG_FIRSTHDR(msgHdrP);
14565     struct cmsghdr* currentP;
14566 
14567     SSDBG( descP, ("SOCKET", "encode_cmsgs {%d} -> entry when"
14568                    "\r\n   msg ctrl len:  %d"
14569                    "\r\n   (ctrl) firstP: 0x%lX"
14570                    "\r\n", descP->sock,
14571                    msgHdrP->msg_controllen, firstP) );
14572 
14573     for (currentP = firstP;
14574          /*
14575           * In *old* versions of darwin, the CMSG_FIRSTHDR does not
14576           * check the msg_controllen, so we do it here.
14577           * We should really test this stuff during configure,
14578           * but for now, this will have to do.
14579           */
14580 #if defined(__DARWIN__)
14581          (msgHdrP->msg_controllen >= sizeof(struct cmsghdr)) &&
14582              (currentP != NULL);
14583 #else
14584          (currentP != NULL);
14585 #endif
14586          currentP = CMSG_NXTHDR(msgHdrP, currentP)) {
14587 
14588         SSDBG( descP,
14589                ("SOCKET", "encode_cmsgs {%d} -> process cmsg header when"
14590                 "\r\n   TArray Size: %d"
14591                 "\r\n", descP->sock, TARRAY_SZ(cmsghdrs)) );
14592 
14593         /* MUST check this since on Linux the returned "cmsg" may actually
14594          * go too far!
14595          */
14596         if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) >
14597             msgHdrP->msg_controllen) {
14598 
14599             /* Ouch, fatal error - give up
14600              * We assume we cannot trust any data if this is wrong.
14601              */
14602 
14603             SSDBG( descP,
14604                    ("SOCKET", "encode_cmsgs {%d} -> check failed when: "
14605                     "\r\n   currentP:           0x%lX"
14606                     "\r\n   (current) cmsg_len: %d"
14607                     "\r\n   firstP:             0x%lX"
14608                     "\r\n   =>                  %d"
14609                     "\r\n   msg ctrl len:       %d"
14610                     "\r\n", descP->sock,
14611                     CHARP(currentP), currentP->cmsg_len, CHARP(firstP),
14612                     (CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP),
14613                     msgHdrP->msg_controllen) );
14614 
14615             TARRAY_ADD(cmsghdrs, esock_atom_bad_data);
14616             break;
14617 
14618         } else {
14619             unsigned char* dataP   = UCHARP(CMSG_DATA(currentP));
14620             size_t         dataPos = dataP - cmsgBinP->data;
14621             size_t         dataLen =
14622                 (UCHARP(currentP) + currentP->cmsg_len) - dataP;
14623             ERL_NIF_TERM
14624                 cmsgHdr,
14625                 keys[]  =
14626                 {esock_atom_level,
14627                  esock_atom_type,
14628                  esock_atom_data,
14629                  atom_value},
14630                 vals[NUM(keys)];
14631             size_t numKeys = NUM(keys);
14632             BOOLEAN_T have_value;
14633 
14634             SSDBG( descP,
14635                    ("SOCKET", "encode_cmsgs {%d} -> cmsg header data: "
14636                     "\r\n   dataPos: %d"
14637                     "\r\n   dataLen: %d"
14638                     "\r\n", descP->sock, dataPos, dataLen) );
14639 
14640             vals[0] = esock_encode_level(env, currentP->cmsg_level);
14641             vals[2] = MKSBIN(env, ctrlBuf, dataPos, dataLen);
14642             have_value =
14643                 encode_cmsg(env,
14644                             currentP->cmsg_level,
14645                             currentP->cmsg_type,
14646                             dataP, dataLen, &vals[1], &vals[3]);
14647 
14648             SSDBG( descP,
14649                    ("SOCKET", "encode_cmsgs {%d} -> "
14650                     "\r\n   %T: %T"
14651                     "\r\n   %T: %T"
14652                     "\r\n   %T: %T"
14653                     "\r\n", descP->sock,
14654                     keys[0], vals[0], keys[1], vals[1], keys[2], vals[2]) );
14655             if (have_value)
14656                 SSDBG( descP,
14657                        ("SOCKET", "encode_cmsgs {%d} -> "
14658                         "\r\n   %T: %T"
14659                         "\r\n", descP->sock, keys[3], vals[3]) );
14660 
14661             /* Guard agains cut-and-paste errors */
14662             ESOCK_ASSERT( numKeys == NUM(vals) );
14663             ESOCK_ASSERT( MKMA(env, keys, vals,
14664                                numKeys - (have_value ? 0 : 1), &cmsgHdr) );
14665 
14666             /* And finally add it to the list... */
14667             TARRAY_ADD(cmsghdrs, cmsgHdr);
14668         }
14669     }
14670 
14671     SSDBG( descP,
14672            ("SOCKET", "encode_cmsgs {%d} -> cmsg headers processed when"
14673             "\r\n   TArray Size: %d"
14674             "\r\n", descP->sock, TARRAY_SZ(cmsghdrs)) );
14675 
14676     /* The tarray is populated - convert it to a list */
14677     TARRAY_TOLIST(cmsghdrs, env, eCMsg);
14678 }
14679 #endif // #ifndef __WIN32__
14680 
14681 
14682 
14683 #ifndef __WIN32__
14684 #ifdef SCM_TIMESTAMP
14685 static
esock_cmsg_encode_timeval(ErlNifEnv * env,unsigned char * data,size_t dataLen,ERL_NIF_TERM * eResult)14686 BOOLEAN_T esock_cmsg_encode_timeval(ErlNifEnv     *env,
14687                                     unsigned char *data,
14688                                     size_t         dataLen,
14689                                     ERL_NIF_TERM  *eResult) {
14690     struct timeval* timeP = (struct timeval *) data;
14691 
14692     if (dataLen < sizeof(*timeP))
14693         return FALSE;
14694 
14695     esock_encode_timeval(env, timeP, eResult);
14696     return TRUE;
14697 }
14698 
esock_cmsg_decode_timeval(ErlNifEnv * env,ERL_NIF_TERM eValue,struct cmsghdr * cmsgP,size_t rem,size_t * usedP)14699 static BOOLEAN_T esock_cmsg_decode_timeval(ErlNifEnv *env,
14700                                            ERL_NIF_TERM eValue,
14701                                            struct cmsghdr *cmsgP,
14702                                            size_t rem,
14703                                            size_t *usedP)
14704 {
14705     struct timeval time, *timeP;
14706 
14707     if (! esock_decode_timeval(env, eValue, &time))
14708         return FALSE;
14709 
14710     if ((timeP = init_cmsghdr(cmsgP, rem, sizeof(*timeP), usedP)) == NULL)
14711         return FALSE;
14712 
14713     *timeP = time;
14714     return TRUE;
14715 }
14716 #endif
14717 #endif
14718 
14719 
14720 #ifndef __WIN32__
14721 #if defined(IP_TOS) || defined(IP_RECVTOS)
14722 static
esock_cmsg_encode_ip_tos(ErlNifEnv * env,unsigned char * data,size_t dataLen,ERL_NIF_TERM * eResult)14723 BOOLEAN_T esock_cmsg_encode_ip_tos(ErlNifEnv     *env,
14724                                    unsigned char *data,
14725                                    size_t         dataLen,
14726                                    ERL_NIF_TERM  *eResult)
14727 {
14728     unsigned char tos;
14729 
14730     if (dataLen < sizeof(tos))
14731         return FALSE;
14732 
14733     tos = *data;
14734 
14735     *eResult = encode_ip_tos(env, tos);
14736     return TRUE;
14737 }
14738 
esock_cmsg_decode_ip_tos(ErlNifEnv * env,ERL_NIF_TERM eValue,struct cmsghdr * cmsgP,size_t rem,size_t * usedP)14739 static BOOLEAN_T esock_cmsg_decode_ip_tos(ErlNifEnv *env,
14740                                           ERL_NIF_TERM eValue,
14741                                           struct cmsghdr *cmsgP,
14742                                           size_t rem,
14743                                           size_t *usedP)
14744 {
14745     int tos, *tosP;
14746 
14747     if (! decode_ip_tos(env, eValue, &tos))
14748         return FALSE;
14749 
14750     if ((tosP = init_cmsghdr(cmsgP, rem, sizeof(*tosP), usedP)) == NULL)
14751         return FALSE;
14752 
14753     *tosP = tos;
14754     return TRUE;
14755 }
14756 #endif // #ifdef IP_TOS
14757 #endif // #ifdef __WIN32__
14758 
14759 #ifndef __WIN32__
14760 #if defined(IP_TTL) || \
14761     defined(IPV6_HOPLIMIT) || \
14762     defined(IPV6_TCLASS) || defined(IPV6_RECVTCLASS)
14763 static
esock_cmsg_encode_int(ErlNifEnv * env,unsigned char * data,size_t dataLen,ERL_NIF_TERM * eResult)14764 BOOLEAN_T esock_cmsg_encode_int(ErlNifEnv     *env,
14765                                 unsigned char *data,
14766                                 size_t         dataLen,
14767                                 ERL_NIF_TERM  *eResult) {
14768     int value;
14769 
14770     if (dataLen < sizeof(value))
14771         return FALSE;
14772 
14773     value = *((int *) data);
14774     *eResult = MKI(env, value);
14775     return TRUE;
14776 }
14777 
esock_cmsg_decode_int(ErlNifEnv * env,ERL_NIF_TERM eValue,struct cmsghdr * cmsgP,size_t rem,size_t * usedP)14778 static BOOLEAN_T esock_cmsg_decode_int(ErlNifEnv *env,
14779                                        ERL_NIF_TERM eValue,
14780                                        struct cmsghdr *cmsgP,
14781                                        size_t rem,
14782                                        size_t *usedP)
14783 {
14784     int value, *valueP;
14785 
14786     if (! GET_INT(env, eValue, &value))
14787         return FALSE;
14788 
14789     if ((valueP = init_cmsghdr(cmsgP, rem, sizeof(*valueP), usedP)) == NULL)
14790         return FALSE;
14791 
14792     *valueP = value;
14793     return TRUE;
14794 }
14795 #endif
14796 #endif
14797 
14798 
14799 #ifndef __WIN32__
esock_cmsg_decode_bool(ErlNifEnv * env,ERL_NIF_TERM eValue,struct cmsghdr * cmsgP,size_t rem,size_t * usedP)14800 static BOOLEAN_T esock_cmsg_decode_bool(ErlNifEnv *env,
14801                                         ERL_NIF_TERM eValue,
14802                                         struct cmsghdr *cmsgP,
14803                                         size_t rem,
14804                                         size_t *usedP)
14805 {
14806     BOOLEAN_T v;
14807     int *valueP;
14808 
14809     if (! esock_decode_bool(eValue, &v))
14810         return FALSE;
14811 
14812     if ((valueP = init_cmsghdr(cmsgP, rem, sizeof(*valueP), usedP)) == NULL)
14813         return FALSE;
14814 
14815     *valueP = v? 1 : 0;
14816     return TRUE;
14817 }
14818 #endif
14819 
14820 
14821 #ifndef __WIN32__
14822 #ifdef IP_RECVTTL
14823 static
esock_cmsg_encode_uchar(ErlNifEnv * env,unsigned char * data,size_t dataLen,ERL_NIF_TERM * eResult)14824 BOOLEAN_T esock_cmsg_encode_uchar(ErlNifEnv     *env,
14825                                   unsigned char *data,
14826                                   size_t         dataLen,
14827                                   ERL_NIF_TERM  *eResult) {
14828     unsigned char value;
14829 
14830     if (dataLen < sizeof(value))
14831         return FALSE;
14832 
14833     value = *data;
14834     *eResult = MKUI(env, value);
14835     return TRUE;
14836 }
14837 #endif
14838 #endif
14839 
14840 #ifndef __WIN32__
14841 #ifdef IP_PKTINFO
14842 static
esock_cmsg_encode_in_pktinfo(ErlNifEnv * env,unsigned char * data,size_t dataLen,ERL_NIF_TERM * eResult)14843 BOOLEAN_T esock_cmsg_encode_in_pktinfo(ErlNifEnv     *env,
14844                                        unsigned char *data,
14845                                        size_t         dataLen,
14846                                        ERL_NIF_TERM  *eResult) {
14847     struct in_pktinfo* pktInfoP = (struct in_pktinfo*) data;
14848     ERL_NIF_TERM       ifIndex;
14849     ERL_NIF_TERM       specDst, addr;
14850 
14851     if (dataLen < sizeof(*pktInfoP))
14852         return FALSE;
14853 
14854     ifIndex  = MKUI(env, pktInfoP->ipi_ifindex);
14855     esock_encode_in_addr(env, &pktInfoP->ipi_spec_dst, &specDst);
14856     esock_encode_in_addr(env, &pktInfoP->ipi_addr, &addr);
14857 
14858     {
14859         ERL_NIF_TERM keys[] = {esock_atom_ifindex,
14860                                esock_atom_spec_dst,
14861                                esock_atom_addr};
14862         ERL_NIF_TERM vals[] = {ifIndex, specDst, addr};
14863         unsigned int numKeys = NUM(keys);
14864         unsigned int numVals = NUM(vals);
14865 
14866         ESOCK_ASSERT( numKeys == numVals );
14867         ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eResult) );
14868     }
14869     return TRUE;
14870 }
14871 #endif
14872 #endif
14873 
14874 #ifndef __WIN32__
14875 #ifdef IP_ORIGDSTADDR
14876 static
esock_cmsg_encode_sockaddr(ErlNifEnv * env,unsigned char * data,size_t dataLen,ERL_NIF_TERM * eResult)14877 BOOLEAN_T esock_cmsg_encode_sockaddr(ErlNifEnv     *env,
14878                                      unsigned char *data,
14879                                      size_t         dataLen,
14880                                      ERL_NIF_TERM  *eResult) {
14881     SOCKLEN_T addrLen = (SOCKLEN_T) dataLen;
14882 
14883     if (addrLen != dataLen)
14884         return FALSE;
14885 
14886     esock_encode_sockaddr(env,
14887                           (ESockAddress*) data,
14888                           addrLen,
14889                           eResult);
14890     return TRUE;
14891 }
14892 #endif
14893 #endif
14894 
14895 #ifndef __WIN32__
14896 #ifdef HAVE_LINUX_ERRQUEUE_H
14897 #if defined(IP_RECVERR) || defined(IPV6_RECVERR)
14898 /* +++ encode_cmsg_encode_recverr +++
14899  *
14900  * Encode the extended socker error in the data part of the cmsg().
14901  *
14902  */
14903 static
esock_cmsg_encode_recverr(ErlNifEnv * env,unsigned char * data,size_t dataLen,ERL_NIF_TERM * eCMsgData)14904 BOOLEAN_T esock_cmsg_encode_recverr(ErlNifEnv                *env,
14905                                     unsigned char            *data,
14906                                     size_t                    dataLen,
14907                                     ERL_NIF_TERM             *eCMsgData)
14908 {
14909     struct sock_extended_err *sock_err = (struct sock_extended_err *) data;
14910     struct sockaddr *offender;
14911     BOOLEAN_T        have_offender = FALSE;
14912     ERL_NIF_TERM
14913         ee_errno, ee_origin, ee_type, ee_code, ee_info, ee_data,
14914         eSockAddr;
14915 
14916     if (dataLen < sizeof(*sock_err))
14917         return FALSE;
14918 
14919     offender = SO_EE_OFFENDER(sock_err);
14920     ee_errno = MKA(env, erl_errno_id(sock_err->ee_errno));
14921     ee_info = MKI(env, sock_err->ee_info);
14922     ee_data = MKI(env, sock_err->ee_data);
14923 
14924     switch (sock_err->ee_origin) {
14925 #if defined(SO_EE_ORIGIN_NONE)
14926     case SO_EE_ORIGIN_NONE:
14927         ee_origin = atom_none;
14928         ee_type = MKI(env, sock_err->ee_type);
14929         ee_code = MKI(env, sock_err->ee_code);
14930         break;
14931 #endif
14932 
14933 #if defined(SO_EE_ORIGIN_LOCAL)
14934     case SO_EE_ORIGIN_LOCAL:
14935         ee_origin = esock_atom_local;
14936         ee_type = MKI(env, sock_err->ee_type);
14937         ee_code = MKI(env, sock_err->ee_code);
14938         break;
14939 #endif
14940 
14941 #if defined(SO_EE_ORIGIN_ICMP)
14942     case SO_EE_ORIGIN_ICMP:
14943         ee_origin = esock_atom_icmp;
14944         switch (sock_err->ee_type) {
14945 
14946 #if defined(ICMP_DEST_UNREACH)
14947         case ICMP_DEST_UNREACH:
14948             ee_type   = atom_dest_unreach;
14949             switch (sock_err->ee_code) {
14950 
14951 #if defined(ICMP_NET_UNREACH)
14952             case ICMP_NET_UNREACH:
14953                 ee_code = atom_net_unreach;
14954                 break;
14955 #endif
14956 
14957 #if defined(ICMP_HOST_UNREACH)
14958             case ICMP_HOST_UNREACH:
14959                 ee_code = atom_host_unreach;
14960                 break;
14961 #endif
14962 
14963 #if defined(ICMP_PORT_UNREACH)
14964             case ICMP_PORT_UNREACH:
14965                 ee_code = atom_port_unreach;
14966                 break;
14967 #endif
14968 
14969 #if defined(ICMP_FRAG_NEEDED)
14970             case ICMP_FRAG_NEEDED:
14971                 ee_code = atom_frag_needed;
14972                 break;
14973 #endif
14974 
14975 #if defined(ICMP_NET_UNKNOWN)
14976             case ICMP_NET_UNKNOWN:
14977                 ee_code = atom_net_unknown;
14978                 break;
14979 #endif
14980 
14981 #if defined(ICMP_HOST_UNKNOWN)
14982             case ICMP_HOST_UNKNOWN:
14983                 ee_code = atom_host_unknown;
14984                 break;
14985 #endif
14986 
14987             default:
14988                 ee_code = MKI(env, sock_err->ee_code);
14989                 break;
14990             }
14991             break;
14992 #endif // ICMP_DEST_UNREACH
14993 
14994 #if defined(ICMP_TIME_EXCEEDED)
14995         case ICMP_TIME_EXCEEDED:
14996             ee_type = atom_time_exceeded;
14997             ee_code = MKI(env, sock_err->ee_code);
14998             break;
14999 #endif
15000 
15001         default:
15002             ee_type = MKI(env, sock_err->ee_type);
15003             ee_code = MKI(env, sock_err->ee_code);
15004             break;
15005         }
15006         break;
15007 #endif // SO_EE_ORIGIN_ICMP
15008 
15009 #if defined(SO_EE_ORIGIN_ICMP6)
15010     case SO_EE_ORIGIN_ICMP6:
15011         ee_origin = esock_atom_icmp6;
15012         switch (sock_err->ee_type) {
15013 
15014 #if defined(ICMPV6_DEST_UNREACH)
15015         case ICMPV6_DEST_UNREACH:
15016             ee_type = atom_dest_unreach;
15017             switch (sock_err->ee_code) {
15018 
15019 #if defined(ICMPV6_NOROUTE)
15020             case ICMPV6_NOROUTE:
15021                 ee_code = atom_noroute;
15022                 break;
15023 #endif
15024 #if defined(ICMPV6_ADM_PROHIBITED)
15025             case ICMPV6_ADM_PROHIBITED:
15026                 ee_code = atom_adm_prohibited;
15027                 break;
15028 #endif
15029 
15030 #if defined(ICMPV6_NOT_NEIGHBOUR)
15031             case ICMPV6_NOT_NEIGHBOUR:
15032                 ee_code = atom_not_neighbour;
15033                 break;
15034 #endif
15035 
15036 #if defined(ICMPV6_ADDR_UNREACH)
15037             case ICMPV6_ADDR_UNREACH:
15038                 ee_code = atom_addr_unreach;
15039                 break;
15040 #endif
15041 
15042 #if defined(ICMPV6_PORT_UNREACH)
15043             case ICMPV6_PORT_UNREACH:
15044                 ee_code = atom_port_unreach;
15045                 break;
15046 #endif
15047 
15048 #if defined(ICMPV6_POLICY_FAIL)
15049             case ICMPV6_POLICY_FAIL:
15050                 ee_code = atom_policy_fail;
15051                 break;
15052 #endif
15053 
15054 #if defined(ICMPV6_REJECT_ROUTE)
15055             case ICMPV6_REJECT_ROUTE:
15056                 ee_code = atom_reject_route;
15057                 break;
15058 #endif
15059 
15060             default:
15061                 ee_code = MKI(env, sock_err->ee_code);
15062                 break;
15063             }
15064             break;
15065 #endif // ICMPV6_DEST_UNREACH
15066 
15067 #if defined(ICMPV6_PKT_TOOBIG)
15068         case ICMPV6_PKT_TOOBIG:
15069             ee_type = atom_pkt_toobig;
15070             ee_code = MKI(env, sock_err->ee_code);
15071             break;
15072 #endif
15073 
15074 #if defined(ICMPV6_TIME_EXCEED)
15075         case ICMPV6_TIME_EXCEED:
15076             ee_type = atom_time_exceeded;
15077             ee_code = MKI(env, sock_err->ee_code);
15078             break;
15079 #endif
15080 
15081         default:
15082             ee_type = MKI(env, sock_err->ee_type);
15083             ee_code = MKI(env, sock_err->ee_code);
15084             break;
15085         }
15086         break;
15087 #endif // SO_EE_ORIGIN_ICMP6
15088 
15089 #if defined(SO_EE_ORIGIN_TXSTATUS)
15090     case SO_EE_ORIGIN_TXSTATUS:
15091         ee_origin = atom_txstatus;
15092         ee_type   = MKI(env, sock_err->ee_type);
15093         ee_code   = MKI(env, sock_err->ee_code);
15094         break;
15095 #endif
15096 
15097 #if defined(SO_EE_ORIGIN_ZEROCOPY)
15098     case SO_EE_ORIGIN_ZEROCOPY:
15099         ee_origin = atom_zerocopy;
15100         ee_type   = MKI(env, sock_err->ee_type);
15101         ee_code   = MKI(env, sock_err->ee_code);
15102         break;
15103 #endif
15104 
15105 #if defined(SO_EE_ORIGIN_TXTIME)
15106     case SO_EE_ORIGIN_TXTIME:
15107         ee_origin = atom_txtime;
15108         ee_type   = MKI(env, sock_err->ee_type);
15109         ee_code   = MKI(env, sock_err->ee_code);
15110         break;
15111 #endif
15112 
15113     default:
15114         ee_origin = MKI(env, sock_err->ee_origin);
15115         ee_type   = MKI(env, sock_err->ee_type);
15116         ee_code   = MKI(env, sock_err->ee_code);
15117         break;
15118     }
15119 
15120     have_offender = CHARP(sock_err) + dataLen > CHARP(offender);
15121     if (have_offender) {
15122         esock_encode_sockaddr(env,
15123                               (ESockAddress *)offender,
15124                               (CHARP(sock_err) + dataLen ) - CHARP(offender),
15125                               &eSockAddr);
15126     } else {
15127         eSockAddr = esock_atom_undefined;
15128     }
15129 
15130     {
15131         ERL_NIF_TERM keys[] = {esock_atom_error,
15132                                atom_origin,
15133                                esock_atom_type,
15134                                atom_code,
15135                                esock_atom_info,
15136                                esock_atom_data,
15137                                atom_offender};
15138         ERL_NIF_TERM vals[] = {ee_errno,
15139                                ee_origin,
15140                                ee_type,
15141                                ee_code,
15142                                ee_info,
15143                                ee_data,
15144                                eSockAddr};
15145         unsigned int numKeys = NUM(keys);
15146         unsigned int numVals = NUM(vals);
15147 
15148         ESOCK_ASSERT( numKeys == numVals );
15149         if (! have_offender) numKeys--;
15150         ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eCMsgData) );
15151     }
15152     return TRUE;
15153 }
15154 #endif // #if defined(IP_RECVERR) || defined(IPV6_RECVERR)
15155 #endif // #ifdef HAVE_LINUX_ERRQUEUE_H
15156 #endif // #ifndef __WIN32__
15157 
15158 #ifndef __WIN32__
15159 #ifdef IPV6_PKTINFO
15160 static
esock_cmsg_encode_in6_pktinfo(ErlNifEnv * env,unsigned char * data,size_t dataLen,ERL_NIF_TERM * eResult)15161 BOOLEAN_T esock_cmsg_encode_in6_pktinfo(ErlNifEnv     *env,
15162                                         unsigned char *data,
15163                                         size_t         dataLen,
15164                                         ERL_NIF_TERM  *eResult) {
15165     struct in6_pktinfo* pktInfoP = (struct in6_pktinfo*) data;
15166     ERL_NIF_TERM        ifIndex, addr;
15167 
15168     if (dataLen < sizeof(*pktInfoP))
15169         return FALSE;
15170     ifIndex  = MKI(env, pktInfoP->ipi6_ifindex);
15171     esock_encode_in6_addr(env, &pktInfoP->ipi6_addr, &addr);
15172     {
15173         ERL_NIF_TERM keys[]  = {esock_atom_addr, esock_atom_ifindex};
15174         ERL_NIF_TERM vals[]  = {addr, ifIndex};
15175         unsigned int numKeys = NUM(keys);
15176         unsigned int numVals = NUM(vals);
15177 
15178         ESOCK_ASSERT( numKeys == numVals );
15179         ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eResult) );
15180     }
15181     return TRUE;
15182 }
15183 #endif
15184 #endif
15185 
15186 
15187 
15188 #ifndef __WIN32__
15189 
15190 struct ESockCmsgSpec {
15191     int type; // Message type
15192 
15193     // Function to encode into erlang term
15194     BOOLEAN_T (* encode)
15195     (ErlNifEnv *env, unsigned char *data, size_t dataLen,
15196      ERL_NIF_TERM *eResult);
15197 
15198     // Function to decode from erlang term
15199     BOOLEAN_T (* decode)
15200     (ErlNifEnv *env, ERL_NIF_TERM eValue,
15201      struct cmsghdr *cmsgP, size_t rem, size_t *usedP);
15202 
15203     ERL_NIF_TERM *nameP; // Pointer to option name atom
15204 };
15205 
cmpESockCmsgSpec(const void * vpa,const void * vpb)15206 static int cmpESockCmsgSpec(const void *vpa, const void *vpb) {
15207     struct ESockCmsgSpec *a, *b;
15208     a = (struct ESockCmsgSpec *) vpa;
15209     b = (struct ESockCmsgSpec *) vpb;
15210     return COMPARE(*(a->nameP), *(b->nameP));
15211 }
15212 
15213 static struct ESockCmsgSpec
15214     cmsgLevelSocket[] =
15215     {
15216 #if defined(SCM_CREDENTIALS)
15217         {SCM_CREDENTIALS, NULL, NULL,
15218          &esock_atom_credentials},
15219 #elif defined(SCM_CREDS)
15220         {SCM_CREDS, NULL, NULL,
15221          &esock_atom_credentials},
15222 #endif
15223 
15224 #if defined(SCM_RIGHTS)
15225         {SCM_RIGHTS, NULL, NULL,
15226          &esock_atom_rights},
15227 #endif
15228 
15229 #if defined(SCM_TIMESTAMP)
15230         {SCM_TIMESTAMP,
15231          &esock_cmsg_encode_timeval, esock_cmsg_decode_timeval,
15232          &esock_atom_timestamp},
15233 #endif
15234     },
15235 
15236     cmsgLevelIP[] =
15237     {
15238 #if defined(IP_TOS)
15239         {IP_TOS, esock_cmsg_encode_ip_tos, esock_cmsg_decode_ip_tos,
15240          &esock_atom_tos},
15241 #endif
15242 
15243 #if defined(IP_TTL)
15244         {IP_TTL, esock_cmsg_encode_int, esock_cmsg_decode_int,
15245          &esock_atom_ttl},
15246 #endif
15247 
15248 #if defined(IP_RECVTTL)
15249         {IP_RECVTTL, esock_cmsg_encode_uchar, NULL,
15250          &esock_atom_recvttl},
15251 #endif
15252 
15253 #if defined(IP_PKTINFO)
15254         {IP_PKTINFO, esock_cmsg_encode_in_pktinfo, NULL,
15255          &esock_atom_pktinfo},
15256 #endif
15257 
15258 #if defined(IP_ORIGDSTADDR)
15259         {IP_ORIGDSTADDR, esock_cmsg_encode_sockaddr, NULL,
15260          &esock_atom_origdstaddr},
15261 #endif
15262 
15263 #if defined(IP_RECVTOS)
15264         {IP_RECVTOS, esock_cmsg_encode_ip_tos, NULL,
15265          &esock_atom_recvtos},
15266 #endif
15267 
15268 #if defined(IP_RECVERR)
15269         {IP_RECVERR,
15270 #if defined(HAVE_LINUX_ERRQUEUE_H)
15271          esock_cmsg_encode_recverr,
15272 #else
15273          NULL,
15274 #endif
15275          NULL,
15276          &esock_atom_recverr},
15277 #endif
15278     };
15279 
15280 #ifdef HAVE_IPV6
15281 static struct ESockCmsgSpec cmsgLevelIPv6[] =
15282     {
15283 #if defined(IPV6_PKTINFO)
15284         {IPV6_PKTINFO, esock_cmsg_encode_in6_pktinfo, NULL,
15285          &esock_atom_pktinfo},
15286 #endif
15287 
15288 #if defined(IPV6_HOPLIMIT)
15289         {IPV6_HOPLIMIT, esock_cmsg_encode_int, esock_cmsg_decode_int,
15290          &esock_atom_hoplimit},
15291 #endif
15292 
15293 #if defined(IPV6_TCLASS)
15294         {IPV6_TCLASS, esock_cmsg_encode_int, esock_cmsg_decode_int,
15295          &esock_atom_tclass},
15296 #endif
15297 
15298 #if defined(IPV6_RECVTCLASS)
15299         {IPV6_RECVTCLASS, esock_cmsg_encode_int, NULL,
15300          &esock_atom_recvtclass},
15301 #endif
15302 
15303 #if defined(IPV6_RECVERR)
15304         {IPV6_RECVERR,
15305 #if defined(HAVE_LINUX_ERRQUEUE_H)
15306          esock_cmsg_encode_recverr,
15307 #else
15308          NULL,
15309 #endif
15310          NULL,
15311          &esock_atom_recverr},
15312 #endif
15313     };
15314 #endif // #ifdef HAVE_IPV6
15315 
initCmsgTables(void)15316 static void initCmsgTables(void) {
15317     ESOCK_SORT_TABLE(cmsgLevelSocket, cmpESockCmsgSpec);
15318     ESOCK_SORT_TABLE(cmsgLevelIP, cmpESockCmsgSpec);
15319 #ifdef HAVE_IPV6
15320     ESOCK_SORT_TABLE(cmsgLevelIPv6, cmpESockCmsgSpec);
15321 #endif
15322 }
15323 
lookupCmsgTable(int level,size_t * num)15324 static struct ESockCmsgSpec *lookupCmsgTable(int level, size_t *num) {
15325     switch (level) {
15326 
15327     case SOL_SOCKET:
15328         *num = NUM(cmsgLevelSocket);
15329         return cmsgLevelSocket;
15330 
15331 #ifdef SOL_IP
15332     case SOL_IP:
15333 #else
15334     case IPPROTO_IP:
15335 #endif
15336         *num = NUM(cmsgLevelIP);
15337         return cmsgLevelIP;
15338 
15339 #ifdef HAVE_IPV6
15340 #ifdef SOL_IPV6
15341     case SOL_IPV6:
15342 #else
15343     case IPPROTO_IPV6:
15344 #endif
15345         *num = NUM(cmsgLevelIPv6);
15346         return cmsgLevelIPv6;
15347 #endif
15348 
15349     default:
15350         return NULL;
15351     }
15352 }
15353 
lookupCmsgSpec(struct ESockCmsgSpec * table,size_t num,ERL_NIF_TERM eType)15354 static struct ESockCmsgSpec *lookupCmsgSpec(struct ESockCmsgSpec *table,
15355                                             size_t num,
15356                                             ERL_NIF_TERM eType) {
15357     struct ESockCmsgSpec key;
15358 
15359     sys_memzero(CHARP(&key), sizeof(key));
15360     key.nameP = &eType;
15361     return bsearch(&key, table, num, sizeof(*table), cmpESockCmsgSpec);
15362 }
15363 
15364 #endif // #ifdef __WIN32__
15365 
15366 
15367 
15368 #ifndef __WIN32__
15369 static
encode_cmsg(ErlNifEnv * env,int level,int type,unsigned char * dataP,size_t dataLen,ERL_NIF_TERM * eType,ERL_NIF_TERM * eData)15370 BOOLEAN_T encode_cmsg(ErlNifEnv*     env,
15371                       int            level,
15372                       int            type,
15373                       unsigned char* dataP,
15374                       size_t         dataLen,
15375                       ERL_NIF_TERM*  eType,
15376                       ERL_NIF_TERM*  eData) {
15377     const struct ESockCmsgSpec *cmsgTable;
15378     size_t num;
15379 
15380     if ((cmsgTable = lookupCmsgTable(level, &num)) != NULL) {
15381         size_t n;
15382 
15383         /* Linear search for type number in level table
15384          */
15385         for (n = 0;  n < num;  n++) {
15386             if (cmsgTable[n].type == type) {
15387                 /* Found the type number in the level table;
15388                  * return the symbolic type (atom)
15389                  * and try to encode the data
15390                  */
15391 
15392                 *eType = *cmsgTable[n].nameP;
15393 
15394                 if (cmsgTable[n].encode != NULL)
15395                     return cmsgTable[n].encode(env, dataP, dataLen, eData);
15396                 else
15397                     return FALSE;
15398             }
15399         }
15400     }
15401     /* No level table, or unknown type number in the table;
15402      * just return the type number as an erlang integer
15403      */
15404 
15405 
15406     *eType = MKI(env, type);
15407     return FALSE;
15408 }
15409 #endif
15410 
15411 
15412 #ifndef __WIN32__
15413 static
decode_cmsghdr_value(ErlNifEnv * env,ESockDescriptor * descP,int level,ERL_NIF_TERM eType,ERL_NIF_TERM eValue,char * bufP,size_t rem,size_t * usedP)15414 BOOLEAN_T decode_cmsghdr_value(ErlNifEnv*   env,
15415                                ESockDescriptor* descP,
15416                                int          level,
15417                                ERL_NIF_TERM eType,
15418                                ERL_NIF_TERM eValue,
15419                                char*        bufP,
15420                                size_t       rem,
15421                                size_t*      usedP)
15422 {
15423     int type;
15424     struct cmsghdr *cmsgP = (struct cmsghdr *) bufP;
15425     struct ESockCmsgSpec *cmsgTable;
15426     struct ESockCmsgSpec *cmsgSpecP = NULL;
15427     size_t num = 0;
15428 
15429     SSDBG( descP,
15430            ("SOCKET",
15431             "decode_cmsghdr_value {%d} -> entry  \r\n"
15432             "   eType:  %T\r\n"
15433             "   eValue: %T\r\n",
15434             descP->sock, eType, eValue) );
15435 
15436     // We have decode functions only for symbolic (atom) types
15437     if (! IS_ATOM(env, eType)) {
15438         SSDBG( descP,
15439                ("SOCKET",
15440                 "decode_cmsghdr_value {%d} -> FALSE:\r\n"
15441                 "   eType not an atom\r\n",
15442                 descP->sock) );
15443         return FALSE;
15444     }
15445 
15446     /* Try to look up the symbolic type
15447      */
15448     if (((cmsgTable = lookupCmsgTable(level, &num)) == NULL) ||
15449         ((cmsgSpecP = lookupCmsgSpec(cmsgTable, num, eType)) == NULL) ||
15450         (cmsgSpecP->decode == NULL)) {
15451         /* We found no table for this level,
15452          * we found no symbolic type in the level table,
15453          * or no decode function for this type
15454          */
15455 
15456         SSDBG( descP,
15457                ("SOCKET",
15458                 "decode_cmsghdr_value {%d} -> FALSE:\r\n"
15459                 "   cmsgTable:  %p\r\n"
15460                 "   cmsgSpecP:  %p\r\n",
15461                 descP->sock, cmsgTable, cmsgSpecP) );
15462         return FALSE;
15463     }
15464 
15465     if (! cmsgSpecP->decode(env, eValue, cmsgP, rem, usedP)) {
15466         // Decode function failed
15467         SSDBG( descP,
15468                ("SOCKET",
15469                 "decode_cmsghdr_value {%d} -> FALSE:\r\n"
15470                 "   decode function failed\r\n",
15471                 descP->sock) );
15472         return FALSE;
15473     }
15474 
15475     // Succesful decode
15476 
15477     type = cmsgSpecP->type;
15478 
15479     SSDBG( descP,
15480            ("SOCKET",
15481             "decode_cmsghdr_value {%d} -> TRUE:\r\n"
15482             "   level:   %d\r\n"
15483             "   type:    %d\r\n",
15484             "   *usedP:  %lu\r\n",
15485             descP->sock, level, type, (unsigned long) *usedP) );
15486 
15487     cmsgP->cmsg_level = level;
15488     cmsgP->cmsg_type = type;
15489     return TRUE;
15490 }
15491 #endif
15492 
15493 #ifndef __WIN32__
15494 static
decode_cmsghdr_data(ErlNifEnv * env,ESockDescriptor * descP,int level,ERL_NIF_TERM eType,ERL_NIF_TERM eData,char * bufP,size_t rem,size_t * usedP)15495 BOOLEAN_T decode_cmsghdr_data(ErlNifEnv*   env,
15496                               ESockDescriptor* descP,
15497                               int          level,
15498                               ERL_NIF_TERM eType,
15499                               ERL_NIF_TERM eData,
15500                               char*        bufP,
15501                               size_t       rem,
15502                               size_t*      usedP)
15503 {
15504     int type;
15505     ErlNifBinary bin;
15506     struct cmsghdr *cmsgP = (struct cmsghdr *) bufP;
15507     struct ESockCmsgSpec *cmsgSpecP = NULL;
15508 
15509     SSDBG( descP,
15510            ("SOCKET",
15511             "decode_cmsghdr_data {%d} -> entry  \r\n"
15512             "   eType: %T\r\n"
15513             "   eData: %T\r\n",
15514             descP->sock, eType, eData) );
15515 
15516     // Decode Type
15517     if (! GET_INT(env, eType, &type)) {
15518         struct ESockCmsgSpec *cmsgTable = NULL;
15519         size_t num = 0;
15520 
15521         /* Try to look up the symbolic (atom) type
15522          */
15523         if ((! IS_ATOM(env, eType)) ||
15524             ((cmsgTable = lookupCmsgTable(level, &num)) == NULL) ||
15525             ((cmsgSpecP = lookupCmsgSpec(cmsgTable, num, eType)) == NULL)) {
15526             /* Type was not an atom,
15527              * we found no table for this level,
15528              * or we found no symbolic type in the level table
15529              */
15530 
15531             SSDBG( descP,
15532                    ("SOCKET",
15533                     "decode_cmsghdr_data {%d} -> FALSE:\r\n"
15534                     "   cmsgTable:  %p\r\n"
15535                     "   cmsgSpecP:  %p\r\n",
15536                     descP->sock, cmsgTable, cmsgSpecP) );
15537             return FALSE;
15538         }
15539 
15540         type = cmsgSpecP->type;
15541     }
15542 
15543     // Decode Data
15544     if (GET_BIN(env, eData, &bin)) {
15545         void *p;
15546 
15547         p = init_cmsghdr(cmsgP, rem, bin.size, usedP);
15548         if (p == NULL) {
15549             /* No room for the data
15550              */
15551 
15552             SSDBG( descP,
15553                    ("SOCKET",
15554                     "decode_cmsghdr_data {%d} -> FALSE:\r\n"
15555                     "   rem:      %lu\r\n"
15556                     "   bin.size: %lu\r\n",
15557                     descP->sock,
15558                     (unsigned long) rem,
15559                     (unsigned long) bin.size) );
15560             return FALSE;
15561         }
15562 
15563         // Copy the binary data
15564         sys_memcpy(p, bin.data, bin.size);
15565 
15566     } else if ((! esock_cmsg_decode_int(env, eData, cmsgP, rem, usedP)) &&
15567                (! esock_cmsg_decode_bool(env, eData, cmsgP, rem, usedP))) {
15568         SSDBG( descP,
15569                ("SOCKET",
15570                 "decode_cmsghdr_data {%d} -> FALSE\r\n",
15571                 descP->sock) );
15572         return FALSE;
15573     }
15574 
15575     // Succesful decode
15576 
15577     SSDBG( descP,
15578            ("SOCKET",
15579             "decode_cmsghdr_data {%d} -> TRUE:\r\n"
15580             "   level:   %d\r\n"
15581             "   type:    %d\r\n"
15582             "   *usedP:  %lu\r\n",
15583             descP->sock, level, type, (unsigned long) *usedP) );
15584 
15585     cmsgP->cmsg_level = level;
15586     cmsgP->cmsg_type = type;
15587     return TRUE;
15588 }
15589 #endif
15590 
15591 /* Clear the CMSG space and init the ->cmsg_len member,
15592  * return the position for the data, and the total used space
15593  */
15594 #ifndef __WIN32__
init_cmsghdr(struct cmsghdr * cmsgP,size_t rem,size_t size,size_t * usedP)15595 static void *init_cmsghdr(struct cmsghdr *cmsgP,
15596                           size_t rem,  // Remaining space
15597                           size_t size, // Size of data
15598                           size_t *usedP)
15599 {
15600     size_t space = CMSG_SPACE(size);
15601 
15602     if (rem < space)
15603         return NULL; // Not enough space
15604 
15605     sys_memzero(cmsgP, space);
15606     cmsgP->cmsg_len = CMSG_LEN(size);
15607 
15608     *usedP = space;
15609     return CMSG_DATA(cmsgP);
15610 }
15611 #endif
15612 
15613 
15614 /* +++ decode_cmsghdrs +++
15615  *
15616  * Decode a list of cmsg(). There can be 0 or more "blocks".
15617  *
15618  * Each element can either be a (erlang) map that needs to be decoded,
15619  * or a (erlang) binary that just needs to be appended to the control
15620  * buffer.
15621  *
15622  * Our "problem" is that we have no idea much memory we actually need.
15623  *
15624  */
15625 #ifndef __WIN32__
15626 static
decode_cmsghdrs(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eCMsg,char * cmsgHdrBufP,size_t cmsgHdrBufLen,size_t * cmsgHdrBufUsed)15627 BOOLEAN_T decode_cmsghdrs(ErlNifEnv*       env,
15628                           ESockDescriptor* descP,
15629                           ERL_NIF_TERM     eCMsg,
15630                           char*            cmsgHdrBufP,
15631                           size_t           cmsgHdrBufLen,
15632                           size_t*          cmsgHdrBufUsed)
15633 {
15634     ERL_NIF_TERM elem, tail, list;
15635     char*        bufP;
15636     size_t       rem, used, totUsed = 0;
15637     unsigned int len;
15638     int          i;
15639 
15640     SSDBG( descP, ("SOCKET", "decode_cmsghdrs {%d} -> entry with"
15641                    "\r\n   eCMsg:      %T"
15642                    "\r\n   cmsgHdrBufP:   0x%lX"
15643                    "\r\n   cmsgHdrBufLen: %d"
15644                    "\r\n", descP->sock,
15645                    eCMsg, cmsgHdrBufP, cmsgHdrBufLen) );
15646 
15647     if (! GET_LIST_LEN(env, eCMsg, &len))
15648         return FALSE;
15649 
15650     SSDBG( descP,
15651            ("SOCKET",
15652             "decode_cmsghdrs {%d} -> list length: %d\r\n",
15653             descP->sock, len) );
15654 
15655     for (i = 0, list = eCMsg, rem  = cmsgHdrBufLen, bufP = cmsgHdrBufP;
15656          i < len; i++) {
15657 
15658         SSDBG( descP, ("SOCKET", "decode_cmsghdrs {%d} -> process elem %d:"
15659                        "\r\n   (buffer) rem:     %u"
15660                        "\r\n   (buffer) totUsed: %u"
15661                        "\r\n", descP->sock, i, rem, totUsed) );
15662 
15663         /* Extract the (current) head of the (cmsg hdr) list */
15664         if (! GET_LIST_ELEM(env, list, &elem, &tail))
15665             return FALSE;
15666 
15667         used = 0; // Just in case...
15668         if (! decode_cmsghdr(env, descP, elem, bufP, rem, &used))
15669             return FALSE;
15670 
15671         bufP     = CHARP( ULONG(bufP) + used );
15672         rem      = SZT( rem - used );
15673         list     = tail;
15674         totUsed += used;
15675 
15676     }
15677 
15678     *cmsgHdrBufUsed = totUsed;
15679 
15680     SSDBG( descP, ("SOCKET", "decode_cmsghdrs {%d} -> done"
15681                    "\r\n   all %u ctrl headers processed"
15682                    "\r\n   totUsed = %lu\r\n",
15683                    descP->sock, len, (unsigned long) totUsed) );
15684 
15685     return TRUE;
15686 }
15687 #endif // #ifndef __WIN32__
15688 
15689 
15690 /* +++ decode_cmsghdr +++
15691  *
15692  * Decode one cmsg(). Put the "result" into the buffer and advance the
15693  * pointer (of the buffer) afterwards. Also update 'rem' accordingly.
15694  * But before the actual decode, make sure that there is enough room in
15695  * the buffer for the cmsg header (sizeof(*hdr) < rem).
15696  *
15697  * The eCMsg should be a map with three fields:
15698  *
15699  *     level :: socket | protocol() | integer()
15700  *     type  :: atom() | integer()
15701  *                                What values are valid depend on the level
15702  *     data  :: binary() | integer() | boolean()
15703  *                                The type of the data depends on
15704  *     or                         level and type, but can be a binary,
15705  *                                which means that the data is already coded.
15706  *     value :: term()            Which is a term matching the decode function
15707  */
15708 #ifndef __WIN32__
15709 static
decode_cmsghdr(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM eCMsg,char * bufP,size_t rem,size_t * used)15710 BOOLEAN_T decode_cmsghdr(ErlNifEnv*       env,
15711                          ESockDescriptor* descP,
15712                          ERL_NIF_TERM     eCMsg,
15713                          char*            bufP,
15714                          size_t           rem,
15715                          size_t*          used)
15716 {
15717     ERL_NIF_TERM eLevel, eType, eData, eValue;
15718     int          level;
15719 
15720     SSDBG( descP, ("SOCKET", "decode_cmsghdr {%d} -> entry with"
15721                    "\r\n   eCMsg: %T"
15722                    "\r\n", descP->sock, eCMsg) );
15723 
15724     // Get 'level' field
15725     if (! GET_MAP_VAL(env, eCMsg, esock_atom_level, &eLevel))
15726         return FALSE;
15727     SSDBG( descP, ("SOCKET", "decode_cmsghdr {%d} -> eLevel: %T"
15728                    "\r\n", descP->sock, eLevel) );
15729 
15730     // Get 'type' field
15731     if (! GET_MAP_VAL(env, eCMsg, esock_atom_type, &eType))
15732         return FALSE;
15733     SSDBG( descP, ("SOCKET", "decode_cmsghdr {%d} -> eType:  %T"
15734                    "\r\n", descP->sock, eType) );
15735 
15736     // Decode Level
15737     if (! esock_decode_level(env, eLevel, &level))
15738         return FALSE;
15739     SSDBG( descP, ("SOCKET", "decode_cmsghdr {%d}-> level:  %d\r\n",
15740                    descP->sock, level) );
15741 
15742     // Get 'data' field
15743     if (! GET_MAP_VAL(env, eCMsg, esock_atom_data, &eData)) {
15744 
15745         // Get 'value' field
15746         if (! GET_MAP_VAL(env, eCMsg, atom_value, &eValue))
15747             return FALSE;
15748         SSDBG( descP, ("SOCKET", "decode_cmsghdr {%d} -> eValue:  %T"
15749                    "\r\n", descP->sock, eValue) );
15750 
15751         // Decode Value
15752         if (! decode_cmsghdr_value(env, descP, level, eType, eValue,
15753                                    bufP, rem, used))
15754             return FALSE;
15755 
15756     } else {
15757 
15758         // Verify no 'value' field
15759         if (GET_MAP_VAL(env, eCMsg, atom_value, &eValue))
15760             return FALSE;
15761 
15762         SSDBG( descP, ("SOCKET", "decode_cmsghdr {%d} -> eData:  %T"
15763                    "\r\n", descP->sock, eData) );
15764 
15765         // Decode Data
15766         if (! decode_cmsghdr_data(env, descP, level, eType, eData,
15767                                   bufP, rem, used))
15768             return FALSE;
15769     }
15770 
15771     SSDBG( descP, ("SOCKET", "decode_cmsghdr {%d}-> used:  %lu\r\n",
15772                    descP->sock, (unsigned long) *used) );
15773 
15774     return TRUE;
15775 }
15776 #endif // #ifndef __WIN32__
15777 
15778 
15779 
15780 /* +++ encode_msg_flags +++
15781  *
15782  * Encode a list of msg_flag().
15783  *
15784  */
15785 #ifndef __WIN32__
15786 static
encode_msg_flags(ErlNifEnv * env,ESockDescriptor * descP,int msgFlags,ERL_NIF_TERM * flags)15787 void encode_msg_flags(ErlNifEnv*       env,
15788                       ESockDescriptor* descP,
15789                       int              msgFlags,
15790                       ERL_NIF_TERM*    flags)
15791 {
15792     SSDBG( descP,
15793            ("SOCKET", "encode_msg_flags {%d} -> entry with"
15794             "\r\n   msgFlags: %d (0x%lX)"
15795             "\r\n", descP->sock, msgFlags, msgFlags) );
15796 
15797     if (msgFlags == 0) {
15798         *flags = MKEL(env);
15799     } else {
15800         size_t n;
15801         SocketTArray ta = TARRAY_CREATE(10); // Just to be on the safe side
15802 
15803         for (n = 0;  n < NUM(msg_flags);  n++) {
15804             int f = msg_flags[n].flag;
15805             if ((f != 0) && ((msgFlags & f) == f)) {
15806                 msgFlags &= ~f;
15807                 TARRAY_ADD(ta, *(msg_flags[n].name));
15808             }
15809             if (msgFlags == 0) goto done;
15810         }
15811         /* Append remaining flags as an integer */
15812         if (msgFlags != 0)
15813             TARRAY_ADD(ta, MKI(env, msgFlags));
15814 
15815     done:
15816         SSDBG( descP,
15817                ("SOCKET", "encode_msg_flags {%d} -> flags processed when"
15818                 "\r\n   TArray size: %d"
15819                 "\r\n", descP->sock, TARRAY_SZ(ta)) );
15820 
15821         TARRAY_TOLIST(ta, env, flags);
15822     }
15823 }
15824 #endif // #ifndef __WIN32__
15825 
15826 
15827 /* +++ decode the ip socket option TOS +++
15828  * The (ip) option can be provide in two ways:
15829  *
15830  *           atom() | integer()
15831  *
15832  * When its an atom it can have the values:
15833  *
15834  *       lowdelay |  throughput | reliability | mincost
15835  *
15836  */
15837 #ifndef __WIN32__
15838 #if defined(IP_TOS)
15839 static
decode_ip_tos(ErlNifEnv * env,ERL_NIF_TERM eVal,int * val)15840 BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
15841 {
15842     BOOLEAN_T result = FALSE;
15843 
15844     if (IS_ATOM(env, eVal)) {
15845 
15846         if (COMPARE(eVal, esock_atom_lowdelay) == 0) {
15847             *val   = IPTOS_LOWDELAY;
15848             result = TRUE;
15849         } else if (COMPARE(eVal, esock_atom_throughput) == 0) {
15850             *val   = IPTOS_THROUGHPUT;
15851             result = TRUE;
15852         } else if (COMPARE(eVal, esock_atom_reliability) == 0) {
15853             *val   = IPTOS_RELIABILITY;
15854             result = TRUE;
15855 #if defined(IPTOS_MINCOST)
15856         } else if (COMPARE(eVal, esock_atom_mincost) == 0) {
15857             *val   = IPTOS_MINCOST;
15858             result = TRUE;
15859 #endif
15860         } else {
15861             *val   = -1;
15862             result = FALSE;
15863         }
15864 
15865     } else if (IS_NUM(env, eVal)) {
15866 
15867         if (GET_INT(env, eVal, val)) {
15868             result = TRUE;
15869         } else {
15870             *val   = -1;
15871             result = FALSE;
15872         }
15873 
15874     } else {
15875         *val   = -1;
15876         result = FALSE;
15877     }
15878 
15879     return result;
15880 }
15881 #endif
15882 #endif // #ifndef __WIN32__
15883 
15884 
15885 /* +++ decode the ip socket option MTU_DISCOVER +++
15886  * The (ip) option can be provide in two ways:
15887  *
15888  *           atom() | integer()
15889  *
15890  * When it's an atom it can have the values:
15891  *
15892  *       want | dont | do | probe
15893  *
15894  */
15895 #ifndef __WIN32__
15896 #if defined(IP_MTU_DISCOVER)
15897 static
decode_ip_pmtudisc(ErlNifEnv * env,ERL_NIF_TERM eVal,int * val)15898 BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
15899 {
15900     if (IS_ATOM(env, eVal)) {
15901 
15902         if (COMPARE(eVal, atom_want) == 0) {
15903             *val = IP_PMTUDISC_WANT;
15904         } else if (COMPARE(eVal, atom_dont) == 0) {
15905             *val = IP_PMTUDISC_DONT;
15906         } else if (COMPARE(eVal, atom_do) == 0) {
15907             *val = IP_PMTUDISC_DO;
15908 #if defined(IP_PMTUDISC_PROBE)
15909         } else if (COMPARE(eVal, atom_probe) == 0) {
15910             *val = IP_PMTUDISC_PROBE;
15911 #endif
15912         } else {
15913             return FALSE;
15914         }
15915 
15916     } else if (! GET_INT(env, eVal, val)) {
15917         return FALSE;
15918     }
15919 
15920     return TRUE;
15921 }
15922 #endif
15923 #endif // #ifndef __WIN32__
15924 
15925 
15926 #ifndef __WIN32__
15927 #if defined(IPV6_MULTICAST_HOPS) || defined(IPV6_UNICAST_HOPS)
15928 static
decode_hops(ErlNifEnv * env,ERL_NIF_TERM eVal,int * val)15929 BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val) {
15930     int hops;
15931 
15932     if (! GET_INT(env, eVal, &hops)) {
15933         if (COMPARE(eVal, esock_atom_default) == 0) {
15934             *val = -1;
15935             return TRUE;
15936         }
15937         return FALSE;
15938     }
15939     if (hops < 0 || 255 < hops)
15940         return FALSE;
15941 
15942     *val = hops;
15943     return TRUE;
15944 }
15945 #endif
15946 #endif // #ifndef __WIN32__
15947 
15948 
15949 /* +++ decode the ipv6 socket option MTU_DISCOVER +++
15950  * The (ip) option can be provide in two ways:
15951  *
15952  *           atom() | integer()
15953  *
15954  * When its an atom it can have the values:
15955  *
15956  *       want | dont | do | probe
15957  *
15958  */
15959 #ifndef __WIN32__
15960 #if defined(IPV6_MTU_DISCOVER)
15961 static
decode_ipv6_pmtudisc(ErlNifEnv * env,ERL_NIF_TERM eVal,int * val)15962 BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val)
15963 {
15964     if (IS_ATOM(env, eVal)) {
15965 
15966         if (COMPARE(eVal, atom_want) == 0) {
15967             *val = IPV6_PMTUDISC_WANT;
15968         } else if (COMPARE(eVal, atom_dont) == 0) {
15969             *val = IPV6_PMTUDISC_DONT;
15970         } else if (COMPARE(eVal, atom_do) == 0) {
15971             *val = IPV6_PMTUDISC_DO;
15972 #if defined(IPV6_PMTUDISC_PROBE)
15973         } else if (COMPARE(eVal, atom_probe) == 0) {
15974             *val = IPV6_PMTUDISC_PROBE;
15975 #endif
15976         } else {
15977             return FALSE;
15978         }
15979 
15980     } else if (! GET_INT(env, eVal, val)) {
15981         return FALSE;
15982     }
15983 
15984     return TRUE;
15985 }
15986 #endif
15987 #endif // #ifndef __WIN32__
15988 
15989 
15990 /* +++ encode the ip socket option MTU_DISCOVER +++
15991  * The (ip) option can be provide in two ways:
15992  *
15993  *           atom() | integer()
15994  *
15995  * If its one of the "known" values, it will be an atom:
15996  *
15997  *       want | dont | do | probe
15998  *
15999  */
16000 #ifndef __WIN32__
16001 #if defined(IP_MTU_DISCOVER)
16002 static
encode_ip_pmtudisc(ErlNifEnv * env,int val,ERL_NIF_TERM * eVal)16003 void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
16004 {
16005     switch (val) {
16006     case IP_PMTUDISC_WANT:
16007         *eVal = atom_want;
16008         break;
16009 
16010     case IP_PMTUDISC_DONT:
16011         *eVal = atom_dont;
16012         break;
16013 
16014     case IP_PMTUDISC_DO:
16015         *eVal = atom_do;
16016         break;
16017 
16018 #if defined(IP_PMTUDISC_PROBE)
16019     case IP_PMTUDISC_PROBE:
16020         *eVal = atom_probe;
16021         break;
16022 #endif
16023 
16024     default:
16025         *eVal = MKI(env, val);
16026         break;
16027     }
16028 
16029     return;
16030 }
16031 #endif
16032 #endif // #ifndef __WIN32__
16033 
16034 
16035 /* +++ encode the ipv6 socket option MTU_DISCOVER +++
16036  * The (ipv6) option can be provide in two ways:
16037  *
16038  *           atom() | integer()
16039  *
16040  * If its one of the "known" values, it will be an atom:
16041  *
16042  *       want | dont | do | probe
16043  *
16044  */
16045 #ifndef __WIN32__
16046 #if defined(IPV6_MTU_DISCOVER)
16047 static
encode_ipv6_pmtudisc(ErlNifEnv * env,int val,ERL_NIF_TERM * eVal)16048 void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal)
16049 {
16050     switch (val) {
16051     case IPV6_PMTUDISC_WANT:
16052         *eVal = atom_want;
16053         break;
16054 
16055     case IPV6_PMTUDISC_DONT:
16056         *eVal = atom_dont;
16057         break;
16058 
16059     case IPV6_PMTUDISC_DO:
16060         *eVal = atom_do;
16061         break;
16062 
16063 #if defined(IPV6_PMTUDISC_PROBE)
16064     case IPV6_PMTUDISC_PROBE:
16065         *eVal = atom_probe;
16066         break;
16067 #endif
16068 
16069     default:
16070         *eVal = MKI(env, val);
16071         break;
16072     }
16073 
16074     return;
16075 }
16076 #endif
16077 #endif // #ifndef __WIN32__
16078 
16079 
16080 /* +++ encode the ip socket option tos +++
16081  * The (ip) option can be provide as:
16082  *
16083  *       lowdelay |  throughput | reliability | mincost | integer()
16084  *
16085  */
16086 #ifndef __WIN32__
16087 static
encode_ip_tos(ErlNifEnv * env,int val)16088 ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val)
16089 {
16090     ERL_NIF_TERM result;
16091 
16092     switch (IPTOS_TOS(val)) {
16093     case IPTOS_LOWDELAY:
16094         result = esock_atom_lowdelay;
16095         break;
16096 
16097     case IPTOS_THROUGHPUT:
16098         result = esock_atom_throughput;
16099         break;
16100 
16101     case IPTOS_RELIABILITY:
16102         result = esock_atom_reliability;
16103         break;
16104 
16105 #if defined(IPTOS_MINCOST)
16106     case IPTOS_MINCOST:
16107         result = esock_atom_mincost;
16108         break;
16109 #endif
16110 
16111     default:
16112         result = MKI(env, val);
16113         break;
16114     }
16115 
16116     return result;
16117 }
16118 #endif // #ifndef __WIN32__
16119 
16120 
16121 #ifndef __WIN32__
16122 #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
16123 
16124 static
decode_sctp_assoc_t(ErlNifEnv * env,ERL_NIF_TERM eVal,sctp_assoc_t * val)16125 BOOLEAN_T decode_sctp_assoc_t(ErlNifEnv* env,
16126                               ERL_NIF_TERM eVal,
16127                               sctp_assoc_t* val)
16128 {
16129     sctp_assoc_t assoc_id;
16130     int i;
16131     unsigned int ui;
16132 
16133     /* Ensure that the assoc_id fits whether it is signed or unsigned
16134      */
16135     if (GET_INT(env, eVal, &i)) {
16136         assoc_id = (sctp_assoc_t) i;
16137         if ((int) assoc_id == i) {
16138             *val = assoc_id;
16139             return TRUE;
16140         }
16141     } else if (GET_UINT(env, eVal, &ui)) {
16142         assoc_id = (sctp_assoc_t) ui;
16143         if ((unsigned int) assoc_id == ui) {
16144             *val = assoc_id;
16145             return TRUE;
16146         }
16147     }
16148 
16149     return FALSE;
16150 }
16151 
16152 static
encode_sctp_assoc_t(ErlNifEnv * env,sctp_assoc_t val)16153 ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env, sctp_assoc_t val)
16154 {
16155     unsigned int ui;
16156 
16157     ui = (unsigned int) val;
16158     if ((sctp_assoc_t) ui == val)
16159         return MKUI(env, ui);
16160     else
16161         return MKI(env, val);
16162 }
16163 
16164 #endif // #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO)
16165 #endif // #ifdef __WIN32__
16166 
16167 
16168 /* *** alloc_descriptor ***
16169  *
16170  * Allocate and perform basic initialization of a socket descriptor.
16171  *
16172  */
16173 #ifndef __WIN32__
16174 static
alloc_descriptor(SOCKET sock,ErlNifEvent event)16175 ESockDescriptor* alloc_descriptor(SOCKET sock, ErlNifEvent event)
16176 {
16177     ESockDescriptor* descP;
16178     char buf[64]; /* Buffer used for building the mutex name */
16179 
16180     ESOCK_ASSERT( (descP =
16181                    enif_alloc_resource(esocks, sizeof(ESockDescriptor)))
16182                   != NULL );
16183 
16184     descP->pattern = ESOCK_DESC_PATTERN_CREATED;
16185 
16186     requestor_init(&descP->connector);
16187     descP->connectorP = NULL;
16188 
16189     sprintf(buf, "esock.w[%d]", sock);
16190     descP->writeMtx       = MCREATE(buf);
16191     descP->writeState     = 0;
16192     requestor_init(&descP->currentWriter);
16193     descP->currentWriterP = NULL; // currentWriter not used
16194     descP->writersQ.first = NULL;
16195     descP->writersQ.last  = NULL;
16196 
16197     descP->writePkgCnt     = 0;
16198     descP->writePkgMax     = 0;
16199     descP->writePkgMaxCnt  = 0;
16200     descP->writeByteCnt    = 0;
16201     descP->writeTries      = 0;
16202     descP->writeWaits      = 0;
16203     descP->writeFails      = 0;
16204 
16205 #ifdef HAVE_SENDFILE
16206     descP->sendfileHandle      = INVALID_HANDLE;
16207     descP->sendfileCountersP = NULL;
16208 #endif
16209 
16210     sprintf(buf, "esock.r[%d]", sock);
16211     descP->readMtx        = MCREATE(buf);
16212     descP->readState      = 0;
16213     requestor_init(&descP->currentReader);
16214     descP->currentReaderP = NULL; // currentReader not used
16215     descP->readersQ.first = NULL;
16216     descP->readersQ.last  = NULL;
16217 
16218     descP->readPkgCnt     = 0;
16219     descP->readPkgMax     = 0;
16220     descP->readPkgMaxCnt  = 0;
16221     descP->readByteCnt    = 0;
16222     descP->readTries      = 0;
16223     descP->readWaits      = 0;
16224     descP->readFails      = 0;
16225 
16226     sprintf(buf, "esock.acc[%d]", sock);
16227     requestor_init(&descP->currentAcceptor);
16228     descP->currentAcceptorP = NULL; // currentAcceptor not used
16229     descP->acceptorsQ.first = NULL;
16230     descP->acceptorsQ.last  = NULL;
16231     descP->accSuccess       = 0;
16232     descP->accFails         = 0;
16233     descP->accTries         = 0;
16234     descP->accWaits         = 0;
16235 
16236     sprintf(buf, "esock.close[%d]", sock);
16237     descP->closeEnv         = NULL;
16238     descP->closeRef         = esock_atom_undefined;
16239     enif_set_pid_undefined(&descP->closerPid);
16240     MON_INIT(&descP->closerMon);
16241 
16242     sprintf(buf, "esock.cfg[%d]", sock);
16243     descP->rBufSz           = ESOCK_RECV_BUFFER_SIZE_DEFAULT;
16244     descP->rNum             = ESOCK_RECV_BUFFER_COUNT_DEFAULT;
16245     descP->rNumCnt          = 0;
16246     descP->rCtrlSz          = ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT;
16247     descP->wCtrlSz          = ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT;
16248     descP->iow              = FALSE;
16249     descP->dbg              = ESOCK_DEBUG_DEFAULT; // Overwritten by caller
16250     descP->useReg           = ESOCK_USE_SOCKET_REGISTRY; // Overwritten by caller
16251     descP->meta.env         = esock_alloc_env("alloc_descriptor - "
16252                                               "meta-env");
16253     descP->meta.ref         = esock_atom_undefined;
16254 
16255     descP->sock             = sock;
16256     descP->event            = event;
16257     descP->origFD           = INVALID_SOCKET;
16258     descP->closeOnClose     = TRUE;
16259 
16260     enif_set_pid_undefined(&descP->ctrlPid);
16261     MON_INIT(&descP->ctrlMon);
16262 
16263     return descP;
16264 }
16265 #endif // #ifndef __WIN32__
16266 
16267 
16268 /* Decrement counters for when a socket is closed
16269  */
16270 #ifndef __WIN32__
16271 static
dec_socket(int domain,int type,int protocol)16272 void dec_socket(int domain, int type, int protocol)
16273 {
16274     MLOCK(data.cntMtx);
16275 
16276     cnt_dec(&data.numSockets, 1);
16277 
16278     /* *** Domain counter *** */
16279     if (domain == AF_INET)
16280         cnt_dec(&data.numDomainInet, 1);
16281 #if defined(HAVE_IN6) && defined(AF_INET6)
16282     else if (domain == AF_INET6)
16283         cnt_dec(&data.numDomainInet6, 1);
16284 #endif
16285 #ifdef HAS_AF_LOCAL
16286     else if (domain == AF_LOCAL)
16287         cnt_dec(&data.numDomainInet6, 1);
16288 #endif
16289 
16290     /* *** Type counter *** */
16291     if (type == SOCK_STREAM)
16292         cnt_dec(&data.numTypeStreams, 1);
16293     else if (type == SOCK_DGRAM)
16294         cnt_dec(&data.numTypeDGrams, 1);
16295 #ifdef SOCK_SEQPACKET
16296     else if (type == SOCK_SEQPACKET)
16297         cnt_dec(&data.numTypeSeqPkgs, 1);
16298 #endif
16299 
16300     /* *** Protocol counter *** */
16301     if (protocol == IPPROTO_IP)
16302         cnt_dec(&data.numProtoIP, 1);
16303     else if (protocol == IPPROTO_TCP)
16304         cnt_dec(&data.numProtoTCP, 1);
16305     else if (protocol == IPPROTO_UDP)
16306         cnt_dec(&data.numProtoUDP, 1);
16307 #if defined(HAVE_SCTP)
16308     else if (protocol == IPPROTO_SCTP)
16309         cnt_dec(&data.numProtoSCTP, 1);
16310 #endif
16311 
16312     MUNLOCK(data.cntMtx);
16313 }
16314 #endif // #ifndef __WIN32__
16315 
16316 
16317 /* Increment counters for when a socket is opened
16318  */
16319 #ifndef __WIN32__
16320 static
inc_socket(int domain,int type,int protocol)16321 void inc_socket(int domain, int type, int protocol)
16322 {
16323     cnt_inc(&data.numSockets, 1);
16324 
16325     /* *** Domain counter *** */
16326     if (domain == AF_INET)
16327         cnt_inc(&data.numDomainInet, 1);
16328 #if defined(HAVE_IN6) && defined(AF_INET6)
16329     else if (domain == AF_INET6)
16330         cnt_inc(&data.numDomainInet6, 1);
16331 #endif
16332 #ifdef HAS_AF_LOCAL
16333     else if (domain == AF_LOCAL)
16334         cnt_inc(&data.numDomainInet6, 1);
16335 #endif
16336 
16337     /* *** Type counter *** */
16338     if (type == SOCK_STREAM)
16339         cnt_inc(&data.numTypeStreams, 1);
16340     else if (type == SOCK_DGRAM)
16341         cnt_inc(&data.numTypeDGrams, 1);
16342 #ifdef SOCK_SEQPACKET
16343     else if (type == SOCK_SEQPACKET)
16344         cnt_inc(&data.numTypeSeqPkgs, 1);
16345 #endif
16346 
16347     /* *** Protocol counter *** */
16348     if (protocol == IPPROTO_IP)
16349         cnt_inc(&data.numProtoIP, 1);
16350     else if (protocol == IPPROTO_TCP)
16351         cnt_inc(&data.numProtoTCP, 1);
16352     else if (protocol == IPPROTO_UDP)
16353         cnt_inc(&data.numProtoUDP, 1);
16354 #if defined(HAVE_SCTP)
16355     else if (protocol == IPPROTO_SCTP)
16356         cnt_inc(&data.numProtoSCTP, 1);
16357 #endif
16358 }
16359 #endif // #ifndef __WIN32__
16360 
16361 
16362 
16363 /* ----------------------------------------------------------------------
16364  *  D e c o d e / E n c o d e   F u n c t i o n s
16365  * ----------------------------------------------------------------------
16366  */
16367 
16368 #ifndef __WIN32__
16369 #ifdef HAVE_SETNS
16370 /* esock_open4_get_netns - extract the netns field from the opts map
16371  */
16372 static
esock_open4_get_netns(ErlNifEnv * env,ERL_NIF_TERM opts,char ** netns)16373 BOOLEAN_T esock_open4_get_netns(ErlNifEnv* env, ERL_NIF_TERM opts, char** netns)
16374 {
16375     ERL_NIF_TERM val;
16376     ErlNifBinary bin;
16377     char*        buf;
16378 
16379     /* The currently only supported extra option is: netns */
16380     if (!GET_MAP_VAL(env, opts, atom_netns, &val)) {
16381         *netns = NULL; // Just in case...
16382         return FALSE;
16383     }
16384 
16385     /* The value should be a binary file name */
16386     if (! enif_inspect_binary(env, val, &bin)) {
16387         *netns = NULL; // Just in case...
16388         return FALSE;
16389     }
16390 
16391     ESOCK_ASSERT( (buf = MALLOC(bin.size+1)) != NULL );
16392 
16393     sys_memcpy(buf, bin.data, bin.size);
16394     buf[bin.size] = '\0';
16395     *netns = buf;
16396     return TRUE;
16397 }
16398 #endif
16399 #endif // #ifndef __WIN32__
16400 
16401 
16402 /* ehow2how - convert internal (erlang) "shutdown how" to
16403  * (proper) "shutdown how"
16404  */
16405 #ifndef __WIN32__
16406 static
ehow2how(ERL_NIF_TERM ehow,int * how)16407 BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how)
16408 {
16409     int cmp;
16410 
16411     cmp = COMPARE(ehow, atom_read_write);
16412     if (cmp == 0)
16413         *how = SHUT_RDWR;
16414     else if (cmp < 0) {
16415         if (COMPARE(ehow, atom_read) == 0)
16416             *how = SHUT_RD;
16417         else
16418             return FALSE;
16419     } else {
16420         if (COMPARE(ehow, atom_write) == 0)
16421             *how = SHUT_WR;
16422         else
16423             return FALSE;
16424     }
16425 
16426     return TRUE;
16427 }
16428 #endif // #ifndef __WIN32__
16429 
16430 
16431 
16432 #ifndef __WIN32__
16433 #ifdef HAS_AF_LOCAL
16434 /* strnlen doesn't exist everywhere */
16435 /*
16436 static
16437 size_t my_strnlen(const char *s, size_t maxlen)
16438 {
16439     size_t i = 0;
16440     while (i < maxlen && s[i] != '\0')
16441         i++;
16442     return i;
16443 }
16444 */
16445 #endif
16446 #endif // #ifndef __WIN32__
16447 
16448 
16449 
16450 
16451 /* ===========================================================================
16452  *
16453  *                   Socket Registry message functions
16454  *
16455  * ===========================================================================
16456  */
16457 
16458 /* Send a (socket) add message to the socket registry process.
16459  * We know that this process *is* alive since the VM would
16460  * terminate otherwise, so there is no need to test if
16461  * the sending fails.
16462  */
16463 #ifndef __WIN32__
16464 static
esock_send_reg_add_msg(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef)16465 void esock_send_reg_add_msg(ErlNifEnv*       env,
16466                             ESockDescriptor* descP,
16467                             ERL_NIF_TERM     sockRef)
16468 {
16469     ERL_NIF_TERM msg = mk_reg_add_msg(env, sockRef);
16470 
16471     if (! esock_send_msg(env, &data.regPid, msg, NULL)) {
16472         SSDBG( descP,
16473                ("SOCKET",
16474                 "esock_send_reg_add_msg(%T) {%d} failed ->"
16475                 "\r\n   regPid: %T"
16476                 "\r\n",
16477                 sockRef, descP->sock, MKPID(env, &data.regPid)) );
16478     }
16479 }
16480 #endif // #ifndef __WIN32__
16481 
16482 
16483 
16484 /* Send a (socket) del message to the socket registry process.
16485  * We know that this process *is* alive since the VM would
16486  * terminate otherwise, so there is no need to test if
16487  * the sending fails.
16488  */
16489 #ifndef __WIN32__
16490 static
esock_send_reg_del_msg(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef)16491 void esock_send_reg_del_msg(ErlNifEnv*   env,
16492                             ESockDescriptor* descP,
16493                             ERL_NIF_TERM sockRef)
16494 {
16495     ERL_NIF_TERM msg = mk_reg_del_msg(env, sockRef);
16496 
16497     if (! esock_send_msg(env, &data.regPid, msg, NULL)) {
16498         SSDBG( descP,
16499                ("SOCKET",
16500                 "esock_send_reg_del_msg(%T) {%d} failed ->"
16501                 "\r\n   regPid: %T"
16502                 "\r\n",
16503                 sockRef, descP->sock, MKPID(env, &data.regPid)) );
16504     }
16505 }
16506 #endif // #ifndef __WIN32__
16507 
16508 
16509 
16510 
16511 /* ===========================================================================
16512  *
16513  *                   Socket user message functions
16514  *
16515  * ===========================================================================
16516  */
16517 
16518 /* Send an counter wrap message to the controlling process:
16519  * A message in the form:
16520  *
16521  *     {'$socket', Socket, counter_wrap, Counter :: atom()}
16522  *
16523  * This message will only be sent if the iow (Inform On Wrap) is TRUE.
16524  */
16525 #ifndef __WIN32__
16526 static
esock_send_wrap_msg(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ERL_NIF_TERM cnt)16527 void esock_send_wrap_msg(ErlNifEnv*       env,
16528                          ESockDescriptor* descP,
16529                          ERL_NIF_TERM     sockRef,
16530                          ERL_NIF_TERM     cnt)
16531 {
16532     ERL_NIF_TERM msg = mk_wrap_msg(env, sockRef, cnt);
16533 
16534     if (! esock_send_msg(env, &descP->ctrlPid, msg, NULL)) {
16535         SSDBG( descP,
16536                ("SOCKET",
16537                 "esock_send_wrap_msg(%T) {%d} failed ->"
16538                 "\r\n   ctrlPid: %T"
16539                 "\r\n   cnt:     %T"
16540                 "\r\n",
16541                 sockRef, descP->sock, MKPID(env, &descP->ctrlPid), cnt) );
16542     }
16543 }
16544 #endif // #ifndef __WIN32__
16545 
16546 
16547 /* Send an close message to the specified process:
16548  * A message in the form:
16549  *
16550  *     {'$socket', Socket, close, CloseRef}
16551  *
16552  * This message is for processes that is waiting in the
16553  * erlang API (close-) function for the socket to be "closed"
16554  * (actually that the 'stop' callback function has been called).
16555  */
16556 #ifndef __WIN32__
16557 static
esock_send_close_msg(ErlNifEnv * env,ESockDescriptor * descP,ErlNifPid * pid)16558 void esock_send_close_msg(ErlNifEnv*       env,
16559                           ESockDescriptor* descP,
16560                           ErlNifPid*       pid)
16561 {
16562     ERL_NIF_TERM sockRef, msg;
16563 
16564     sockRef = enif_make_resource(descP->closeEnv, descP);
16565     msg     = mk_close_msg(descP->closeEnv, sockRef, descP->closeRef);
16566 
16567     if (! esock_send_msg(env, pid, msg, descP->closeEnv)) {
16568         SSDBG( descP,
16569                ("SOCKET",
16570                 "esock_send_close_msg(%T) {%d} failed ->"
16571                 "\r\n   pid:      %T"
16572                 "\r\n   closeRef: %T"
16573                 "\r\n",
16574                 sockRef, descP->sock, MKPID(env, pid), descP->closeRef) );
16575     }
16576 }
16577 #ifdef HAVE_SENDFILE
16578 static void
esock_send_sendfile_deferred_close_msg(ErlNifEnv * env,ESockDescriptor * descP)16579 esock_send_sendfile_deferred_close_msg(ErlNifEnv*       env,
16580                                        ESockDescriptor* descP)
16581 {
16582     ERL_NIF_TERM sockRef, msg;
16583     ErlNifPid   *pid;
16584 
16585     pid = &data.regPid;
16586     sockRef = enif_make_resource(env, descP);
16587     msg = mk_reg_msg(env, atom_sendfile_deferred_close, sockRef);
16588 
16589     /* If this send should fail we have leaked a file descriptor
16590      * (intolerable), and if we try to close it here, on a regular
16591      * scheduler, it might hang "forever" due to e.g NFS
16592      * (out of the question), so terminating the VM
16593      * is the only viable option
16594      */
16595     ESOCK_ASSERT( esock_send_msg(env, pid, msg, NULL) );
16596 }
16597 #endif // #ifdef HAVE_SENDFILE
16598 #endif // #ifndef __WIN32__
16599 
16600 
16601 /* Send an abort message to the specified process:
16602  * A message in the form:
16603  *
16604  *     {'$socket', Socket, abort, {RecvRef, Reason}}
16605  *
16606  * This message is for processes that is waiting in the
16607  * erlang API functions for a select message.
16608  */
16609 #ifndef __WIN32__
16610 static
esock_send_abort_msg(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ESockRequestor * reqP,ERL_NIF_TERM reason)16611 void esock_send_abort_msg(ErlNifEnv*       env,
16612                           ESockDescriptor* descP,
16613                           ERL_NIF_TERM     sockRef,
16614                           ESockRequestor*  reqP,
16615                           ERL_NIF_TERM     reason)
16616 
16617 
16618 
16619 
16620 {
16621     ERL_NIF_TERM msg;
16622 
16623     msg =
16624         mk_abort_msg(reqP->env,
16625                      /* sockRef not in env so copy */
16626                      CP_TERM(reqP->env, sockRef), reqP->ref, reason);
16627 
16628     if (! esock_send_msg(env, &reqP->pid, msg, reqP->env)) {
16629         SSDBG( descP,
16630                ("SOCKET",
16631                 "esock_send_abort_msg(%T) {%d} failed ->"
16632                 "\r\n   pid: %T"
16633                 "\r\n",
16634                 sockRef, descP->sock, MKPID(env, &reqP->pid)) );
16635     }
16636     reqP->env = NULL;
16637 }
16638 #endif // #ifndef __WIN32__
16639 
16640 
16641 /* Send a message to the specified process.
16642  */
16643 #ifndef __WIN32__
16644 static
esock_send_msg(ErlNifEnv * env,ErlNifPid * pid,ERL_NIF_TERM msg,ErlNifEnv * msgEnv)16645 BOOLEAN_T esock_send_msg(ErlNifEnv*   env,
16646                          ErlNifPid*   pid,
16647                          ERL_NIF_TERM msg,
16648                          ErlNifEnv*   msgEnv)
16649 {
16650     int res = enif_send(env, pid, msgEnv, msg);
16651     esock_free_env("esock_msg_send - msg-env", msgEnv);
16652 
16653     return !!res;
16654 }
16655 #endif // #ifndef __WIN32__
16656 
16657 
16658 
16659 /* *** mk_reg_add_msg ***
16660  *
16661  * Construct a socket add message for the socket registry.
16662  *
16663  *         {'$socket', add, Socket}
16664  *
16665  */
16666 #ifndef __WIN32__
16667 static
mk_reg_add_msg(ErlNifEnv * env,ERL_NIF_TERM sockRef)16668 ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv*   env,
16669                             ERL_NIF_TERM sockRef)
16670 {
16671     return mk_reg_msg(env, atom_add, sockRef);
16672 }
16673 #endif // #ifndef __WIN32__
16674 
16675 
16676 /* *** mk_reg_del_msg ***
16677  *
16678  * Construct a socket del message for the socket registry.
16679  *
16680  *         {'$socket', del, Socket}
16681  *
16682  */
16683 #ifndef __WIN32__
16684 static
mk_reg_del_msg(ErlNifEnv * env,ERL_NIF_TERM sockRef)16685 ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv*   env,
16686                             ERL_NIF_TERM sockRef)
16687 {
16688     return mk_reg_msg(env, atom_del, sockRef);
16689 }
16690 #endif // #ifndef __WIN32__
16691 
16692 
16693 /* *** mk_reg_msg ***
16694  *
16695  * Construct a general message for the socket registry.
16696  * Tag is (at this time) either the atom 'add' or the atom 'del'.
16697  *
16698  *         {'$socket', Tag, Socket}
16699  *
16700  */
16701 #ifndef __WIN32__
16702 static
mk_reg_msg(ErlNifEnv * env,ERL_NIF_TERM tag,ERL_NIF_TERM sockRef)16703 ERL_NIF_TERM mk_reg_msg(ErlNifEnv*   env,
16704                         ERL_NIF_TERM tag,
16705                         ERL_NIF_TERM sockRef)
16706 {
16707     ERL_NIF_TERM socket = mk_socket(env, sockRef);
16708 
16709     return MKT3(env, esock_atom_socket_tag, tag, socket);
16710 }
16711 #endif // #ifndef __WIN32__
16712 
16713 
16714 /* *** mk_abort_msg ***
16715  *
16716  * Create the abort message, which has the following form:
16717  *
16718  *     {'$socket', Socket, abort, {OpRef, Reason}}
16719  *
16720  * This message is for processes that are waiting in the
16721  * erlang API functions for a select (or this) message.
16722  */
16723 #ifndef __WIN32__
16724 static
mk_abort_msg(ErlNifEnv * env,ERL_NIF_TERM sockRef,ERL_NIF_TERM opRef,ERL_NIF_TERM reason)16725 ERL_NIF_TERM mk_abort_msg(ErlNifEnv*   env,
16726                           ERL_NIF_TERM sockRef,
16727                           ERL_NIF_TERM opRef,
16728                           ERL_NIF_TERM reason)
16729 {
16730     ERL_NIF_TERM info = MKT2(env, opRef, reason);
16731 
16732     return mk_socket_msg(env, sockRef, esock_atom_abort, info);
16733 }
16734 #endif // #ifndef __WIN32__
16735 
16736 
16737 /* *** mk_wrap_msg ***
16738  *
16739  * Construct a counter wrap (socket) message. It has the form:
16740  *
16741  *         {'$socket', Socket, counter_wrap, Counter}
16742  *
16743  */
16744 #ifndef __WIN32__
16745 static
mk_wrap_msg(ErlNifEnv * env,ERL_NIF_TERM sockRef,ERL_NIF_TERM cnt)16746 ERL_NIF_TERM mk_wrap_msg(ErlNifEnv*   env,
16747                          ERL_NIF_TERM sockRef,
16748                          ERL_NIF_TERM cnt)
16749 {
16750     return mk_socket_msg(env, sockRef, atom_counter_wrap, cnt);
16751 }
16752 #endif // #ifndef __WIN32__
16753 
16754 
16755 /* *** mk_close_msg ***
16756  *
16757  * Construct a close (socket) message. It has the form:
16758  *
16759  *         {'$socket', Socket, close, closeRef}
16760  *
16761  */
16762 #ifndef __WIN32__
16763 static
mk_close_msg(ErlNifEnv * env,ERL_NIF_TERM sockRef,ERL_NIF_TERM closeRef)16764 ERL_NIF_TERM mk_close_msg(ErlNifEnv*   env,
16765                           ERL_NIF_TERM sockRef,
16766                           ERL_NIF_TERM closeRef)
16767 {
16768     return mk_socket_msg(env, sockRef, esock_atom_close, closeRef);
16769 }
16770 #endif // #ifndef __WIN32__
16771 
16772 
16773 /* *** mk_select_msg ***
16774  *
16775  * Construct a select (socket) message. It has the form:
16776  *
16777  *         {'$socket', Socket, select, selectRef}
16778  *
16779  */
16780 #ifndef __WIN32__
16781 static
mk_select_msg(ErlNifEnv * env,ERL_NIF_TERM sockRef,ERL_NIF_TERM selectRef)16782 ERL_NIF_TERM mk_select_msg(ErlNifEnv*   env,
16783                            ERL_NIF_TERM sockRef,
16784                            ERL_NIF_TERM selectRef)
16785 {
16786     return mk_socket_msg(env, sockRef, atom_select, selectRef);
16787 }
16788 #endif // #ifndef __WIN32__
16789 
16790 
16791 /* *** mk_socket_msg ***
16792  *
16793  * Construct the socket message:
16794  *
16795  *         {'$socket', Socket, Tag, Info}
16796  *
16797  * Socket :: socket:socket()
16798  * Tag    :: atom()
16799  * Info   :: term()
16800  *
16801  */
16802 #ifndef __WIN32__
16803 static
mk_socket_msg(ErlNifEnv * env,ERL_NIF_TERM sockRef,ERL_NIF_TERM tag,ERL_NIF_TERM info)16804 ERL_NIF_TERM mk_socket_msg(ErlNifEnv*   env,
16805                            ERL_NIF_TERM sockRef,
16806                            ERL_NIF_TERM tag,
16807                            ERL_NIF_TERM info)
16808 {
16809     ERL_NIF_TERM socket = mk_socket(env, sockRef);
16810 
16811     return MKT4(env, esock_atom_socket_tag, socket, tag, info);
16812 }
16813 #endif // #ifndef __WIN32__
16814 
16815 
16816 /* *** mk_socket ***
16817  *
16818  * Simple utility function that construct the socket tuple:
16819  *
16820  *     socket:socket() :: {'$socket', SockRef :: reference()}
16821  */
16822 #ifndef __WIN32__
16823 static
mk_socket(ErlNifEnv * env,ERL_NIF_TERM sockRef)16824 ERL_NIF_TERM mk_socket(ErlNifEnv*   env,
16825                        ERL_NIF_TERM sockRef)
16826 {
16827     return MKT2(env, esock_atom_socket_tag, sockRef);
16828 }
16829 #endif // #ifndef __WIN32__
16830 
16831 
16832 /* ----------------------------------------------------------------------
16833  *  S e l e c t   W r a p p e r   F u n c t i o n s
16834  * ----------------------------------------------------------------------
16835  */
16836 
16837 /* *** esock_select_read ***
16838  *
16839  * Perform a read select. When the select is triggered, a 'select'
16840  * message (see mk_select_msg) will be sent.
16841  *
16842  * There are two ways to handle the select message:
16843  * 1) Create "your own" environment and create the message using it
16844  *    and then pass it on to the select function.
16845  * 2) Or, to create the message using any available environment,
16846  *    and then pass a NULL pointer to the select function.
16847  *    This will have the effect that the select function will
16848  *    create its own environment and then copy the message to it.
16849  * We choose the second alternative.
16850  */
16851 #ifndef __WIN32__
16852 static
esock_select_read(ErlNifEnv * env,ErlNifEvent event,void * obj,const ErlNifPid * pidP,ERL_NIF_TERM sockRef,ERL_NIF_TERM selectRef)16853 int esock_select_read(ErlNifEnv*       env,
16854                       ErlNifEvent      event,     // The file descriptor
16855                       void*            obj,       // The socket descriptor object
16856                       const ErlNifPid* pidP,      // Destination
16857                       ERL_NIF_TERM     sockRef,   // Socket
16858                       ERL_NIF_TERM     selectRef) // "ID" of the operation
16859 {
16860     ERL_NIF_TERM selectMsg = mk_select_msg(env, sockRef, selectRef);
16861 
16862     return enif_select_read(env, event, obj, pidP, selectMsg, NULL);
16863 
16864 }
16865 #endif // #ifndef __WIN32__
16866 
16867 
16868 /* *** esock_select_write ***
16869  *
16870  * Perform a write select. When the select is triggered, a 'select'
16871  * message (see mk_select_msg) will be sent.
16872  * The sockRef is copied to the msgEnv when the socket message is created,
16873  * so no need to do that here, but the selectRef needs to be copied.
16874  */
16875 #ifndef __WIN32__
16876 static
esock_select_write(ErlNifEnv * env,ErlNifEvent event,void * obj,const ErlNifPid * pidP,ERL_NIF_TERM sockRef,ERL_NIF_TERM selectRef)16877 int esock_select_write(ErlNifEnv*       env,
16878                        ErlNifEvent      event,     // The file descriptor
16879                        void*            obj,       // The socket descriptor
16880                        const ErlNifPid* pidP,       // Destination
16881                        ERL_NIF_TERM     sockRef,   // Socket
16882                        ERL_NIF_TERM     selectRef) // "ID" of the operation
16883 {
16884     ERL_NIF_TERM selectMsg = mk_select_msg(env, sockRef, selectRef);
16885 
16886     return enif_select_write(env, event, obj, pidP, selectMsg, NULL);
16887 }
16888 #endif // #ifndef __WIN32__
16889 
16890 
16891 /* *** esock_select_stop ***
16892  *
16893  * WARNING: enif_select may call esock_stop directly
16894  * in which case deadlock is avoided by esock_stop that checks
16895  * if it got a direct call and then does not lock readMtx and writeMtx.
16896  *
16897  * So readMtx and writeMtx are supposed to be locked
16898  * when this function is called.
16899  */
16900 #ifndef __WIN32__
16901 static
esock_select_stop(ErlNifEnv * env,ErlNifEvent event,void * obj)16902 int esock_select_stop(ErlNifEnv*  env,
16903                       ErlNifEvent event,
16904                       void*       obj)
16905 {
16906     return enif_select(env, event, (ERL_NIF_SELECT_STOP), obj, NULL,
16907                        esock_atom_undefined);
16908 }
16909 #endif // #ifndef __WIN32__
16910 
16911 #ifndef __WIN32__
16912 static
esock_select_cancel(ErlNifEnv * env,ErlNifEvent event,enum ErlNifSelectFlags mode,void * obj)16913 int esock_select_cancel(ErlNifEnv*             env,
16914                         ErlNifEvent            event,
16915                         enum ErlNifSelectFlags mode,
16916                         void*                  obj)
16917 {
16918     return enif_select(env, event, (ERL_NIF_SELECT_CANCEL | mode), obj, NULL,
16919                        esock_atom_undefined);
16920 }
16921 #endif // #ifndef __WIN32__
16922 
16923 
16924 /* ----------------------------------------------------------------------
16925  *  A c t i v a t e   N e x t   ( o p e r a t o r )   F u n c t i o n s
16926  * ----------------------------------------------------------------------
16927  */
16928 
16929 /* *** activate_next_acceptor ***
16930  * *** activate_next_writer   ***
16931  * *** activate_next_reader   ***
16932  *
16933  * This functions pops the requestors queue and then selects until it
16934  * manages to successfully activate a requestor or the queue is empty.
16935  * Return value indicates if a new requestor was activated or not.
16936  */
16937 
16938 #ifndef __WIN32__
16939 
16940 #define ACTIVATE_NEXT_FUNCS                                               \
16941     ACTIVATE_NEXT_FUNC_DECL(acceptor, read,  currentAcceptor, acceptorsQ) \
16942     ACTIVATE_NEXT_FUNC_DECL(writer,   write, currentWriter,   writersQ)   \
16943     ACTIVATE_NEXT_FUNC_DECL(reader,   read,  currentReader,   readersQ)
16944 
16945 #define ACTIVATE_NEXT_FUNC_DECL(F, S, R, Q)                  \
16946     static                                                   \
16947     BOOLEAN_T activate_next_##F(ErlNifEnv*       env,        \
16948                                 ESockDescriptor* descP,      \
16949                                 ERL_NIF_TERM     sockRef)    \
16950     {                                                        \
16951         BOOLEAN_T          popped, activated;                \
16952         int                sres;                             \
16953         ERL_NIF_TERM       reason;                           \
16954         ESockRequestor*    reqP = &descP->R;                 \
16955         ESockRequestQueue* q    = &descP->Q;                 \
16956                                                              \
16957         popped = FALSE;                                      \
16958         do {                                                 \
16959                                                              \
16960             if (requestor_pop(q, reqP)) {                    \
16961                                                              \
16962                 /* There was another one */                  \
16963                                                              \
16964                 SSDBG( descP,                                           \
16965                        ("SOCKET",                                       \
16966                         "activate_next_" #F "(%T) {%d} ->"              \
16967                         " new (active) requestor: "                     \
16968                         "\r\n   pid: %T"                                \
16969                         "\r\n   ref: %T"                                \
16970                         "\r\n", sockRef, descP->sock,                   \
16971                         reqP->pid, reqP->ref) );                        \
16972                                                                         \
16973                 /* We need to copy req ref to 'env' */                  \
16974                 if ((sres =                                             \
16975                      esock_select_##S(env, descP->sock, descP,          \
16976                                       &reqP->pid, sockRef,              \
16977                                       CP_TERM(env, reqP->ref))) < 0) {  \
16978                                                                         \
16979                     /* We need to inform this process, reqP->pid,  */   \
16980                     /* that we failed to select, so we don't leave */   \
16981                     /* it hanging.                                 */   \
16982                     /* => send abort                               */   \
16983                                                                         \
16984                     reason = MKT2(env,                                  \
16985                                   esock_atom_select_failed,             \
16986                                   MKI(env, sres));                      \
16987                     esock_send_abort_msg(env, descP, sockRef,           \
16988                                          reqP, reason);                 \
16989                                                                         \
16990                 } else {                                                \
16991                                                                         \
16992                     descP->S##State |= ESOCK_STATE_SELECTED;            \
16993                                                                         \
16994                     /* Success: New requestor selected */               \
16995                     popped    = TRUE;                                   \
16996                     activated = TRUE;                                   \
16997                                                                         \
16998                 }                                                       \
16999                                                                         \
17000             } else {                                                    \
17001                                                                         \
17002                 SSDBG( descP,                                           \
17003                        ("SOCKET",                                       \
17004                         "activate_next_" #F "(%T) {%d} ->"              \
17005                         " no more requestors\r\n",                      \
17006                         sockRef, descP->sock) );                        \
17007                                                                         \
17008                 popped    = TRUE;                                       \
17009                 activated = FALSE;                                      \
17010             }                                                           \
17011                                                                         \
17012         } while (!popped);                                              \
17013                                                                         \
17014         SSDBG( descP,                                                   \
17015                ("SOCKET", "activate_next_" #F "(%T) {%d} -> "           \
17016                 "done with %s\r\n",                                     \
17017                 sockRef, descP->sock, B2S(activated)) );                \
17018                                                                         \
17019         return activated;                                               \
17020     }
17021 ACTIVATE_NEXT_FUNCS
17022 #undef ACTIVATE_NEXT_FUNC_DECL
17023 
17024 #endif // #ifndef __WIN32__
17025 
17026 
17027 /* ----------------------------------------------------------------------
17028  *  R e q u e s t o r   Q u e u e   F u n c t i o n s
17029  * ----------------------------------------------------------------------
17030  *
17031  * Since each of these functions (search4pid, push, pop and unqueue
17032  * are virtually identical for acceptors, writers and readers,
17033  * we make use of set of declaration macros.
17034  */
17035 
17036 #ifndef __WIN32__
17037 
17038 /* *** acceptor_search4pid ***
17039  * *** writer_search4pid   ***
17040  * *** reader_search4pid   ***
17041  *
17042  * Search for a pid in the requestor (acceptor, writer, or reader) queue.
17043  *
17044  */
17045 
17046 #define REQ_SEARCH4PID_FUNCS                       \
17047     REQ_SEARCH4PID_FUNC_DECL(acceptor, acceptorsQ) \
17048     REQ_SEARCH4PID_FUNC_DECL(writer,   writersQ)   \
17049     REQ_SEARCH4PID_FUNC_DECL(reader,   readersQ)
17050 
17051 #define REQ_SEARCH4PID_FUNC_DECL(F, Q)                 \
17052     static                                             \
17053     BOOLEAN_T F##_search4pid(ErlNifEnv*       env,     \
17054                              ESockDescriptor* descP,   \
17055                              ErlNifPid*       pid)     \
17056     {                                                  \
17057         return qsearch4pid(env, &descP->Q, pid);       \
17058     }
17059 REQ_SEARCH4PID_FUNCS
17060 #undef REQ_SEARCH4PID_FUNC_DECL
17061 
17062 #endif // #ifndef __WIN32__
17063 
17064 
17065 
17066 /* *** acceptor_push ***
17067  * *** writer_push   ***
17068  * *** reader_push   ***
17069  *
17070  * Push a requestor (acceptor, writer, or reader) onto its queue.
17071  * This happens when we already have a current request (of its type).
17072  *
17073  */
17074 
17075 #ifndef __WIN32__
17076 
17077 #define REQ_PUSH_FUNCS                       \
17078     REQ_PUSH_FUNC_DECL(acceptor, acceptorsQ) \
17079     REQ_PUSH_FUNC_DECL(writer,   writersQ)   \
17080     REQ_PUSH_FUNC_DECL(reader,   readersQ)
17081 
17082 #define REQ_PUSH_FUNC_DECL(F, Q)                                        \
17083     static                                                              \
17084     void F##_push(ErlNifEnv*       env,                                 \
17085                   ESockDescriptor* descP,                               \
17086                   ErlNifPid        pid, /* self() */                    \
17087                   ERL_NIF_TERM     ref)                                 \
17088     {                                                                   \
17089         ESockRequestQueueElement *e;                                    \
17090         ESockRequestor           *reqP;                                 \
17091                                                                         \
17092         ESOCK_ASSERT( (e = MALLOC(sizeof(ESockRequestQueueElement)))    \
17093                       != NULL );                                        \
17094         reqP = &e->data;                                                \
17095         reqP->pid = pid;                                                \
17096         ESOCK_ASSERT( MONP("reader_push -> " #F " request",             \
17097                            env, descP, &pid, &reqP->mon) == 0 );        \
17098         reqP->env = esock_alloc_env(#F "_push");                        \
17099         reqP->ref = CP_TERM(reqP->env, ref);                            \
17100                                                                         \
17101         qpush(&descP->Q, e);                                            \
17102     }
17103 REQ_PUSH_FUNCS
17104 #undef REQ_PUSH_FUNC_DECL
17105 
17106 #endif // #ifndef __WIN32__
17107 
17108 
17109 
17110 /* *** acceptor_pop ***
17111  * *** writer_pop   ***
17112  * *** reader_pop   ***
17113  *
17114  * Pop a requestor (acceptor, writer, or reader) from its queue.
17115  *
17116  */
17117 
17118 #ifndef __WIN32__
17119 
17120 #define REQ_POP_FUNCS                       \
17121     REQ_POP_FUNC_DECL(acceptor, acceptorsQ) \
17122     REQ_POP_FUNC_DECL(writer,   writersQ)   \
17123     REQ_POP_FUNC_DECL(reader,   readersQ)
17124 
17125 #define REQ_POP_FUNC_DECL(F, Q)                \
17126     static                                     \
17127     BOOLEAN_T F##_pop(ErlNifEnv*       env,    \
17128                       ESockDescriptor* descP,  \
17129                       ESockRequestor*  reqP)   \
17130     {                                          \
17131         return requestor_pop(&descP->Q, reqP); \
17132     }
17133 REQ_POP_FUNCS
17134 #undef REQ_POP_FUNC_DECL
17135 
17136 #endif // #ifndef __WIN32__
17137 
17138 
17139 
17140 /* *** acceptor_unqueue ***
17141  * *** writer_unqueue   ***
17142  * *** reader_unqueue   ***
17143  *
17144  * Remove a requestor (acceptor, writer, or reader) from its queue.
17145  *
17146  */
17147 
17148 #ifndef __WIN32__
17149 
17150 #define REQ_UNQUEUE_FUNCS                       \
17151     REQ_UNQUEUE_FUNC_DECL(acceptor, acceptorsQ) \
17152     REQ_UNQUEUE_FUNC_DECL(writer,   writersQ)   \
17153     REQ_UNQUEUE_FUNC_DECL(reader,   readersQ)
17154 
17155 #define REQ_UNQUEUE_FUNC_DECL(F, Q)                             \
17156     static                                                      \
17157     BOOLEAN_T F##_unqueue(ErlNifEnv*       env,                 \
17158                           ESockDescriptor* descP,               \
17159                           ERL_NIF_TERM*    refP,                \
17160                           const ErlNifPid* pidP)                \
17161     {                                                           \
17162         return qunqueue(env, descP, "qunqueue -> waiting " #F,  \
17163                         &descP->Q, refP, pidP);                 \
17164     }
17165 REQ_UNQUEUE_FUNCS
17166 #undef REQ_UNQUEUE_FUNC_DECL
17167 
17168 #endif // #ifndef __WIN32__
17169 
17170 
17171 
17172 /* *** requestor pop ***
17173  *
17174  * Pop an requestor from its queue.
17175  */
17176 
17177 #ifndef __WIN32__
17178 
17179 static
requestor_pop(ESockRequestQueue * q,ESockRequestor * reqP)17180 BOOLEAN_T requestor_pop(ESockRequestQueue* q,
17181                         ESockRequestor*    reqP)
17182 {
17183     ESockRequestQueueElement* e = qpop(q);
17184 
17185     esock_free_env("requestor_pop", reqP->env);
17186 
17187     if (e != NULL) {
17188         reqP->pid = e->data.pid;
17189         reqP->mon = e->data.mon;
17190         reqP->env = e->data.env;
17191         reqP->ref = e->data.ref;
17192         FREE(e);
17193         return TRUE;
17194     } else {
17195         /* Queue was empty */
17196         requestor_init(reqP);
17197         return FALSE;
17198     }
17199 
17200 }
17201 
requestor_init(ESockRequestor * reqP)17202 static void requestor_init(ESockRequestor* reqP) {
17203     enif_set_pid_undefined(&reqP->pid);
17204     MON_INIT(&reqP->mon);
17205     reqP->env = NULL;
17206     reqP->ref = esock_atom_undefined;
17207 }
17208 
requestor_release(const char * slogan,ErlNifEnv * env,ESockDescriptor * descP,ESockRequestor * reqP)17209 static void requestor_release(const char*      slogan,
17210                               ErlNifEnv*       env,
17211                               ESockDescriptor* descP,
17212                               ESockRequestor*  reqP) {
17213 
17214     enif_set_pid_undefined(&reqP->pid);
17215     (void) DEMONP(slogan, env, descP, &reqP->mon);
17216     esock_free_env(slogan, reqP->env);
17217     reqP->env = NULL;
17218     reqP->ref = esock_atom_undefined;
17219 }
17220 #endif // #ifndef __WIN32__
17221 
17222 
17223 
17224 #ifndef __WIN32__
17225 
17226 static
qsearch4pid(ErlNifEnv * env,ESockRequestQueue * q,ErlNifPid * pid)17227 BOOLEAN_T qsearch4pid(ErlNifEnv*         env,
17228                       ESockRequestQueue* q,
17229                       ErlNifPid*         pid)
17230 {
17231     ESockRequestQueueElement* tmp = q->first;
17232 
17233     while (tmp != NULL) {
17234         if (COMPARE_PIDS(&tmp->data.pid, pid) == 0)
17235             return TRUE;
17236         else
17237             tmp = tmp->nextP;
17238     }
17239 
17240     return FALSE;
17241 }
17242 
17243 static
qpush(ESockRequestQueue * q,ESockRequestQueueElement * e)17244 void qpush(ESockRequestQueue*        q,
17245            ESockRequestQueueElement* e)
17246 {
17247     if (q->first != NULL) {
17248         q->last->nextP = e;
17249         q->last        = e;
17250         e->nextP       = NULL;
17251     } else {
17252         q->first = e;
17253         q->last  = e;
17254         e->nextP = NULL;
17255     }
17256 }
17257 
17258 static
qpop(ESockRequestQueue * q)17259 ESockRequestQueueElement* qpop(ESockRequestQueue* q)
17260 {
17261     ESockRequestQueueElement* e = q->first;
17262 
17263     if (e != NULL) {
17264         /* Atleast one element in the queue */
17265         if (e == q->last) {
17266             /* Only one element in the queue */
17267             q->first = q->last = NULL;
17268         } else {
17269             /* More than one element in the queue */
17270             q->first = e->nextP;
17271         }
17272     }
17273 
17274     return e;
17275 }
17276 #endif // #ifndef __WIN32__
17277 
17278 
17279 
17280 #ifndef __WIN32__
17281 static
qunqueue(ErlNifEnv * env,ESockDescriptor * descP,const char * slogan,ESockRequestQueue * q,ERL_NIF_TERM * refP,const ErlNifPid * pidP)17282 BOOLEAN_T qunqueue(ErlNifEnv*         env,
17283                    ESockDescriptor*   descP,
17284                    const char*        slogan,
17285                    ESockRequestQueue* q,
17286                    ERL_NIF_TERM*      refP,
17287                    const ErlNifPid*   pidP)
17288 {
17289     ESockRequestQueueElement* e = q->first;
17290     ESockRequestQueueElement* p = NULL;
17291 
17292     /* Check if it was one of the waiting acceptor processes */
17293     while (e != NULL) {
17294         if (COMPARE_PIDS(&e->data.pid, pidP) == 0) {
17295             if ((refP != NULL) && (COMPARE(e->data.ref, *refP) != 0))
17296                 return FALSE;
17297 
17298             /* We have a match */
17299 
17300             (void) DEMONP(slogan, env, descP, &e->data.mon);
17301 
17302             if (p != NULL) {
17303                 /* Not the first, but could be the last */
17304                 if (q->last == e) {
17305                     q->last  = p;
17306                     p->nextP = NULL;
17307                 } else {
17308                     p->nextP = e->nextP;
17309                 }
17310 
17311             } else {
17312                 /* The first and could also be the last */
17313                 if (q->last == e) {
17314                     q->last  = NULL;
17315                     q->first = NULL;
17316                 } else {
17317                     q->first = e->nextP;
17318                 }
17319             }
17320 
17321             esock_free_env("qunqueue", e->data.env);
17322             FREE(e);
17323 
17324             return TRUE;
17325         }
17326 
17327         /* Try next */
17328         p = e;
17329         e = e->nextP;
17330     }
17331 
17332     return FALSE;
17333 }
17334 #endif // #ifndef __WIN32__
17335 
17336 
17337 
17338 /* ----------------------------------------------------------------------
17339  *  C o u n t e r   F u n c t i o n s
17340  * ----------------------------------------------------------------------
17341  */
17342 
17343 #ifndef __WIN32__
17344 
17345 static
cnt_inc(ESockCounter * cnt,ESockCounter inc)17346 BOOLEAN_T cnt_inc(ESockCounter* cnt, ESockCounter inc)
17347 {
17348     BOOLEAN_T    wrap;
17349     ESockCounter max     = ESOCK_COUNTER_MAX;
17350     ESockCounter current = *cnt;
17351 
17352     if ((max - inc) >= current) {
17353       *cnt += inc;
17354       wrap  = FALSE;
17355     } else {
17356       *cnt = inc - (max - current) - 1;
17357       wrap = TRUE;
17358     }
17359 
17360     return (wrap);
17361 }
17362 
17363 static
cnt_dec(ESockCounter * cnt,ESockCounter dec)17364 void cnt_dec(ESockCounter* cnt, ESockCounter dec)
17365 {
17366     ESockCounter current = *cnt;
17367 
17368     if (dec > current)
17369         *cnt = 0; // The counter cannot be < 0 so this is the best we can do...
17370     else
17371         *cnt -= dec;
17372 
17373     return;
17374 }
17375 
17376 #endif // #ifndef __WIN32__
17377 
17378 
17379 
17380 
17381 /* ----------------------------------------------------------------------
17382  *  M o n i t o r   W r a p p e r   F u n c t i o n s
17383  * ----------------------------------------------------------------------
17384  */
17385 
17386 #ifndef __WIN32__
17387 
17388 static
esock_monitor(const char * slogan,ErlNifEnv * env,ESockDescriptor * descP,const ErlNifPid * pid,ESockMonitor * monP)17389 int esock_monitor(const char*      slogan,
17390                   ErlNifEnv*       env,
17391                   ESockDescriptor* descP,
17392                   const ErlNifPid* pid,
17393                   ESockMonitor*    monP)
17394 {
17395     int res;
17396 
17397     SSDBG( descP, ("SOCKET",
17398                    "esock_monitor {%d} [%T] %s: try monitor\r\n",
17399                    descP->sock, esock_self(env), slogan) );
17400 
17401     res = enif_monitor_process(env, descP, pid, &monP->mon);
17402 
17403     if (res != 0) {
17404         monP->isActive = FALSE;
17405 
17406         SSDBG( descP,
17407                ("SOCKET",
17408                 "esock_monitor {%d} [%T] %s: monitor failed: %d\r\n",
17409                 descP->sock, esock_self(env), slogan, res) );
17410     } else {
17411         monP->isActive = TRUE;
17412 
17413         SSDBG( descP,
17414                ("SOCKET",
17415                 "esock_monitor {%d} [%T] %s: monitor ok: %T\r\n",
17416                 descP->sock, esock_self(env), slogan,
17417                 esock_make_monitor_term(env, monP)) );
17418     }
17419 
17420     return res;
17421 }
17422 
17423 static
esock_demonitor(const char * slogan,ErlNifEnv * env,ESockDescriptor * descP,ESockMonitor * monP)17424 int esock_demonitor(const char*      slogan,
17425                     ErlNifEnv*       env,
17426                     ESockDescriptor* descP,
17427                     ESockMonitor*    monP)
17428 {
17429     int res;
17430 
17431     if (! monP->isActive)
17432         return 1;
17433 
17434     SSDBG( descP, ("SOCKET",
17435                    "esock_demonitor {%d} [%T] %s: try demonitor %T\r\n",
17436                    descP->sock, esock_self(env), slogan,
17437                    esock_make_monitor_term(env, monP)) );
17438 
17439     res = enif_demonitor_process(env, descP, &monP->mon);
17440     esock_monitor_init(monP);
17441 
17442     if (res != 0) {
17443         SSDBG( descP,
17444                ("SOCKET",
17445                 "esock_demonitor {%d}[%T] %s: demonitor failed: %d\r\n",
17446                 descP->sock, esock_self(env), slogan, res) );
17447     }
17448 
17449     return res;
17450 }
17451 
17452 static
esock_monitor_init(ESockMonitor * monP)17453 void esock_monitor_init(ESockMonitor* monP)
17454 {
17455     monP->isActive = FALSE;
17456 }
17457 
17458 static
esock_make_monitor_term(ErlNifEnv * env,const ESockMonitor * monP)17459 ERL_NIF_TERM esock_make_monitor_term(ErlNifEnv* env, const ESockMonitor* monP)
17460 {
17461     if (monP->isActive)
17462         return enif_make_monitor_term(env, &monP->mon);
17463     else
17464         return esock_atom_undefined;
17465 }
17466 
esock_monitor_eq(const ESockMonitor * monP,const ErlNifMonitor * mon)17467 static BOOLEAN_T esock_monitor_eq(const ESockMonitor* monP,
17468                                   const ErlNifMonitor* mon) {
17469     if (monP->isActive)
17470         return enif_compare_monitors(&monP->mon, mon) == 0;
17471     else
17472         return FALSE;
17473 }
17474 
17475 #endif // #ifndef __WIN32__
17476 
17477 
17478 
17479 /* ----------------------------------------------------------------------
17480  *  C a l l b a c k   F u n c t i o n s
17481  * ----------------------------------------------------------------------
17482  */
17483 
17484 
17485 #ifndef __WIN32__
free_request_queue(ESockRequestQueue * q)17486 static void free_request_queue(ESockRequestQueue* q)
17487 {
17488     while (q->first) {
17489         ESockRequestQueueElement* free_me = q->first;
17490         q->first = free_me->nextP;
17491         esock_free_env("dtor", free_me->data.env);
17492         FREE(free_me);
17493     }
17494 }
17495 #endif // #ifndef __WIN32__
17496 
17497 /* =========================================================================
17498  * esock_dtor - Callback function for resource destructor
17499  *
17500  */
17501 static
esock_dtor(ErlNifEnv * env,void * obj)17502 void esock_dtor(ErlNifEnv* env, void* obj)
17503 {
17504 #ifndef __WIN32__
17505   ESockDescriptor* descP = (ESockDescriptor*) obj;
17506 
17507   MLOCK(descP->readMtx);
17508   MLOCK(descP->writeMtx);
17509 
17510   SGDBG( ("SOCKET", "dtor {%d,0x%X}\r\n",
17511           descP->sock, descP->readState | descP->writeState) );
17512 
17513   if (IS_SELECTED(descP)) {
17514       /* We have used the socket in the select machinery,
17515        * so we must have closed it properly to get here
17516        */
17517       ESOCK_ASSERT( IS_CLOSED(descP->readState) );
17518       ESOCK_ASSERT( IS_CLOSED(descP->writeState) );
17519       ESOCK_ASSERT( descP->sock == INVALID_SOCKET );
17520   } else {
17521       /* The socket is only opened, should be safe to close nonblocking */
17522       (void) sock_close(descP->sock);
17523       descP->sock = INVALID_SOCKET;
17524   }
17525 
17526   SGDBG( ("SOCKET", "dtor -> set state and pattern\r\n") );
17527   descP->readState  |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
17528   descP->writeState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED);
17529   descP->pattern     = (ESOCK_DESC_PATTERN_DTOR | ESOCK_STATE_CLOSED);
17530 
17531   esock_free_env("dtor reader", descP->currentReader.env);
17532   descP->currentReader.env = NULL;
17533 
17534   esock_free_env("dtor writer", descP->currentWriter.env);
17535   descP->currentWriter.env = NULL;
17536 
17537   esock_free_env("dtor acceptor", descP->currentAcceptor.env);
17538   descP->currentAcceptor.env = NULL;
17539 
17540   SGDBG( ("SOCKET", "dtor -> try free readers request queue\r\n") );
17541   free_request_queue(&descP->readersQ);
17542 
17543   SGDBG( ("SOCKET", "dtor -> try free writers request queue\r\n") );
17544   free_request_queue(&descP->writersQ);
17545 
17546   SGDBG( ("SOCKET", "dtor -> try free acceptors request queue\r\n") );
17547   free_request_queue(&descP->acceptorsQ);
17548 
17549 #ifdef HAVE_SENDFILE
17550   ESOCK_ASSERT( descP->sendfileHandle == INVALID_HANDLE );
17551   if (descP->sendfileCountersP != NULL) {
17552       FREE(descP->sendfileCountersP);
17553       descP->sendfileCountersP = NULL;
17554   }
17555 #endif
17556 
17557   esock_free_env("dtor close env", descP->closeEnv);
17558   descP->closeEnv = NULL;
17559 
17560   esock_free_env("dtor meta env", descP->meta.env);
17561   descP->meta.env = NULL;
17562 
17563   MUNLOCK(descP->writeMtx);
17564   MUNLOCK(descP->readMtx);
17565 
17566   SGDBG( ("SOCKET", "dtor -> try destroy read mutex\r\n") );
17567   MDESTROY(descP->readMtx);  descP->readMtx  = NULL;
17568 
17569   SGDBG( ("SOCKET", "dtor -> try destroy write mutex\r\n") );
17570   MDESTROY(descP->writeMtx); descP->writeMtx = NULL;
17571 
17572   SGDBG( ("SOCKET", "dtor -> done\r\n") );
17573 #endif // #ifndef __WIN32__
17574 }
17575 
17576 
17577 /* =========================================================================
17578  * esock_stop - Callback function for resource stop
17579  *
17580  * When the socket is stopped, we need to inform:
17581  *
17582  *     * the controlling process
17583  *     * the current writer and any waiting writers
17584  *     * the current reader and any waiting readers
17585  *     * the current acceptor and any waiting acceptor
17586  *
17587  * Also, make sure no process gets the message twice
17588  * (in case it is, for instance, both controlling process
17589  * and a writer).
17590  *
17591  */
17592 static
esock_stop(ErlNifEnv * env,void * obj,ErlNifEvent fd,int is_direct_call)17593 void esock_stop(ErlNifEnv* env, void* obj, ErlNifEvent fd, int is_direct_call)
17594 {
17595 #ifndef __WIN32__
17596     ESockDescriptor* descP = (ESockDescriptor*) obj;
17597 
17598     if (is_direct_call) {
17599         return; // Nothing to do, caller gets ERL_NIF_SELECT_STOP_CALLED
17600     }
17601 
17602     // This is a scheduled call, caller gets ERL_NIF_SELECT_STOP_SCHEDULED
17603     MLOCK(descP->readMtx);
17604     MLOCK(descP->writeMtx);
17605 
17606     SSDBG( descP, ("SOCKET", "esock_stop {%d/%d} -> when %s"
17607                    "\r\n   ctrlPid:      %T"
17608                    "\r\n   closerPid:    %T"
17609                    "\r\ncounters:"
17610                    "\r\n   writePkgCnt:      %lu"
17611                    "\r\n   writePkgMax:      %lu"
17612                    "\r\n   writeByteCnt:     %lu"
17613                    "\r\n   writeTries:       %lu"
17614                    "\r\n   writeWaits:       %lu"
17615                    "\r\n   writeFails:       %lu"
17616                    "\r\n   readPkgCnt:       %lu"
17617                    "\r\n   readPkgMax:       %lu"
17618                    "\r\n   readByteCnt:      %lu"
17619                    "\r\n   readTries:        %lu"
17620                    "\r\n   readWaits:        %lu"
17621                    "\r\n   accSuccess:       %lu"
17622                    "\r\n   accTries:         %lu"
17623                    "\r\n   accWaits:         %lu"
17624                    "\r\n   accFails:         %lu"
17625                    "\r\n",
17626                    descP->sock, fd,
17627                    (is_direct_call) ? "called" : "scheduled",
17628                    descP->ctrlPid,
17629                    descP->closerPid,
17630                    //
17631                    (unsigned long) descP->writePkgCnt,
17632                    (unsigned long) descP->writePkgMax,
17633                    (unsigned long) descP->writeByteCnt,
17634                    (unsigned long) descP->writeTries,
17635                    (unsigned long) descP->writeWaits,
17636                    (unsigned long) descP->writeFails,
17637                    (unsigned long) descP->readPkgCnt,
17638                    (unsigned long) descP->readPkgMax,
17639                    (unsigned long) descP->readByteCnt,
17640                    (unsigned long) descP->readTries,
17641                    (unsigned long) descP->readWaits,
17642 		   //
17643 		   (unsigned long) descP->accSuccess,
17644                    (unsigned long) descP->accTries,
17645                    (unsigned long) descP->accWaits,
17646 		   (unsigned long) descP->accFails) );
17647 
17648 #ifdef HAVE_SENDFILE
17649     if (descP->sendfileCountersP != NULL) {
17650         ESockSendfileCounters *cP = descP->sendfileCountersP;
17651 
17652         SSDBG( descP, ("SOCKET", "esock_stop {%d/%d} ->"
17653                        "\r\nsendfileCounters:"
17654                        "\r\n   cnt:      %lu"
17655                        "\r\n   byteCnt:  %lu"
17656                        "\r\n   fails:    %lu"
17657                        "\r\n   max:      %lu"
17658                        "\r\n   pkg:      %lu"
17659                        "\r\n   pkgMax    %lu"
17660                        "\r\n   tries:    %lu"
17661                        "\r\n   waits:    %lu"
17662                        "\r\n",
17663                        descP->sock, fd,
17664                        (unsigned long) cP->cnt,
17665                        (unsigned long) cP->byteCnt,
17666                        (unsigned long) cP->fails,
17667                        (unsigned long) cP->max,
17668                        (unsigned long) cP->pkg,
17669                        (unsigned long) cP->pkgMax,
17670                        (unsigned long) cP->tries,
17671                        (unsigned long) cP->waits) );
17672     }
17673 #endif
17674 
17675     /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
17676      *
17677      *           Inform waiting Closer, or close socket
17678      *
17679      * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
17680      */
17681 
17682     if (! enif_is_pid_undefined(&descP->closerPid)) {
17683         /* We have a waiting closer process after nif_close()
17684          * - send message to trigger nif_finalize_close()
17685          */
17686 
17687         SSDBG( descP,
17688                ("SOCKET",
17689                 "esock_stop {%d/%d} -> send close msg to %T\r\n",
17690                 descP->sock, fd, MKPID(env, &descP->closerPid)) );
17691 
17692         esock_send_close_msg(env, descP, &descP->closerPid);
17693         /* Message send frees closeEnv */
17694         descP->closeEnv = NULL;
17695         descP->closeRef = esock_atom_undefined;
17696     } else {
17697         int err;
17698 
17699         /* We do not have a closer process
17700          * - have to do an unclean (non blocking) close */
17701 
17702 #ifdef HAVE_SENDFILE
17703         if (descP->sendfileHandle != INVALID_HANDLE)
17704             esock_send_sendfile_deferred_close_msg(env, descP);
17705 #endif
17706 
17707         err = esock_close_socket(env, descP, FALSE);
17708 
17709         if (err != 0)
17710             esock_warning_msg("Failed closing socket without "
17711                               "closer process: "
17712                               "\r\n   Controlling Process: %T"
17713                               "\r\n   Descriptor:          %d"
17714                               "\r\n   Errno:               %d (%T)"
17715                               "\r\n",
17716                               descP->ctrlPid, descP->sock,
17717                               err, MKA(env, erl_errno_id(err)));
17718     }
17719 
17720     SSDBG( descP,
17721            ("SOCKET",
17722             "esock_stop {%d/%d} -> done\r\n",
17723             descP->sock, fd) );
17724 
17725     MUNLOCK(descP->writeMtx);
17726     MUNLOCK(descP->readMtx);
17727 #endif // #ifndef __WIN32__
17728 }
17729 
17730 
17731 
17732 /* *** esock_stop_handle_current ***
17733  *
17734  * Handle current requestor (reader, writer or acceptor) during
17735  * socket stop.
17736  */
17737 #ifndef __WIN32__
17738 static
esock_stop_handle_current(ErlNifEnv * env,const char * role,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ESockRequestor * reqP)17739 void esock_stop_handle_current(ErlNifEnv*       env,
17740                                const char*      role,
17741                                ESockDescriptor* descP,
17742                                ERL_NIF_TERM     sockRef,
17743                                ESockRequestor*  reqP)
17744 {
17745     (void) DEMONP("esock_stop_handle_current", env, descP, &reqP->mon);
17746 
17747     SSDBG( descP, ("SOCKET",
17748                    "esock_stop_handle_current {%d} ->"
17749                    " send abort message to current %s %T %T\r\n",
17750                    descP->sock, role, reqP->pid, reqP->ref) );
17751 
17752     esock_send_abort_msg(env, descP, sockRef, reqP, atom_closed);
17753 
17754     enif_set_pid_undefined(&reqP->pid);
17755     reqP->ref = esock_atom_undefined;
17756 }
17757 #endif // #ifndef __WIN32__
17758 
17759 
17760 
17761 /* This function traverse the queue and sends the specified
17762  * nif_abort message with the specified reason to each member,
17763  * and empty the queue.
17764  */
17765 #ifndef __WIN32__
17766 static
inform_waiting_procs(ErlNifEnv * env,const char * role,ESockDescriptor * descP,ERL_NIF_TERM sockRef,ESockRequestQueue * q,ERL_NIF_TERM reason)17767 void inform_waiting_procs(ErlNifEnv*         env,
17768                           const char*        role,
17769                           ESockDescriptor*   descP,
17770                           ERL_NIF_TERM       sockRef,
17771                           ESockRequestQueue* q,
17772                           ERL_NIF_TERM       reason)
17773 {
17774     ESockRequestQueueElement* currentP = q->first;
17775     ESockRequestQueueElement* nextP;
17776 
17777     SSDBG( descP,
17778            ("SOCKET",
17779             "inform_waiting_procs -> handle waiting %s(s)\r\n", role) );
17780 
17781     while (currentP != NULL) {
17782 
17783         /* <KOLLA>
17784          *
17785          * Should we inform anyone if we fail to demonitor?
17786          * NOT SURE WHAT THAT WOULD REPRESENT AND IT IS NOT
17787          * IMPORTANT IN *THIS* CASE, BUT IT'S A FUNDAMENTAL OP...
17788          *
17789          * </KOLLA>
17790          */
17791 
17792         SSDBG( descP,
17793                ("SOCKET",
17794                 "inform_waiting_procs(%T) {%d} -> "
17795                 "send abort message to waiting %s %T\r\n",
17796                 sockRef, descP->sock,
17797                 role, currentP->data.pid) );
17798 
17799         esock_send_abort_msg(env, descP, sockRef, &currentP->data, reason);
17800 
17801         (void) DEMONP("inform_waiting_procs -> current 'request'",
17802                       env, descP, &currentP->data.mon);
17803 
17804         nextP = currentP->nextP;
17805         FREE(currentP);
17806         currentP = nextP;
17807     }
17808 
17809     q->first = NULL;
17810     q->last  = NULL;
17811 }
17812 #endif // #ifndef __WIN32__
17813 
17814 
17815 /* =========================================================================
17816  * esock_down - Callback function for resource down (monitored processes)
17817  *
17818  */
17819 static
esock_down(ErlNifEnv * env,void * obj,const ErlNifPid * pidP,const ErlNifMonitor * monP)17820 void esock_down(ErlNifEnv*           env,
17821                 void*                obj,
17822                 const ErlNifPid*     pidP,
17823                 const ErlNifMonitor* monP)
17824 {
17825 #ifndef __WIN32__
17826     ESockDescriptor* descP = (ESockDescriptor*) obj;
17827 
17828     MLOCK(descP->readMtx);
17829     MLOCK(descP->writeMtx);
17830 
17831     SSDBG( descP, ("SOCKET", "esock_down {%d} -> entry with"
17832                    "\r\n   pid:   %T"
17833                    "\r\n   Close: %s (%s)"
17834                    "\r\n",
17835                    descP->sock, MKPID(env, pidP),
17836                    B2S(IS_CLOSED(descP->readState)),
17837                    B2S(IS_CLOSING(descP->readState))) );
17838 
17839     if (COMPARE_PIDS(&descP->closerPid, pidP) == 0) {
17840 
17841         /* The closer process went down
17842          * - it will not call nif_finalize_close
17843          */
17844 
17845         enif_set_pid_undefined(&descP->closerPid);
17846 
17847         if (MON_EQ(&descP->closerMon, monP)) {
17848             MON_INIT(&descP->closerMon);
17849 
17850             SSDBG( descP,
17851                    ("SOCKET",
17852                     "esock_down {%d} -> closer process exit\r\n",
17853                     descP->sock) );
17854 
17855         } else {
17856             // The owner is the closer so we used its monitor
17857 
17858             ESOCK_ASSERT( MON_EQ(&descP->ctrlMon, monP) );
17859             MON_INIT(&descP->ctrlMon);
17860             enif_set_pid_undefined(&descP->ctrlPid);
17861 
17862             SSDBG( descP,
17863                    ("SOCKET",
17864                     "esock_down {%d} -> closer controlling process exit\r\n",
17865                     descP->sock) );
17866         }
17867 
17868         /* Since the closer went down there was one,
17869          * hence esock_close() must have run or scheduled esock_stop(),
17870          * or the socket has never been selected upon
17871          */
17872 
17873         if (descP->closeEnv == NULL) {
17874             int err;
17875 
17876             /* Since there is no closeEnv,
17877              * esock_close() did not schedule esock_stop()
17878              * and is about to call esock_finalize_close() but died,
17879              * or esock_stop() has run, sent close_msg to the closer
17880              * and cleared ->closeEnv but the closer died
17881              * - we have to do an unclean (non blocking) socket close here
17882              */
17883 
17884 #ifdef HAVE_SENDFILE
17885             if (descP->sendfileHandle != INVALID_HANDLE)
17886                 esock_send_sendfile_deferred_close_msg(env, descP);
17887 #endif
17888 
17889             err = esock_close_socket(env, descP, FALSE);
17890             if (err != 0)
17891                 esock_warning_msg("Failed closing socket for terminating "
17892                                   "closer process: "
17893                                   "\r\n   Closer Process: %T"
17894                                   "\r\n   Descriptor:     %d"
17895                                   "\r\n   Errno:          %d (%T)"
17896                                   "\r\n",
17897                                   MKPID(env, pidP), descP->sock,
17898                                   err, MKA(env, erl_errno_id(err)));
17899         } else {
17900             /* Since there is a closeEnv esock_stop() has not run yet
17901              * - when it finds that there is no closer process
17902              *   it will close the socket and ignore the close_msg
17903              */
17904             esock_free_env("esock_down - close-env", descP->closeEnv);
17905             descP->closeEnv = NULL;
17906             descP->closeRef = esock_atom_undefined;
17907         }
17908 
17909     } else if (MON_EQ(&descP->ctrlMon, monP)) {
17910         MON_INIT(&descP->ctrlMon);
17911         /* The owner went down */
17912         enif_set_pid_undefined(&descP->ctrlPid);
17913 
17914         if (IS_OPEN(descP->readState)) {
17915             SSDBG( descP,
17916                    ("SOCKET",
17917                     "esock_down {%d} -> controller process exit"
17918                     "\r\n   initiate close\r\n",
17919                     descP->sock) );
17920 
17921             esock_down_ctrl(env, descP, pidP);
17922 
17923             descP->readState  |= ESOCK_STATE_CLOSING;
17924             descP->writeState |= ESOCK_STATE_CLOSING;
17925         } else {
17926             SSDBG( descP,
17927                    ("SOCKET",
17928                     "esock_down {%d} -> controller process exit"
17929                     "\r\n   already closed or closing\r\n",
17930                     descP->sock) );
17931         }
17932 
17933     } else if (descP->connectorP != NULL &&
17934                MON_EQ(&descP->connector.mon, monP)) {
17935         MON_INIT(&descP->connector.mon);
17936 
17937         SSDBG( descP,
17938                ("SOCKET",
17939                 "esock_down {%d} -> connector process exit\r\n",
17940                 descP->sock) );
17941 
17942         /* connectorP is only set during connection.
17943          * Forget all about the ongoing connection.
17944          * We might end up connected, but the process that initiated
17945          * the connection has died and will never know
17946          */
17947 
17948         requestor_release("esock_down->connector",
17949                           env, descP, &descP->connector);
17950         descP->connectorP = NULL;
17951         descP->writeState &= ~ESOCK_STATE_CONNECTING;
17952 
17953     } else {
17954         ERL_NIF_TERM     sockRef;
17955 
17956         /* check all operation queue(s): acceptor, writer and reader.
17957          *
17958          * Is it really any point in doing this if the socket is closed?
17959          *
17960          */
17961 
17962         sockRef = enif_make_resource(env, descP);
17963 
17964         if (IS_CLOSED(descP->readState)) {
17965             SSDBG( descP,
17966                    ("SOCKET",
17967                     "esock_down(%T) {%d} -> stray down: %T\r\n",
17968                     sockRef, descP->sock, pidP) );
17969         } else {
17970 
17971             SSDBG( descP,
17972                    ("SOCKET",
17973                     "esock_down(%T) {%d} -> other process term\r\n",
17974                     sockRef, descP->sock) );
17975 
17976             if (descP->currentReaderP != NULL)
17977                 esock_down_reader(env, descP, sockRef, pidP, monP);
17978             if (descP->currentAcceptorP != NULL)
17979                 esock_down_acceptor(env, descP, sockRef, pidP, monP);
17980             if (descP->currentWriterP != NULL)
17981                 esock_down_writer(env, descP, sockRef, pidP, monP);
17982         }
17983     }
17984 
17985     MUNLOCK(descP->writeMtx);
17986     MUNLOCK(descP->readMtx);
17987 
17988     SSDBG( descP, ("SOCKET", "esock_down -> done\r\n") );
17989 
17990 #endif // #ifndef __WIN32__
17991 }
17992 
17993 
17994 
17995 /* *** esock_down_ctrl ***
17996  *
17997  * Stop after a downed controller
17998  *
17999  */
18000 #ifndef __WIN32__
18001 static
esock_down_ctrl(ErlNifEnv * env,ESockDescriptor * descP,const ErlNifPid * pidP)18002 void esock_down_ctrl(ErlNifEnv*           env,
18003                      ESockDescriptor*     descP,
18004                      const ErlNifPid*     pidP)
18005 {
18006     SSDBG( descP,
18007            ("SOCKET", "esock_down_ctrl {%d} ->"
18008             "\r\n   Pid: %T"
18009             "\r\n", descP->sock, MKPID(env, pidP)) );
18010 
18011     if (esock_do_stop(env, descP)) {
18012         /* esock_stop() is scheduled
18013          * - it has to close the socket
18014          */
18015         SSDBG( descP,
18016                ("SOCKET", "esock_down_ctrl {%d} -> stop was scheduled\r\n",
18017                 descP->sock) );
18018     } else {
18019         int err;
18020 
18021         /* Socket is not in the select machinery
18022          * so esock_stop() will not be called
18023          * - we have to do an unclean (non blocking) socket close here
18024          */
18025 
18026 #ifdef HAVE_SENDFILE
18027         if (descP->sendfileHandle != INVALID_HANDLE)
18028             esock_send_sendfile_deferred_close_msg(env, descP);
18029 #endif
18030 
18031         err = esock_close_socket(env, descP, FALSE);
18032         if (err != 0)
18033             esock_warning_msg("Failed closing socket for terminating "
18034                               "owner process: "
18035                               "\r\n   Owner Process:  %T"
18036                               "\r\n   Descriptor:     %d"
18037                               "\r\n   Errno:          %d (%T)"
18038                               "\r\n",
18039                               MKPID(env, pidP), descP->sock,
18040                               err, MKA(env, erl_errno_id(err)));
18041     }
18042 }
18043 #endif // #ifndef __WIN32__
18044 
18045 
18046 
18047 /* *** esock_down_acceptor ***
18048  *
18049  * Check and then handle a downed acceptor process.
18050  *
18051  */
18052 #ifndef __WIN32__
18053 static
esock_down_acceptor(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,const ErlNifPid * pidP,const ErlNifMonitor * monP)18054 void esock_down_acceptor(ErlNifEnv*           env,
18055                          ESockDescriptor*     descP,
18056                          ERL_NIF_TERM         sockRef,
18057                          const ErlNifPid*     pidP,
18058                          const ErlNifMonitor* monP)
18059 {
18060     if (MON_EQ(&descP->currentAcceptor.mon, monP)) {
18061         MON_INIT(&descP->currentAcceptor.mon);
18062 
18063         SSDBG( descP,
18064                ("SOCKET",
18065                 "esock_down_acceptor(%T) {%d} -> "
18066                 "current acceptor - try activate next\r\n",
18067                 sockRef, descP->sock) );
18068 
18069         if (!activate_next_acceptor(env, descP, sockRef)) {
18070 
18071             SSDBG( descP,
18072                    ("SOCKET",
18073                     "esock_down_acceptor(%T) {%d} -> no more writers\r\n",
18074                     sockRef, descP->sock) );
18075 
18076             descP->readState &= ~ESOCK_STATE_ACCEPTING;
18077 
18078             descP->currentAcceptorP = NULL;
18079         }
18080 
18081     } else {
18082 
18083         /* Maybe unqueue one of the waiting acceptors */
18084 
18085         SSDBG( descP,
18086                ("SOCKET",
18087                 "esock_down_acceptor(%T) {%d} -> "
18088                 "not current acceptor - maybe a waiting acceptor\r\n",
18089                 sockRef, descP->sock) );
18090 
18091         acceptor_unqueue(env, descP, NULL, pidP);
18092     }
18093 }
18094 #endif // #ifndef __WIN32__
18095 
18096 
18097 /* *** esock_down_writer ***
18098  *
18099  * Check and then handle a downed writer process.
18100  *
18101  */
18102 #ifndef __WIN32__
18103 static
esock_down_writer(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,const ErlNifPid * pidP,const ErlNifMonitor * monP)18104 void esock_down_writer(ErlNifEnv*           env,
18105                        ESockDescriptor*     descP,
18106                        ERL_NIF_TERM         sockRef,
18107                        const ErlNifPid*     pidP,
18108                        const ErlNifMonitor* monP)
18109 {
18110     if (MON_EQ(&descP->currentWriter.mon, monP)) {
18111         MON_INIT(&descP->currentWriter.mon);
18112 
18113         SSDBG( descP,
18114                ("SOCKET",
18115                 "esock_down_writer(%T) {%d} -> "
18116                 "current writer - try activate next\r\n",
18117                 sockRef, descP->sock) );
18118 
18119         if (!activate_next_writer(env, descP, sockRef)) {
18120 
18121             SSDBG( descP,
18122                    ("SOCKET",
18123                     "esock_down_writer(%T) {%d} -> no active writer\r\n",
18124                     sockRef, descP->sock) );
18125 
18126             descP->currentWriterP = NULL;
18127         }
18128 
18129     } else {
18130 
18131         /* Maybe unqueue one of the waiting writer(s) */
18132 
18133         SSDBG( descP,
18134                ("SOCKET",
18135                 "esock_down_writer(%T) {%d} -> "
18136                 "not current writer - maybe a waiting writer\r\n",
18137                 sockRef, descP->sock) );
18138 
18139         writer_unqueue(env, descP, NULL, pidP);
18140     }
18141 }
18142 #endif // #ifndef __WIN32__
18143 
18144 
18145 
18146 
18147 /* *** esock_down_reader ***
18148  *
18149  * Check and then handle a downed reader process.
18150  *
18151  */
18152 #ifndef __WIN32__
18153 static
esock_down_reader(ErlNifEnv * env,ESockDescriptor * descP,ERL_NIF_TERM sockRef,const ErlNifPid * pidP,const ErlNifMonitor * monP)18154 void esock_down_reader(ErlNifEnv*           env,
18155                        ESockDescriptor*     descP,
18156                        ERL_NIF_TERM         sockRef,
18157                        const ErlNifPid*     pidP,
18158                        const ErlNifMonitor* monP)
18159 {
18160     if (MON_EQ(&descP->currentReader.mon, monP)) {
18161         MON_INIT(&descP->currentReader.mon);
18162 
18163         SSDBG( descP,
18164                ("SOCKET",
18165                 "esock_down_reader(%T) {%d} -> "
18166                 "current reader - try activate next\r\n",
18167                 sockRef, descP->sock) );
18168 
18169         if (! activate_next_reader(env, descP, sockRef)) {
18170 
18171             SSDBG( descP,
18172                    ("SOCKET",
18173                     "esock_down_reader(%T) {%d} -> no more readers\r\n",
18174                     sockRef, descP->sock) );
18175 
18176             descP->currentReaderP = NULL;
18177         }
18178 
18179     } else {
18180 
18181         /* Maybe unqueue one of the waiting reader(s) */
18182 
18183         SSDBG( descP,
18184                ("SOCKET",
18185                 "esock_down_reader(%T) {%d} -> "
18186                 "not current reader - maybe a waiting reader\r\n",
18187                 sockRef, descP->sock) );
18188 
18189         reader_unqueue(env, descP, NULL, pidP);
18190     }
18191 }
18192 #endif // #ifndef __WIN32__
18193 
18194 
18195 
18196 /* ----------------------------------------------------------------------
18197  *  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
18198  * ----------------------------------------------------------------------
18199  */
18200 
18201 static
18202 ErlNifFunc esock_funcs[] =
18203 {
18204     // Some utility and support functions
18205     {"nif_info",                0, nif_info, 0},
18206     {"nif_info",                1, nif_info, 0},
18207     {"nif_supports",            0, nif_supports, 0},
18208     {"nif_supports",            1, nif_supports, 0},
18209     {"nif_command",             1, nif_command, 0},
18210 
18211     // The proper "socket" interface
18212     {"nif_open",                2, nif_open, 0},
18213     {"nif_open",                4, nif_open, 0},
18214     {"nif_bind",                2, nif_bind, 0},
18215     {"nif_connect",             1, nif_connect, 0},
18216     {"nif_connect",             3, nif_connect, 0},
18217     {"nif_listen",              2, nif_listen, 0},
18218     {"nif_accept",              2, nif_accept, 0},
18219     {"nif_send",                4, nif_send, 0},
18220     {"nif_sendto",              5, nif_sendto, 0},
18221     {"nif_sendmsg",             5, nif_sendmsg, 0},
18222     {"nif_sendfile",            5, nif_sendfile, ERL_NIF_DIRTY_JOB_IO_BOUND},
18223     {"nif_sendfile",            4, nif_sendfile, ERL_NIF_DIRTY_JOB_IO_BOUND},
18224     {"nif_sendfile",            1, nif_sendfile, ERL_NIF_DIRTY_JOB_IO_BOUND},
18225     {"nif_recv",                4, nif_recv, 0},
18226     {"nif_recvfrom",            4, nif_recvfrom, 0},
18227     {"nif_recvmsg",             5, nif_recvmsg, 0},
18228     {"nif_close",               1, nif_close, 0},
18229     {"nif_shutdown",            2, nif_shutdown, 0},
18230     {"nif_setopt",              5, nif_setopt, 0},
18231     {"nif_getopt",              3, nif_getopt, 0},
18232     {"nif_getopt",              4, nif_getopt, 0},
18233     {"nif_sockname",            1, nif_sockname, 0},
18234     {"nif_peername",            1, nif_peername, 0},
18235 
18236     /* Misc utility functions */
18237 
18238     /* "Extra" functions to "complete" the socket interface.
18239      * For instance, the function nif_finalize_close
18240      * is called after the close *select* has "completed".
18241      */
18242     {"nif_cancel",              3, nif_cancel, 0},
18243     {"nif_finalize_close",      1, nif_finalize_close, ERL_NIF_DIRTY_JOB_IO_BOUND}
18244 };
18245 
18246 
18247 #ifndef __WIN32__
18248 static
extract_debug_filename(ErlNifEnv * env,ERL_NIF_TERM map)18249 char* extract_debug_filename(ErlNifEnv*   env,
18250 			     ERL_NIF_TERM map)
18251 {
18252     /* See the functions above */
18253     ERL_NIF_TERM val;
18254     ErlNifBinary bin;
18255     char *filename;
18256 
18257     if (! GET_MAP_VAL(env, map, atom_debug_filename, &val))
18258         return NULL;
18259 
18260     if (! enif_inspect_binary(env, val, &bin))
18261         return NULL;
18262 
18263     ESOCK_ASSERT( (filename = MALLOC(bin.size + 1)) != NULL );
18264 
18265     sys_memcpy(filename, bin.data, bin.size);
18266     filename[bin.size] = '\0';
18267     return filename;
18268 }
18269 #endif // #ifndef __WIN32__
18270 
18271 
18272 
18273 /* =======================================================================
18274  * load_info - A map of misc info (e.g global debug)
18275  */
18276 
18277 static
on_load(ErlNifEnv * env,void ** priv_data,ERL_NIF_TERM load_info)18278 int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
18279 {
18280     /* +++ Local atoms and error reason atoms +++ */
18281 #define LOCAL_ATOM_DECL(A) atom_##A = MKA(env, #A)
18282     LOCAL_ATOMS;
18283     LOCAL_ERROR_REASON_ATOMS;
18284 #undef LOCAL_ATOM_DECL
18285 
18286     /* Global atom(s) and error reason atom(s) */
18287 #define GLOBAL_ATOM_DECL(A) esock_atom_##A = MKA(env, #A)
18288     GLOBAL_ATOMS;
18289     GLOBAL_ERROR_REASON_ATOMS;
18290 #undef GLOBAL_ATOM_DECL
18291 
18292     esock_atom_socket_tag = MKA(env, "$socket");
18293 
18294 #ifndef __WIN32__
18295 
18296     if (! esock_extract_pid_from_map(env, load_info,
18297                                      atom_registry,
18298                                      &data.regPid)) {
18299         enif_set_pid_undefined(&data.regPid);
18300         return 1; // Failure - no registry pid
18301     }
18302 
18303     data.useReg =
18304         esock_get_bool_from_map(env, load_info,
18305                                 atom_use_registry,
18306                                 ESOCK_USE_SOCKET_REGISTRY);
18307 
18308     data.iow =
18309         esock_get_bool_from_map(env, load_info,
18310                                 atom_iow,
18311                                 ESOCK_NIF_IOW_DEFAULT);
18312 
18313     {
18314         char *debug_filename;
18315 
18316         debug_filename = extract_debug_filename(env, load_info);
18317 
18318         if (esock_dbg_init(debug_filename)) {
18319             // Pick up early debug flags only if debug_filename is ok
18320 
18321             data.dbg =
18322                 esock_get_bool_from_map(env, load_info,
18323                                         esock_atom_debug,
18324                                         ESOCK_GLOBAL_DEBUG_DEFAULT);
18325             data.sockDbg =
18326                 esock_get_bool_from_map(env, load_info,
18327                                         atom_socket_debug,
18328                                         ESOCK_DEBUG_DEFAULT);
18329         }
18330 
18331         if (debug_filename != NULL)
18332             FREE(debug_filename);
18333     }
18334 
18335     data.protocolsMtx = MCREATE("esock.protocols");
18336 
18337     /* +++ Global Counters +++ */
18338     data.cntMtx         = MCREATE("esock.gcnt");
18339     data.numSockets     = 0;
18340     data.numTypeDGrams  = 0;
18341     data.numTypeStreams = 0;
18342     data.numTypeSeqPkgs = 0;
18343     data.numDomainLocal = 0;
18344     data.numDomainInet  = 0;
18345     data.numDomainInet6 = 0;
18346     data.numProtoIP     = 0;
18347     data.numProtoTCP    = 0;
18348     data.numProtoUDP    = 0;
18349     data.numProtoSCTP   = 0;
18350 
18351     initOpts();
18352     initCmsgTables();
18353 
18354     data.iov_max =
18355 #if defined(NO_SYSCONF) || (! defined(_SC_IOV_MAX))
18356 #   ifdef IOV_MAX
18357         IOV_MAX
18358 #   else
18359         16
18360 #   endif
18361 #else
18362         sysconf(_SC_IOV_MAX)
18363 #endif
18364         ;
18365     ESOCK_ASSERT( data.iov_max > 0 );
18366 
18367 #endif // #ifndef __WIN32__
18368 
18369     esocks = enif_open_resource_type_x(env,
18370                                        "sockets",
18371                                        &esockInit,
18372                                        ERL_NIF_RT_CREATE,
18373                                        NULL);
18374     return esocks != NULL ?
18375         0: // Success
18376         1; // Failure
18377 }
18378 
18379 /*
18380  * MODULE:  socket (the erlang API/interface module)
18381  * funcs:   esock_funcs (defines the API of this nif)
18382  * load:    on_load (load this nif)
18383  * upgrade: NULL (not used)
18384  * NULL:    THIS IS NOT USED
18385  * unload:  NULL (not used)
18386  */
18387 ERL_NIF_INIT(prim_socket, esock_funcs, on_load, NULL, NULL, NULL)
18388