diff options
author | Micael Karlberg <bmk@erlang.org> | 2023-04-05 17:59:15 +0200 |
---|---|---|
committer | Micael Karlberg <bmk@erlang.org> | 2023-04-05 17:59:15 +0200 |
commit | 30a8a6cf56df07579aae6f47d4a890bc94fa689c (patch) | |
tree | 151bae9a754537e77f3cb041f53a672ae369f439 /erts | |
parent | 52b5b71607a0693fbc3a16267d7dc40834f66f9b (diff) | |
parent | 2bbcf3d17c19cb0883a75717849d531277dfe339 (diff) | |
download | erlang-30a8a6cf56df07579aae6f47d4a890bc94fa689c.tar.gz |
Merge branch 'bmk/erts/esock/20230331/win_async_io2/OTP-18029'
Diffstat (limited to 'erts')
-rw-r--r-- | erts/emulator/Makefile.in | 3 | ||||
-rw-r--r-- | erts/emulator/nifs/common/prim_socket_int.h | 159 | ||||
-rw-r--r-- | erts/emulator/nifs/common/prim_socket_nif.c | 6852 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_asyncio.h | 172 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_int.h | 72 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_io.h | 80 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_syncio.h | 55 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.c | 122 | ||||
-rw-r--r-- | erts/emulator/nifs/common/socket_util.h | 30 | ||||
-rw-r--r-- | erts/emulator/nifs/unix/unix_socket_syncio.c | 2714 | ||||
-rw-r--r-- | erts/emulator/nifs/win32/win_socket_asyncio.c | 9600 | ||||
-rw-r--r-- | erts/preloaded/ebin/prim_socket.beam | bin | 36920 -> 36524 bytes | |||
-rw-r--r-- | erts/preloaded/src/prim_socket.erl | 10 |
13 files changed, 15305 insertions, 4564 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 0a488a010b..d911febb81 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -1150,7 +1150,8 @@ OS_OBJS = \ $(OBJDIR)/sys_interrupt.o \ $(OBJDIR)/sys_env.o \ $(OBJDIR)/dosmap.o \ - $(OBJDIR)/win_prim_file.o + $(OBJDIR)/win_prim_file.o \ + $(OBJDIR)/win_socket_asyncio.o else OS_OBJS = \ diff --git a/erts/emulator/nifs/common/prim_socket_int.h b/erts/emulator/nifs/common/prim_socket_int.h index 855011446b..90937d308e 100644 --- a/erts/emulator/nifs/common/prim_socket_int.h +++ b/erts/emulator/nifs/common/prim_socket_int.h @@ -27,6 +27,7 @@ #define PRIM_SOCKET_INT_H__ #include <erl_nif.h> +#include <sys.h> #include "socket_int.h" #include "socket_dbg.h" @@ -40,6 +41,7 @@ #if defined(__WIN32__) #define INVALID_EVENT NULL +#define SOCKET_FORMAT_STR "%lld" #else @@ -48,6 +50,7 @@ typedef int HANDLE; #define INVALID_SOCKET (-1) typedef int SOCKET; /* A subset of HANDLE */ #define INVALID_EVENT INVALID_HANDLE +#define SOCKET_FORMAT_STR "%d" #endif @@ -77,12 +80,18 @@ typedef int SOCKET; /* A subset of HANDLE */ // #define ESOCK_STATE_DTOR 0x8000 +#define IS_BOUND(st) \ + (((st) & ESOCK_STATE_BOUND) != 0) + #define IS_CLOSED(st) \ (((st) & ESOCK_STATE_CLOSED) != 0) #define IS_CLOSING(st) \ (((st) & ESOCK_STATE_CLOSING) != 0) +#define IS_ACCEPTING(st) \ + (((st) & ESOCK_STATE_ACCEPTING) != 0) + #define IS_OPEN(st) \ (((st) & (ESOCK_STATE_CLOSED | ESOCK_STATE_CLOSING)) == 0) @@ -90,6 +99,9 @@ typedef int SOCKET; /* A subset of HANDLE */ ((((d)->readState | (d)->writeState) & ESOCK_STATE_SELECTED) != 0) +#define ESOCK_DESC_PATTERN_CREATED 0x03030303 +#define ESOCK_DESC_PATTERN_DTOR 0xC0C0C0C0 + /* ========================================================================== * The ESOCK_IS_ERROR macro below is used for portability reasons. @@ -319,7 +331,12 @@ typedef struct { typedef struct { int flag; ERL_NIF_TERM* name; -} ESockMsgFlag; +} ESockFlag; + +extern const ESockFlag esock_msg_flags[]; +extern const int esock_msg_flags_length; +extern const ESockFlag esock_ioctl_flags[]; +extern const int esock_ioctl_flags_length; /* ********************************************************************* * @@ -335,7 +352,6 @@ typedef struct { /* XXX Should be locked but too awkward and small gain */ BOOLEAN_T dbg; BOOLEAN_T useReg; - unsigned int ioNumThreads; // Set once and never changed /* Registry stuff */ ErlNifPid regPid; /* Constant - not locked */ @@ -404,8 +420,21 @@ typedef struct { ErlNifMutex* writeMtx; /**/ unsigned int writeState; // For debugging +#ifndef __WIN32__ + /* + * On *none* Windows: + * This is intended for the *current* writer. + * The queue is intended for *waiting* writers. + * + * *On* Windows: + * We let the I/O Completion Ports handle the queue'ing + * so we do not need to keep track which request is active + * and which are waiting. + * We only use the *queue* as a database. + */ ESockRequestor currentWriter; ESockRequestor* currentWriterP; // NULL or ¤tWriter +#endif ESockRequestQueue writersQ; ESockCounter writePkgCnt; ESockCounter writePkgMax; @@ -418,6 +447,7 @@ typedef struct { HANDLE sendfileHandle; ESockSendfileCounters* sendfileCountersP; #endif + /* +++ Connector +++ */ ESockRequestor connector; ESockRequestor* connectorP; // NULL or &connector @@ -429,8 +459,21 @@ typedef struct { ErlNifMutex* readMtx; /**/ unsigned int readState; // For debugging +#ifndef __WIN32__ + /* + * On *none* Windows: + * This is intended for the *current* reader. + * The queue is intended for *waiting* readers. + * + * *On* Windows: + * We let the I/O Completion Ports handle the queue'ing + * so we do not need to keep track which request is active + * and which are waiting. + * We only use the *queue* as a database. + */ ESockRequestor currentReader; ESockRequestor* currentReaderP; // NULL or ¤tReader +#endif ESockRequestQueue readersQ; ErlNifBinary rbuffer; // DO WE NEED THIS Uint32 readCapacity; // DO WE NEED THIS @@ -441,9 +484,23 @@ typedef struct { ESockCounter readTries; ESockCounter readWaits; ESockCounter readFails; + /* +++ Accept stuff +++ */ +#ifndef __WIN32__ + /* + * On *none* Windows: + * This is intended for the *current* acceptor. + * The queue is intended for *waiting* acceptors. + * + * *On* Windows: + * We let the I/O Completion Ports handle the queue'ing + * so we do not need to keep track which request is active + * and which are waiting. + * We only use the *queue* as a database. + */ ESockRequestor currentAcceptor; ESockRequestor* currentAcceptorP; // NULL or ¤tAcceptor +#endif ESockRequestQueue acceptorsQ; ESockCounter accSuccess; ESockCounter accTries; @@ -456,10 +513,14 @@ typedef struct { * If rNum is 0 (zero), then rNumCnt is not used and only *one* read will * be done. Also, when get'ing the value of the option (rcvbuf) with * getopt, the value will be reported as an integer. If the rNum has a - * value greater then 0 (zero), then it will instead be reported as {N, BufSz}. + * value greater then 0 (zero), then it will instead be reported as + * {N, BufSz}. + * On Windows, rNum and rNumCnt is *not* used! */ +#ifndef __WIN32__ unsigned int rNum; // recv: Number of reads using rBufSz unsigned int rNumCnt; // recv: Current number of reads (so far) +#endif size_t rCtrlSz; // Read control buffer size /* Locked by readMtx and writeMtx combined for writing, @@ -477,7 +538,6 @@ typedef struct { ESockMonitor ctrlMon; /* +++ The actual socket +++ */ SOCKET sock; - ErlNifEvent event; SOCKET origFD; // A 'socket' created from this FD BOOLEAN_T closeOnClose; // Have we dup'ed or not /* +++ The dbg flag for SSDBG +++ */ @@ -486,6 +546,11 @@ typedef struct { /* Lock order: readMtx, writeMtx, cntMtx */ + +#if defined(ESOCK_DESCRIPTOR_FILLER) + char filler[1024]; +#endif + } ESockDescriptor; @@ -503,7 +568,17 @@ extern char* erl_errno_id(int error); /* THIS IS JUST TEMPORARY??? */ * ======================================================================== * */ -extern ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event); +extern ESockDescriptor* esock_alloc_descriptor(SOCKET sock); +extern void esock_dealloc_descriptor(ErlNifEnv* env, + ESockDescriptor* descP); + +extern BOOLEAN_T esock_open_is_debug(ErlNifEnv* env, + ERL_NIF_TERM eopts, + BOOLEAN_T def); +extern BOOLEAN_T esock_open_use_registry(ErlNifEnv* env, + ERL_NIF_TERM eopts, + BOOLEAN_T def); +extern BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto); extern BOOLEAN_T esock_getopt_int(SOCKET sock, int level, @@ -512,17 +587,20 @@ extern BOOLEAN_T esock_getopt_int(SOCKET sock, /* ** Socket Registry functions *** */ -#ifndef __WIN32__ extern void esock_send_reg_add_msg(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM sockRef); extern void esock_send_reg_del_msg(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM sockRef); -#endif // #ifndef __WIN32__ /* *** Message sending functions *** */ +extern void esock_send_simple_abort_msg(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid* pid, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM reason); extern void esock_send_abort_msg(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM sockRef, @@ -581,9 +659,21 @@ extern int esock_select_cancel(ErlNifEnv* env, ErlNifEvent event, enum ErlNifSelectFlags mode, void* obj); +extern ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +extern ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +extern ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef, + int smode, + int rmode); /* *** Request queue functions *** */ +extern void esock_free_request_queue(ESockRequestQueue* q); extern BOOLEAN_T esock_requestor_pop(ESockRequestQueue* q, ESockRequestor* reqP); @@ -593,6 +683,7 @@ extern void esock_requestor_release(const char* slogan, ESockDescriptor* descP, ESockRequestor* reqP); + /* *** esock_activate_next_acceptor *** * *** esock_activate_next_writer *** * *** esock_activate_next_reader *** @@ -637,7 +728,8 @@ ACTIVATE_NEXT_FUNCS_DEFS extern void esock_##O##_push(ErlNifEnv* env, \ ESockDescriptor* descP, \ ErlNifPid pid, \ - ERL_NIF_TERM ref); \ + ERL_NIF_TERM ref, \ + void* dataP); \ extern BOOLEAN_T esock_##O##_pop(ErlNifEnv* env, \ ESockDescriptor* descP, \ ESockRequestor* reqP); \ @@ -652,6 +744,7 @@ ESOCK_OPERATOR_FUNCS_DEFS /* *** Environment wrapper functions *** * These hould really be inline, but for now... */ +extern void esock_clear_env(const char* slogan, ErlNifEnv* env); extern void esock_free_env(const char* slogan, ErlNifEnv* env); extern ErlNifEnv* esock_alloc_env(const char* slogan); @@ -696,6 +789,30 @@ extern void esock_encode_msg_flags(ErlNifEnv* env, #endif +extern void esock_stop_handle_current(ErlNifEnv* env, + const char* role, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ESockRequestor* reqP); +extern void esock_inform_waiting_procs(ErlNifEnv* env, + const char* role, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ESockRequestQueue* q, + ERL_NIF_TERM reason); + + +/* *** Control Message 'stuff' *** + */ +extern void* esock_init_cmsghdr(struct cmsghdr* cmsgP, + size_t rem, // Remaining space + size_t size, // Size of data + size_t* usedP); +extern ESockCmsgSpec* esock_lookup_cmsg_table(int level, size_t *num); +extern ESockCmsgSpec* esock_lookup_cmsg_spec(ESockCmsgSpec* table, + size_t num, + ERL_NIF_TERM eType); + /* *** Sendfile 'stuff' *** */ #ifdef HAVE_SENDFILE @@ -704,16 +821,32 @@ extern ESockSendfileCounters initESockSendfileCounters; #endif +/* *** message functions **** + */ +extern void esock_send_wrap_msg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM cnt); +extern BOOLEAN_T esock_send_msg(ErlNifEnv* env, + ErlNifPid* pid, + ERL_NIF_TERM msg, + ErlNifEnv* msgEnv); +extern ERL_NIF_TERM esock_mk_socket_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM tag, + ERL_NIF_TERM info); +extern ERL_NIF_TERM esock_mk_socket(ErlNifEnv* env, + ERL_NIF_TERM sockRef); +#ifdef HAVE_SENDFILE +extern void esock_send_sendfile_deferred_close_msg(ErlNifEnv* env, + ESockDescriptor* descP); +#endif + /* *** 'close' functions *** */ -#ifndef __WIN32__ -extern BOOLEAN_T esock_do_stop(ErlNifEnv* env, - ESockDescriptor* descP); extern int esock_close_socket(ErlNifEnv* env, ESockDescriptor* descP, BOOLEAN_T unlock); -#endif - #endif // PRIM_SOCKET_INT_H__ diff --git a/erts/emulator/nifs/common/prim_socket_nif.c b/erts/emulator/nifs/common/prim_socket_nif.c index 8e0c4d1d6a..a46e44d53b 100644 --- a/erts/emulator/nifs/common/prim_socket_nif.c +++ b/erts/emulator/nifs/common/prim_socket_nif.c @@ -125,6 +125,10 @@ ERL_NIF_INIT(prim_socket, esock_funcs, on_load, NULL, NULL, NULL) * * * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ +#define ESOCK_CMSG_SPACE(l) WSA_CMSG_SPACE((l)) +#define ESOCK_CMSG_LEN(l) WSA_CMSG_LEN((l)) +#define ESOCK_CMSG_DATA(p) WSA_CMSG_DATA((p)) + #define STRNCASECMP strncasecmp #define INCL_WINSOCK_API_TYPEDEFS 1 @@ -167,6 +171,10 @@ ERL_NIF_INIT(prim_socket, esock_funcs, on_load, NULL, NULL, NULL) * * * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ +#define ESOCK_CMSG_SPACE(l) CMSG_SPACE((l)) +#define ESOCK_CMSG_LEN(l) CMSG_LEN((l)) +#define ESOCK_CMSG_DATA(p) CMSG_DATA((p)) + #include <sys/time.h> #ifdef NETDB_H_NEEDS_IN_H @@ -387,6 +395,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #include "socket_util.h" #include "prim_socket_int.h" #include "socket_io.h" +#include "socket_asyncio.h" #include "socket_syncio.h" #include "prim_file_nif_dyncall.h" @@ -447,9 +456,6 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; #define ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT 1024 #define ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT 1024 -#define ESOCK_DESC_PATTERN_CREATED 0x03030303 -#define ESOCK_DESC_PATTERN_DTOR 0xC0C0C0C0 - /*---------------------------------------------------------------------------- * Interface constants. @@ -458,8 +464,7 @@ static void (*esock_sctp_freepaddrs)(struct sockaddr *addrs) = NULL; * msg_flag() in socket.erl. */ -const ESockMsgFlag msg_flags[] = { - +const ESockFlag esock_msg_flags[] = { { #ifdef MSG_CMSG_CLOEXEC MSG_CMSG_CLOEXEC, @@ -548,13 +553,9 @@ const ESockMsgFlag msg_flags[] = { #endif &esock_atom_trunc} }; +const int esock_msg_flags_length = NUM(esock_msg_flags); - -static const struct ioctl_flag { - int flag; - ERL_NIF_TERM *name; -} ioctl_flags[] = { - +const ESockFlag esock_ioctl_flags[] = { { #ifdef IFF_UP IFF_UP, @@ -824,6 +825,7 @@ static const struct ioctl_flag { #endif &esock_atom_nogroup} }; +const int esock_ioctl_flags_length = NUM(esock_ioctl_flags); @@ -879,29 +881,29 @@ static const struct ioctl_flag { /* *** Windows macros *** */ -#define sock_accept(s, addr, len) \ - make_noninheritable_handle(accept((s), (addr), (len))) -#define sock_bind(s, addr, len) bind((s), (addr), (len)) +/* #define sock_accept(s, addr, len) \ + make_noninheritable_handle(accept((s), (addr), (len))) */ +// #define sock_bind(s, addr, len) bind((s), (addr), (len)) #define sock_close(s) closesocket((s)) -#define sock_close_event(e) WSACloseEvent(e) -#define sock_connect(s, addr, len) connect((s), (addr), (len)) -#define sock_create_event(s) WSACreateEvent() +// #define sock_close_event(e) WSACloseEvent(e) +// #define sock_connect(s, addr, len) connect((s), (addr), (len)) +// #define sock_create_event(s) WSACreateEvent() #define sock_errno() WSAGetLastError() #define sock_getopt(s,l,o,v,ln) getsockopt((s),(l),(o),(v),(ln)) -#define sock_htons(x) htons((x)) -#define sock_htonl(x) htonl((x)) +// #define sock_htons(x) htons((x)) +// #define sock_htonl(x) htonl((x)) #define sock_listen(s, b) listen((s), (b)) #define sock_name(s, addr, len) getsockname((s), (addr), (len)) -#define sock_ntohs(x) ntohs((x)) -#define sock_open(domain, type, proto) \ - make_noninheritable_handle(socket((domain), (type), (proto))) +// #define sock_ntohs(x) ntohs((x)) +/* #define sock_open(domain, type, proto) \ + make_noninheritable_handle(socket((domain), (type), (proto))) */ #define sock_peer(s, addr, len) getpeername((s), (addr), (len)) -#define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) +// #define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) #define sock_recvfrom(s,buf,blen,flag,addr,alen) \ recvfrom((s),(buf),(blen),(flag),(addr),(alen)) -#define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) -#define sock_sendto(s,buf,blen,flag,addr,alen) \ - sendto((s),(buf),(blen),(flag),(addr),(alen)) +/* #define sock_send(s,buf,len,flag) send((s),(buf),(len),(flag)) */ +/* #define sock_sendto(s,buf,blen,flag,addr,alen) \ + sendto((s),(buf),(blen),(flag),(addr),(alen)) */ #define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln)) #define sock_shutdown(s, how) shutdown((s), (how)) @@ -933,18 +935,18 @@ static unsigned long one_value = 1; // #endif // #define sock_bind(s, addr, len) bind((s), (addr), (len)) #define sock_close(s) close((s)) -#define sock_close_event(e) /* do nothing */ +// #define sock_close_event(e) /* do nothing */ // #define sock_connect(s, addr, len) connect((s), (addr), (len)) -#define sock_create_event(s) (s) /* return file descriptor */ +// #define sock_create_event(s) (s) /* return file descriptor */ #define sock_errno() errno #define sock_getopt(s,t,n,v,l) getsockopt((s),(t),(n),(v),(l)) -#define sock_htons(x) htons((x)) -#define sock_htonl(x) htonl((x)) -// #define sock_listen(s, b) listen((s), (b)) -// #define sock_name(s, addr, len) getsockname((s), (addr), (len)) -#define sock_ntohs(x) ntohs((x)) +// #define sock_htons(x) htons((x)) +// #define sock_htonl(x) htonl((x)) +#define sock_listen(s, b) listen((s), (b)) +#define sock_name(s, addr, len) getsockname((s), (addr), (len)) +// #define sock_ntohs(x) ntohs((x)) // #define sock_open(domain, type, proto) socket((domain), (type), (proto)) -// #define sock_peer(s, addr, len) getpeername((s), (addr), (len)) +#define sock_peer(s, addr, len) getpeername((s), (addr), (len)) // #define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) /* #define sock_recvfrom(s,buf,blen,flag,addr,alen) \ */ /* recvfrom((s),(buf),(blen),(flag),(addr),(alen)) */ @@ -954,7 +956,7 @@ static unsigned long one_value = 1; /* #define sock_sendto(s,buf,blen,flag,addr,alen) \ */ /* sendto((s),(buf),(blen),(flag),(addr),(alen)) */ #define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln)) -// #define sock_shutdown(s, how) shutdown((s), (how)) +#define sock_shutdown(s, how) shutdown((s), (how)) /* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * @@ -1078,6 +1080,11 @@ typedef struct { ESockIOInit init; ESockIOFinish finish; + ESockIOInfo info; + ESockIOCommand cmd; + ESockIOSupports0 supports_0; + ESockIOSupports1 supports_1; + ESockIOOpenWithFd open_with_fd; ESockIOOpenPlain open_plain; ESockIOBind bind; @@ -1103,69 +1110,87 @@ typedef struct { ESockIOSockName sockname; ESockIOPeerName peername; + + /* The various cancel operations */ + ESockIOCancelConnect cancel_connect; + ESockIOCancelAccept cancel_accept; + ESockIOCancelSend cancel_send; + ESockIOCancelRecv cancel_recv; + + /* Socket option callback functions */ + ESockIOSetopt setopt; + ESockIOSetoptNative setopt_native; + ESockIOSetoptOtp setopt_otp; + ESockIOGetopt getopt; + ESockIOGetoptNative getopt_native; + ESockIOGetoptOtp getopt_otp; + + /* Socket ioctl callback functions */ + ESockIOIoctl_2 ioctl_2; + ESockIOIoctl_3 ioctl_3; + ESockIOIoctl_4 ioctl_4; + + /* (socket) NIF resource callback functions */ + ESockIODTor dtor; + ESockIOStop stop; + ESockIODown down; + } ESockIoBackend; +/* ------------------------------------------------------------------------ + * Socket option(s) and table(s) + */ -#ifndef __WIN32__ -/* ---------------------------------------------------------------------- * - * * - * * - * Start of non-__WIN32__ section a.k.a UNIX section * - * * - * * - * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ +struct ESockOpt +{ + int opt; // Option number -/* And here comes the functions that does the actual work (for the most part) */ + // Function to set option + ERL_NIF_TERM (*setopt)(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); -static ERL_NIF_TERM esock_command(ErlNifEnv* env, - ERL_NIF_TERM command, - ERL_NIF_TERM cdata); -static ERL_NIF_TERM esock_command_debug(ErlNifEnv* env, - ERL_NIF_TERM cdata); -static ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env, - ERL_NIF_TERM cdata); -static ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env, - ERL_NIF_TERM cdata); + // Function to get option + ERL_NIF_TERM (*getopt)(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt); -static ERL_NIF_TERM esock_global_info(ErlNifEnv* env); -static ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, - ESockDescriptor* descP); -static ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv* env, - ESockDescriptor* descP); -static ERL_NIF_TERM esock_socket_info_type(ErlNifEnv* env, - ESockDescriptor* descP); -static ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env, - ESockDescriptor* descP); -static ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env, - ESockDescriptor* descP); -static ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env, - unsigned int state); -#define ESOCK_SOCKET_INFO_REQ_FUNCS \ - ESOCK_SOCKET_INFO_REQ_FUNC_DEF(readers); \ - ESOCK_SOCKET_INFO_REQ_FUNC_DEF(writers); \ - ESOCK_SOCKET_INFO_REQ_FUNC_DEF(acceptors); + ERL_NIF_TERM *nameP; // Pointer to option name atom +}; -#define ESOCK_SOCKET_INFO_REQ_FUNC_DEF(F) \ - static ERL_NIF_TERM esock_socket_info_##F(ErlNifEnv* env, \ - ESockDescriptor* descP); -ESOCK_SOCKET_INFO_REQ_FUNCS -#undef ESOCK_SOCKET_INFO_REQ_FUNC_DEF -static ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env, - ESockDescriptor* descP, - ESockRequestor* currentRequestorP, - ESockRequestQueue* q); +/* Option levels table*/ -static ERL_NIF_TERM esock_supports_0(ErlNifEnv* env); -static ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key); +struct ESockOptLevel +{ + int level; // Level number + + size_t num; // Number of options + + struct ESockOpt *opts; // Options table + + ERL_NIF_TERM *nameP; // Pointer to level name atom +}; -static ERL_NIF_TERM esock_supports_msg_flags(ErlNifEnv* env); -static ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env); -static ERL_NIF_TERM esock_supports_ioctl_requests(ErlNifEnv* env); -static ERL_NIF_TERM esock_supports_ioctl_flags(ErlNifEnv* env); -static ERL_NIF_TERM esock_supports_options(ErlNifEnv* env); + +/* First chunk of forwards...some of these are used in the options tables... */ + +/* Set native options */ +static ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); +static ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM valueSpec); /* Set OTP level options */ static ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env, ESockDescriptor* descP, @@ -1180,28 +1205,186 @@ static ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env, * *** esock_setopt_otp_meta *** * *** esock_setopt_otp_use_registry *** */ -#define ESOCK_SETOPT_OTP_FUNCS \ - ESOCK_SETOPT_OTP_FUNC_DEF(debug); \ - ESOCK_SETOPT_OTP_FUNC_DEF(iow); \ - ESOCK_SETOPT_OTP_FUNC_DEF(ctrl_proc); \ - ESOCK_SETOPT_OTP_FUNC_DEF(rcvbuf); \ - ESOCK_SETOPT_OTP_FUNC_DEF(rcvctrlbuf); \ - ESOCK_SETOPT_OTP_FUNC_DEF(sndctrlbuf); \ - ESOCK_SETOPT_OTP_FUNC_DEF(meta); \ +#define ESOCK_SETOPT_OTP_FUNCS \ + ESOCK_SETOPT_OTP_FUNC_DEF(debug); \ + ESOCK_SETOPT_OTP_FUNC_DEF(iow); \ + ESOCK_SETOPT_OTP_FUNC_DEF(ctrl_proc); \ + ESOCK_SETOPT_OTP_FUNC_DEF(rcvbuf); \ + ESOCK_SETOPT_OTP_FUNC_DEF(rcvctrlbuf); \ + ESOCK_SETOPT_OTP_FUNC_DEF(sndctrlbuf); \ + ESOCK_SETOPT_OTP_FUNC_DEF(meta); \ ESOCK_SETOPT_OTP_FUNC_DEF(use_registry); -#define ESOCK_SETOPT_OTP_FUNC_DEF(F) \ - static ERL_NIF_TERM esock_setopt_otp_##F(ErlNifEnv* env, \ - ESockDescriptor* descP, \ +#define ESOCK_SETOPT_OTP_FUNC_DEF(F) \ + static ERL_NIF_TERM esock_setopt_otp_##F(ErlNifEnv* env, \ + ESockDescriptor* descP, \ ERL_NIF_TERM eVal) ESOCK_SETOPT_OTP_FUNCS #undef ESOCK_SETOPT_OTP_FUNC_DEF -/* Set native options */ -static ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env, - ESockDescriptor* descP, - int level, - int opt, - ERL_NIF_TERM eVal); +static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt); +/* *** esock_getopt_otp_debug *** + * *** esock_getopt_otp_iow *** + * *** esock_getopt_otp_ctrl_proc *** + * *** esock_getopt_otp_rcvbuf *** + * *** esock_getopt_otp_rcvctrlbuf *** + * *** esock_getopt_otp_sndctrlbuf *** + * *** esock_getopt_otp_fd *** + * *** esock_getopt_otp_meta *** + * *** esock_getopt_otp_use_registry *** + * *** esock_getopt_otp_domain *** + * *** //esock_getopt_otp_type *** + * *** //esock_getopt_otp_protocol *** + * *** //esock_getopt_otp_dtp *** + */ +#define ESOCK_GETOPT_OTP_FUNCS \ + ESOCK_GETOPT_OTP_FUNC_DEF(debug); \ + ESOCK_GETOPT_OTP_FUNC_DEF(iow); \ + ESOCK_GETOPT_OTP_FUNC_DEF(ctrl_proc); \ + ESOCK_GETOPT_OTP_FUNC_DEF(rcvbuf); \ + ESOCK_GETOPT_OTP_FUNC_DEF(rcvctrlbuf); \ + ESOCK_GETOPT_OTP_FUNC_DEF(sndctrlbuf); \ + ESOCK_GETOPT_OTP_FUNC_DEF(fd); \ + ESOCK_GETOPT_OTP_FUNC_DEF(meta); \ + ESOCK_GETOPT_OTP_FUNC_DEF(use_registry); \ + ESOCK_GETOPT_OTP_FUNC_DEF(domain); +#if 0 +ESOCK_GETOPT_OTP_FUNC_DEF(type); \ +ESOCK_GETOPT_OTP_FUNC_DEF(protocol); \ +ESOCK_GETOPT_OTP_FUNC_DEF(dtp); +#endif +#define ESOCK_GETOPT_OTP_FUNC_DEF(F) \ + static ERL_NIF_TERM esock_getopt_otp_##F(ErlNifEnv* env, \ + ESockDescriptor* descP) +ESOCK_GETOPT_OTP_FUNCS +#undef ESOCK_GETOPT_OTP_FUNC_DEF + +static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + void* optVal, + socklen_t optLen); +static ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt); +static ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt); +static ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + SOCKOPTLEN_T valueSz); +static ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ErlNifBinary* binP); + +static int socket_setopt(int sock, + int level, + int opt, + const void* optVal, + const socklen_t optLen); + +static int cmpESockOpt(const void *vpa, const void *vpb); +static int cmpESockOptLevel(const void *vpa, const void *vpb); +static struct ESockOpt *lookupOpt(int level, int opt); + + +static ERL_NIF_TERM esock_supports_0(ErlNifEnv* env); +static ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key); + +static ERL_NIF_TERM esock_supports_msg_flags(ErlNifEnv* env); +static ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env); +static ERL_NIF_TERM esock_supports_ioctl_requests(ErlNifEnv* env); +static ERL_NIF_TERM esock_supports_ioctl_flags(ErlNifEnv* env); +static ERL_NIF_TERM esock_supports_options(ErlNifEnv* env); + +#ifndef __WIN32__ +/* ---------------------------------------------------------------------- * + * * + * * + * Start of non-__WIN32__ section a.k.a UNIX section * + * * + * * + * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv */ + +/* *** esock_activate_next_acceptor *** + * *** esock_activate_next_writer *** + * *** esock_activate_next_reader *** + * + * All the activate-next functions for acceptor, writer and reader + * have exactly the same API, so we apply some macro magic to simplify. + * They simply operates on dufferent data structures. + * + */ + +#define ACTIVATE_NEXT_FUNCS_DEFS \ + ACTIVATE_NEXT_FUNC_DEF(acceptor) \ + ACTIVATE_NEXT_FUNC_DEF(writer) \ + ACTIVATE_NEXT_FUNC_DEF(reader) + +#define ACTIVATE_NEXT_FUNC_DEF(F) \ + extern BOOLEAN_T esock_activate_next_##F(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM sockRef); +ACTIVATE_NEXT_FUNCS_DEFS +#undef ACTIVATE_NEXT_FUNC_DEF + +/* esock_acceptor_search4pid | esock_writer_search4pid | esock_reader_search4pid + * esock_acceptor_push | esock_writer_push | esock_reader_push + * esock_acceptor_pop | esock_writer_pop | esock_reader_pop + * esock_acceptor_unqueue | esock_writer_unqueue | esock_reader_unqueue + * + * All the queue operator functions (search4pid, push, pop + * and unqueue) for acceptor, writer and reader has exactly + * the same API, so we apply some macro magic to simplify. + */ + +#define ESOCK_OPERATOR_FUNCS_DEFS \ + ESOCK_OPERATOR_FUNCS_DEF(acceptor) \ + ESOCK_OPERATOR_FUNCS_DEF(writer) \ + ESOCK_OPERATOR_FUNCS_DEF(reader) + +#define ESOCK_OPERATOR_FUNCS_DEF(O) \ + extern BOOLEAN_T esock_##O##_search4pid(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ErlNifPid* pid); \ + extern void esock_##O##_push(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ErlNifPid pid, \ + ERL_NIF_TERM ref, \ + void* dataP); \ + extern BOOLEAN_T esock_##O##_pop(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ESockRequestor* reqP); \ + extern BOOLEAN_T esock_##O##_unqueue(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM* refP, \ + const ErlNifPid* pidP); +ESOCK_OPERATOR_FUNCS_DEFS +#undef ESOCK_OPERATOR_FUNCS_DEF + +static ERL_NIF_TERM mk_select_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM selectRef); + + +/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * + * * + * * + * End of non-__WIN32__ section a.k.a UNIX section * + * * + * * + * ---------------------------------------------------------------------- */ +#endif // #ifndef __WIN32__ + + static ERL_NIF_TERM esock_setopt(ErlNifEnv* env, ESockDescriptor* descP, int level, @@ -1367,50 +1550,6 @@ static ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv* env, #endif // defined(HAVE_SCTP) -static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env, - ESockDescriptor* descP, - int eOpt); -/* *** esock_getopt_otp_debug *** - * *** esock_getopt_otp_iow *** - * *** esock_getopt_otp_ctrl_proc *** - * *** esock_getopt_otp_rcvbuf *** - * *** esock_getopt_otp_rcvctrlbuf *** - * *** esock_getopt_otp_sndctrlbuf *** - * *** esock_getopt_otp_fd *** - * *** esock_getopt_otp_meta *** - * *** esock_getopt_otp_use_registry *** - * *** esock_getopt_otp_domain *** - * *** //esock_getopt_otp_type *** - * *** //esock_getopt_otp_protocol *** - * *** //esock_getopt_otp_dtp *** - */ -#define ESOCK_GETOPT_OTP_FUNCS \ - ESOCK_GETOPT_OTP_FUNC_DEF(debug); \ - ESOCK_GETOPT_OTP_FUNC_DEF(iow); \ - ESOCK_GETOPT_OTP_FUNC_DEF(ctrl_proc); \ - ESOCK_GETOPT_OTP_FUNC_DEF(rcvbuf); \ - ESOCK_GETOPT_OTP_FUNC_DEF(rcvctrlbuf); \ - ESOCK_GETOPT_OTP_FUNC_DEF(sndctrlbuf); \ - ESOCK_GETOPT_OTP_FUNC_DEF(fd); \ - ESOCK_GETOPT_OTP_FUNC_DEF(meta); \ - ESOCK_GETOPT_OTP_FUNC_DEF(use_registry); \ - ESOCK_GETOPT_OTP_FUNC_DEF(domain); -#if 0 - ESOCK_GETOPT_OTP_FUNC_DEF(type); \ - ESOCK_GETOPT_OTP_FUNC_DEF(protocol); \ - ESOCK_GETOPT_OTP_FUNC_DEF(dtp); -#endif -#define ESOCK_GETOPT_OTP_FUNC_DEF(F) \ - static ERL_NIF_TERM esock_getopt_otp_##F(ErlNifEnv* env, \ - ESockDescriptor* descP) -ESOCK_GETOPT_OTP_FUNCS -#undef ESOCK_GETOPT_OTP_FUNC_DEF - -static ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env, - ESockDescriptor* descP, - int level, - int opt, - ERL_NIF_TERM valueSpec); static ERL_NIF_TERM esock_getopt(ErlNifEnv* env, ESockDescriptor* descP, int level, @@ -1447,9 +1586,9 @@ ERL_NIF_TERM esock_getopt_sock_protocol(ErlNifEnv* env, #if defined(IP_MTU_DISCOVER) static ERL_NIF_TERM esock_getopt_ip_mtu_discover(ErlNifEnv* env, - ESockDescriptor* descP, - int level, - int opt); + ESockDescriptor* descP, + int level, + int opt); #endif #if defined(IP_MULTICAST_IF) static ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv* env, @@ -1515,295 +1654,6 @@ static ERL_NIF_TERM esock_getopt_sctp_rtoinfo(ErlNifEnv* env, #endif // defined(HAVE_SCTP) -static ERL_NIF_TERM esock_ioctl1(ErlNifEnv* env, - ESockDescriptor* descP, - unsigned long req); -static ERL_NIF_TERM esock_ioctl2(ErlNifEnv* env, - ESockDescriptor* descP, - unsigned long req, - ERL_NIF_TERM arg); -static ERL_NIF_TERM esock_ioctl3(ErlNifEnv* env, - ESockDescriptor* descP, - unsigned long req, - ERL_NIF_TERM ename, - ERL_NIF_TERM eval); -static ERL_NIF_TERM esock_ioctl_gifconf(ErlNifEnv* env, - ESockDescriptor* descP); -#if defined(SIOCGIFNAME) -static ERL_NIF_TERM esock_ioctl_gifname(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM eidx); -#endif - -/* esock_ioctl_gifindex */ -#if defined(SIOCGIFINDEX) -#define IOCTL_GIFINDEX_FUNC_DEF IOCTL_GET_FUNC_DEF(gifindex) -#else -#define IOCTL_GIFINDEX_FUNC_DEF -#endif - -/* esock_ioctl_gifflags */ -#if defined(SIOCGIFFLAGS) -#define IOCTL_GIFFLAGS_FUNC_DEF IOCTL_GET_FUNC_DEF(gifflags) -#else -#define IOCTL_GIFFLAGS_FUNC_DEF -#endif - -/* esock_ioctl_gifaddr */ -#if defined(SIOCGIFADDR) -#define IOCTL_GIFADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifaddr) -#else -#define IOCTL_GIFADDR_FUNC_DEF -#endif - -/* esock_ioctl_gifdstaddr */ -#if defined(SIOCGIFDSTADDR) -#define IOCTL_GIFDSTADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifdstaddr) -#else -#define IOCTL_GIFDSTADDR_FUNC_DEF -#endif - -/* esock_ioctl_gifbrdaddr */ -#if defined(SIOCGIFBRDADDR) -#define IOCTL_GIFBRDADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifbrdaddr) -#else -#define IOCTL_GIFBRDADDR_FUNC_DEF -#endif - -/* esock_ioctl_gifnetmask */ -#if defined(SIOCGIFNETMASK) -#define IOCTL_GIFNETMASK_FUNC_DEF IOCTL_GET_FUNC_DEF(gifnetmask) -#else -#define IOCTL_GIFNETMASK_FUNC_DEF -#endif - -/* esock_ioctl_gifmtu */ -#if defined(SIOCGIFMTU) -#define IOCTL_GIFMTU_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmtu) -#else -#define IOCTL_GIFMTU_FUNC_DEF -#endif - -/* esock_ioctl_gifhwaddr */ -#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) -#define IOCTL_GIFHWADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifhwaddr) -#else -#define IOCTL_GIFHWADDR_FUNC_DEF -#endif - -/* esock_ioctl_gifmap */ -#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) -#define IOCTL_GIFMAP_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmap) -#else -#define IOCTL_GIFMAP_FUNC_DEF -#endif - -/* esock_ioctl_giftxqlen */ -#if defined(SIOCGIFTXQLEN) -#define IOCTL_GIFTXQLEN_FUNC_DEF IOCTL_GET_FUNC_DEF(giftxqlen) -#else -#define IOCTL_GIFTXQLEN_FUNC_DEF -#endif - -#define IOCTL_GET_FUNCS_DEF \ - IOCTL_GIFINDEX_FUNC_DEF; \ - IOCTL_GIFFLAGS_FUNC_DEF; \ - IOCTL_GIFADDR_FUNC_DEF; \ - IOCTL_GIFDSTADDR_FUNC_DEF; \ - IOCTL_GIFBRDADDR_FUNC_DEF; \ - IOCTL_GIFNETMASK_FUNC_DEF; \ - IOCTL_GIFMTU_FUNC_DEF; \ - IOCTL_GIFHWADDR_FUNC_DEF; \ - IOCTL_GIFMAP_FUNC_DEF; \ - IOCTL_GIFTXQLEN_FUNC_DEF; -#define IOCTL_GET_FUNC_DEF(F) \ - static ERL_NIF_TERM esock_ioctl_##F(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ERL_NIF_TERM ename) -IOCTL_GET_FUNCS_DEF -#undef IOCTL_GET_FUNC_DEF - -/* esock_ioctl_sifflags */ -#if defined(SIOCSIFFLAGS) -#define IOCTL_SIFFLAGS_FUNC_DEF IOCTL_SET_FUNC_DEF(sifflags) -#else -#define IOCTL_SIFFLAGS_FUNC_DEF -#endif - -/* esock_ioctl_sifaddr */ -#if defined(SIOCSIFADDR) -#define IOCTL_SIFADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifaddr) -#else -#define IOCTL_SIFADDR_FUNC_DEF -#endif - -/* esock_ioctl_sifdstaddr */ -#if defined(SIOCSIFDSTADDR) -#define IOCTL_SIFDSTADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifdstaddr) -#else -#define IOCTL_SIFDSTADDR_FUNC_DEF -#endif - -/* esock_ioctl_sifbrdaddr */ -#if defined(SIOCSIFBRDADDR) -#define IOCTL_SIFBRDADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifbrdaddr) -#else -#define IOCTL_SIFBRDADDR_FUNC_DEF -#endif - -/* esock_ioctl_sifnetmask */ -#if defined(SIOCSIFNETMASK) -#define IOCTL_SIFNETMASK_FUNC_DEF IOCTL_SET_FUNC_DEF(sifnetmask) -#else -#define IOCTL_SIFNETMASK_FUNC_DEF -#endif - -/* esock_ioctl_sifmtu */ -#if defined(SIOCSIFMTU) -#define IOCTL_SIFMTU_FUNC_DEF IOCTL_SET_FUNC_DEF(sifmtu) -#else -#define IOCTL_SIFMTU_FUNC_DEF -#endif - -/* esock_ioctl_siftxqlen */ -#if defined(SIOCSIFTXQLEN) -#define IOCTL_SIFTXQLEN_FUNC_DEF IOCTL_SET_FUNC_DEF(siftxqlen) -#else -#define IOCTL_SIFTXQLEN_FUNC_DEF -#endif - -#define IOCTL_SET_FUNCS_DEF \ - IOCTL_SIFFLAGS_FUNC_DEF; \ - IOCTL_SIFADDR_FUNC_DEF; \ - IOCTL_SIFDSTADDR_FUNC_DEF; \ - IOCTL_SIFBRDADDR_FUNC_DEF; \ - IOCTL_SIFNETMASK_FUNC_DEF; \ - IOCTL_SIFMTU_FUNC_DEF; \ - IOCTL_SIFTXQLEN_FUNC_DEF; -#define IOCTL_SET_FUNC_DEF(F) \ - static ERL_NIF_TERM esock_ioctl_##F(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ERL_NIF_TERM ename, \ - ERL_NIF_TERM evalue) -IOCTL_SET_FUNCS_DEF -#undef IOCTL_SET_FUNC_DEF - - -static ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env, - ESockDescriptor* descP, - struct ifconf* ifcP); -static ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env, - ESockDescriptor* descP, - struct ifreq* ifrP); -static ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env, - char* name); -static ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env, - struct sockaddr* sa); -static ERL_NIF_TERM make_ifreq(ErlNifEnv* env, - ERL_NIF_TERM name, - ERL_NIF_TERM key2, - ERL_NIF_TERM val2); -#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) -static ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env, - ESockDescriptor* descP, - struct ifmap* mapP); -#endif -#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) -static ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env, - ESockDescriptor* descP, - struct sockaddr* addrP); -#endif -static ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env, - ESockDescriptor* descP, - struct sockaddr* addrP); -static ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env, - ESockDescriptor* descP, - short flags); -#if defined(SIOCSIFFLAGS) -static BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM eflags, - short* flags); -#endif -static BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM eaddr, - ESockAddress* addr); -#if defined(SIOCSIFMTU) -static BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM emtu, - int* mtu); -#endif -#if defined(SIOCSIFTXQLEN) -static BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM etxqlen, - int* txqlen); -#endif -#if defined(SIOCSIFTXQLEN) -static BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM eivalue, - int* ivalue); -#endif -static ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env, - ESockDescriptor* descP, - int ivalue); - -static ERL_NIF_TERM esock_cancel(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM op, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM esock_cancel_connect(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM esock_cancel_accept(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef); -static ERL_NIF_TERM esock_cancel_accept_waiting(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef, - const ErlNifPid* selfP); -static ERL_NIF_TERM esock_cancel_send(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM esock_cancel_send_current(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef); -static ERL_NIF_TERM esock_cancel_send_waiting(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef, - const ErlNifPid* selfP); -static ERL_NIF_TERM esock_cancel_recv(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef); -static ERL_NIF_TERM esock_cancel_recv_waiting(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef, - const ErlNifPid* selfP); -static ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef); -static ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef, - int smode, - int rmode); - #if defined(USE_SETOPT_STR_OPT) static ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -1822,7 +1672,7 @@ static ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv* env, int level, int opt, ERL_NIF_TERM eVal); -#if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \ +#if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \ && defined(ESOCK_USE_RCVSNDTIMEO) static ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -1830,13 +1680,6 @@ static ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv* env, int opt, ERL_NIF_TERM eVal); #endif -static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env, - ESockDescriptor* descP, - int level, - int opt, - void* optVal, - socklen_t optLen); - #if defined(USE_GETOPT_STR_OPT) static ERL_NIF_TERM esock_getopt_str_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -1845,25 +1688,7 @@ static ERL_NIF_TERM esock_getopt_str_opt(ErlNifEnv* env, int max, BOOLEAN_T stripNUL); #endif -static ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env, - ESockDescriptor* descP, - int level, - int opt); -static ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env, - ESockDescriptor* descP, - int level, - int opt); -static ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv* env, - ESockDescriptor* descP, - int level, - int opt, - SOCKOPTLEN_T valueSz); -static ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv* env, - ESockDescriptor* descP, - int level, - int opt, - ErlNifBinary* binP); -#if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \ +#if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \ && defined(ESOCK_USE_RCVSNDTIMEO) static ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -1873,36 +1698,946 @@ static ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv* env, -/* ------------------------------------------------------------------------ - * Socket option tables and handling + + + + +static ERL_NIF_TERM esock_shutdown(ErlNifEnv* env, + ESockDescriptor* descP, + int how); +static ERL_NIF_TERM esock_sockname(ErlNifEnv* env, + ESockDescriptor* descP); +static ERL_NIF_TERM esock_peername(ErlNifEnv* env, + ESockDescriptor* descP); + +static ERL_NIF_TERM esock_command(ErlNifEnv* env, + ERL_NIF_TERM command, + ERL_NIF_TERM cdata); +static ERL_NIF_TERM esock_command_debug(ErlNifEnv* env, + ERL_NIF_TERM cdata); +static ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env, + ERL_NIF_TERM cdata); +static ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env, + ERL_NIF_TERM cdata); + +#define ESOCK_SOCKET_INFO_REQ_FUNCS \ + ESOCK_SOCKET_INFO_REQ_FUNC_DEF(readers); \ + ESOCK_SOCKET_INFO_REQ_FUNC_DEF(writers); \ + ESOCK_SOCKET_INFO_REQ_FUNC_DEF(acceptors); + +#define ESOCK_SOCKET_INFO_REQ_FUNC_DEF(F) \ + static ERL_NIF_TERM esock_socket_info_##F(ErlNifEnv* env, \ + ESockDescriptor* descP); +ESOCK_SOCKET_INFO_REQ_FUNCS +#undef ESOCK_SOCKET_INFO_REQ_FUNC_DEF + +static ERL_NIF_TERM esock_cancel(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM op, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +/* +static ERL_NIF_TERM esock_cancel_recv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +*/ +static ERL_NIF_TERM esock_listen(ErlNifEnv* env, + ESockDescriptor* descP, + int backlog); + +static ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env, + ESockDescriptor* descP, +#ifndef __WIN32__ + ESockRequestor* currentRequestorP, +#endif + ESockRequestQueue* q); + +static ERL_NIF_TERM esock_global_info(ErlNifEnv* env); +static ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, + ESockDescriptor* descP); +static ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv* env, + ESockDescriptor* descP); +static ERL_NIF_TERM esock_socket_info_type(ErlNifEnv* env, + ESockDescriptor* descP); +static ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env, + ESockDescriptor* descP); +static ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env, + unsigned int state); +static ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env, + ESockDescriptor* descP); + +static ERL_NIF_TERM mk_close_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM closeRef); +static ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env, + ERL_NIF_TERM tag, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM mk_simple_abort_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM reason); +static ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef, + ERL_NIF_TERM reason); +static ERL_NIF_TERM mk_wrap_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM cnt); +static BOOLEAN_T qsearch4pid(ErlNifEnv* env, + ESockRequestQueue* q, + ErlNifPid* pid); +static unsigned int qlength(ESockRequestQueue* q); +static void qpush(ESockRequestQueue* q, + ESockRequestQueueElement* e); +static ESockRequestQueueElement* qpop(ESockRequestQueue* q); +static BOOLEAN_T qunqueue(ErlNifEnv* env, + ESockDescriptor* descP, + const char* slogan, + ESockRequestQueue* q, + ERL_NIF_TERM* refP, + const ErlNifPid* pidP); +static ESockRequestQueueElement* qget(ErlNifEnv* env, + ESockDescriptor* descP, + const char* slogan, + ESockRequestQueue* q, + ERL_NIF_TERM* refP, + const ErlNifPid* pidP); + +static char* extract_debug_filename(ErlNifEnv* env, + ERL_NIF_TERM map); + + +/* --------------------------------------------------------------------- */ + +#if defined(IP_TOS) +static BOOLEAN_T decode_ip_tos(ErlNifEnv* env, + ERL_NIF_TERM eVal, + int* val); +#endif +#if defined(IP_MTU_DISCOVER) +static BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv* env, + ERL_NIF_TERM eVal, + int* val); +#endif +#if defined(IP_MTU_DISCOVER) +static void encode_ip_pmtudisc(ErlNifEnv* env, + int val, + ERL_NIF_TERM* eVal); +#endif +#if defined(IPV6_MTU_DISCOVER) +static BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, + ERL_NIF_TERM eVal, + int* val); +#endif +#if defined(IPV6_MTU_DISCOVER) +static void encode_ipv6_pmtudisc(ErlNifEnv* env, + int val, + ERL_NIF_TERM* eVal); +#endif + +static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val); + +#if defined(IPV6_MULTICAST_HOPS) || defined(IPV6_UNICAST_HOPS) +static +BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val); +#endif + +#if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO) +static BOOLEAN_T decode_sctp_assoc_t(ErlNifEnv* env, + ERL_NIF_TERM eVal, + sctp_assoc_t* val); +static ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env, + sctp_assoc_t val); +#endif // #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO) + + +static BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how); + + +/* +#if defined(HAS_AF_LOCAL) || defined(SO_BINDTODEVICE) +static size_t my_strnlen(const char *s, size_t maxlen); +#endif +*/ + +static void esock_dtor(ErlNifEnv* env, void* obj); +static void esock_stop(ErlNifEnv* env, + void* obj, + ErlNifEvent fd, + int is_direct_call); +static void esock_down(ErlNifEnv* env, + void* obj, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); + +static void esock_on_halt(void* priv_data); + +static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); + + + +#if HAVE_IN6 +# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY +# if HAVE_DECL_IN6ADDR_ANY_INIT +static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } }; +# else +static const struct in6_addr in6addr_any = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; +# endif /* HAVE_IN6ADDR_ANY_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_ANY */ + +# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK +# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT +static const struct in6_addr in6addr_loopback = + { { IN6ADDR_LOOPBACK_INIT } }; +# else +static const struct in6_addr in6addr_loopback = + { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }; +# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */ +# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */ +#endif /* HAVE_IN6 */ + + + +/* *** Global atoms *** + * Note that when an (global) atom is added here, it must also be added + * in the socket_int.h file! */ +#define GLOBAL_ATOMS \ + GLOBAL_ATOM_DECL(abort); \ + GLOBAL_ATOM_DECL(accept); \ + GLOBAL_ATOM_DECL(acceptconn); \ + GLOBAL_ATOM_DECL(acceptfilter); \ + GLOBAL_ATOM_DECL(acc_success); \ + GLOBAL_ATOM_DECL(acc_fails); \ + GLOBAL_ATOM_DECL(acc_tries); \ + GLOBAL_ATOM_DECL(acc_waits); \ + GLOBAL_ATOM_DECL(adaption_layer); \ + GLOBAL_ATOM_DECL(addr); \ + GLOBAL_ATOM_DECL(addrform); \ + GLOBAL_ATOM_DECL(add_membership); \ + GLOBAL_ATOM_DECL(add_socket); \ + GLOBAL_ATOM_DECL(add_source_membership); \ + GLOBAL_ATOM_DECL(alen); \ + GLOBAL_ATOM_DECL(allmulti); \ + GLOBAL_ATOM_DECL(already); \ + GLOBAL_ATOM_DECL(any); \ + GLOBAL_ATOM_DECL(appletlk); \ + GLOBAL_ATOM_DECL(arcnet); \ + GLOBAL_ATOM_DECL(associnfo); \ + GLOBAL_ATOM_DECL(atm); \ + GLOBAL_ATOM_DECL(authhdr); \ + GLOBAL_ATOM_DECL(auth_active_key); \ + GLOBAL_ATOM_DECL(auth_asconf); \ + GLOBAL_ATOM_DECL(auth_chunk); \ + GLOBAL_ATOM_DECL(auth_delete_key); \ + GLOBAL_ATOM_DECL(auth_key); \ + GLOBAL_ATOM_DECL(auth_level); \ + GLOBAL_ATOM_DECL(autoclose); \ + GLOBAL_ATOM_DECL(automedia); \ + GLOBAL_ATOM_DECL(ax25); \ + GLOBAL_ATOM_DECL(bad_data); \ + GLOBAL_ATOM_DECL(base_addr); \ + GLOBAL_ATOM_DECL(bindtodevice); \ + GLOBAL_ATOM_DECL(block_source); \ + GLOBAL_ATOM_DECL(broadcast); \ + GLOBAL_ATOM_DECL(busy_poll); \ + GLOBAL_ATOM_DECL(cancel); \ + GLOBAL_ATOM_DECL(cantconfig); \ + GLOBAL_ATOM_DECL(chaos); \ + GLOBAL_ATOM_DECL(checksum); \ + GLOBAL_ATOM_DECL(close); \ + GLOBAL_ATOM_DECL(closed); \ + GLOBAL_ATOM_DECL(cmsg_cloexec); \ + GLOBAL_ATOM_DECL(command); \ + GLOBAL_ATOM_DECL(completion); \ + GLOBAL_ATOM_DECL(confirm); \ + GLOBAL_ATOM_DECL(congestion); \ + GLOBAL_ATOM_DECL(connect); \ + GLOBAL_ATOM_DECL(connected); \ + GLOBAL_ATOM_DECL(connecting); \ + GLOBAL_ATOM_DECL(context); \ + GLOBAL_ATOM_DECL(cork); \ + GLOBAL_ATOM_DECL(counters); \ + GLOBAL_ATOM_DECL(credentials); \ + GLOBAL_ATOM_DECL(ctrl); \ + GLOBAL_ATOM_DECL(ctrunc); \ + GLOBAL_ATOM_DECL(data); \ + GLOBAL_ATOM_DECL(data_size); \ + GLOBAL_ATOM_DECL(debug); \ + GLOBAL_ATOM_DECL(default); \ + GLOBAL_ATOM_DECL(default_send_params); \ + GLOBAL_ATOM_DECL(delayed_ack_time); \ + GLOBAL_ATOM_DECL(dgram); \ + GLOBAL_ATOM_DECL(disable_fragments); \ + GLOBAL_ATOM_DECL(dlci); \ + GLOBAL_ATOM_DECL(dma); \ + GLOBAL_ATOM_DECL(domain); \ + GLOBAL_ATOM_DECL(dontfrag); \ + GLOBAL_ATOM_DECL(dontroute); \ + GLOBAL_ATOM_DECL(dormant); \ + GLOBAL_ATOM_DECL(drop_membership); \ + GLOBAL_ATOM_DECL(drop_source_membership); \ + GLOBAL_ATOM_DECL(dstopts); \ + GLOBAL_ATOM_DECL(dup); \ + GLOBAL_ATOM_DECL(dying); \ + GLOBAL_ATOM_DECL(dynamic); \ + GLOBAL_ATOM_DECL(echo); \ + GLOBAL_ATOM_DECL(eether); \ + GLOBAL_ATOM_DECL(efile); \ + GLOBAL_ATOM_DECL(egp); \ + GLOBAL_ATOM_DECL(enotsup); \ + GLOBAL_ATOM_DECL(eor); \ + GLOBAL_ATOM_DECL(error); \ + GLOBAL_ATOM_DECL(errqueue); \ + GLOBAL_ATOM_DECL(esp_network_level); \ + GLOBAL_ATOM_DECL(esp_trans_level); \ + GLOBAL_ATOM_DECL(ether); \ + GLOBAL_ATOM_DECL(eui64); \ + GLOBAL_ATOM_DECL(events); \ + GLOBAL_ATOM_DECL(explicit_eor); \ + GLOBAL_ATOM_DECL(faith); \ + GLOBAL_ATOM_DECL(false); \ + GLOBAL_ATOM_DECL(family); \ + GLOBAL_ATOM_DECL(fastroute); \ + GLOBAL_ATOM_DECL(flags); \ + GLOBAL_ATOM_DECL(flowinfo); \ + GLOBAL_ATOM_DECL(fragment_interleave); \ + GLOBAL_ATOM_DECL(freebind); \ + GLOBAL_ATOM_DECL(frelay); \ + GLOBAL_ATOM_DECL(get_overlapped_result); \ + GLOBAL_ATOM_DECL(get_peer_addr_info); \ + GLOBAL_ATOM_DECL(hatype); \ + GLOBAL_ATOM_DECL(hdrincl); \ + GLOBAL_ATOM_DECL(hmac_ident); \ + GLOBAL_ATOM_DECL(hoplimit); \ + GLOBAL_ATOM_DECL(hopopts); \ + GLOBAL_ATOM_DECL(host); \ + GLOBAL_ATOM_DECL(icmp); \ + GLOBAL_ATOM_DECL(icmp6); \ + GLOBAL_ATOM_DECL(ieee802); \ + GLOBAL_ATOM_DECL(ieee1394); \ + GLOBAL_ATOM_DECL(ifindex); \ + GLOBAL_ATOM_DECL(igmp); \ + GLOBAL_ATOM_DECL(implink); \ + GLOBAL_ATOM_DECL(index); \ + GLOBAL_ATOM_DECL(inet); \ + GLOBAL_ATOM_DECL(inet6); \ + GLOBAL_ATOM_DECL(infiniband); \ + GLOBAL_ATOM_DECL(info); \ + GLOBAL_ATOM_DECL(initmsg); \ + GLOBAL_ATOM_DECL(invalid); \ + GLOBAL_ATOM_DECL(integer_range); \ + GLOBAL_ATOM_DECL(iov); \ + GLOBAL_ATOM_DECL(ip); \ + GLOBAL_ATOM_DECL(ipcomp_level); \ + GLOBAL_ATOM_DECL(ipip); \ + GLOBAL_ATOM_DECL(ipv6); \ + GLOBAL_ATOM_DECL(irq); \ + GLOBAL_ATOM_DECL(i_want_mapped_v4_addr); \ + GLOBAL_ATOM_DECL(join_group); \ + GLOBAL_ATOM_DECL(keepalive); \ + GLOBAL_ATOM_DECL(keepcnt); \ + GLOBAL_ATOM_DECL(keepidle); \ + GLOBAL_ATOM_DECL(keepintvl); \ + GLOBAL_ATOM_DECL(kernel); \ + GLOBAL_ATOM_DECL(knowsepoch); \ + GLOBAL_ATOM_DECL(leave_group); \ + GLOBAL_ATOM_DECL(level); \ + GLOBAL_ATOM_DECL(linger); \ + GLOBAL_ATOM_DECL(link); \ + GLOBAL_ATOM_DECL(link0); \ + GLOBAL_ATOM_DECL(link1); \ + GLOBAL_ATOM_DECL(link2); \ + GLOBAL_ATOM_DECL(local); \ + GLOBAL_ATOM_DECL(localtlk); \ + GLOBAL_ATOM_DECL(local_auth_chunks); \ + GLOBAL_ATOM_DECL(loopback); \ + GLOBAL_ATOM_DECL(lowdelay); \ + GLOBAL_ATOM_DECL(lower_up); \ + GLOBAL_ATOM_DECL(mark); \ + GLOBAL_ATOM_DECL(master); \ + GLOBAL_ATOM_DECL(maxburst); \ + GLOBAL_ATOM_DECL(maxseg); \ + GLOBAL_ATOM_DECL(md5sig); \ + GLOBAL_ATOM_DECL(mem_end); \ + GLOBAL_ATOM_DECL(mem_start); \ + GLOBAL_ATOM_DECL(metricom); \ + GLOBAL_ATOM_DECL(mincost); \ + GLOBAL_ATOM_DECL(minttl); \ + GLOBAL_ATOM_DECL(monitor); \ + GLOBAL_ATOM_DECL(more); \ + GLOBAL_ATOM_DECL(msfilter); \ + GLOBAL_ATOM_DECL(mtu); \ + GLOBAL_ATOM_DECL(mtu_discover); \ + GLOBAL_ATOM_DECL(multicast); \ + GLOBAL_ATOM_DECL(multicast_all); \ + GLOBAL_ATOM_DECL(multicast_hops); \ + GLOBAL_ATOM_DECL(multicast_if); \ + GLOBAL_ATOM_DECL(multicast_loop); \ + GLOBAL_ATOM_DECL(multicast_ttl); \ + GLOBAL_ATOM_DECL(name); \ + GLOBAL_ATOM_DECL(netns); \ + GLOBAL_ATOM_DECL(netrom); \ + GLOBAL_ATOM_DECL(nlen); \ + GLOBAL_ATOM_DECL(noarp); \ + GLOBAL_ATOM_DECL(nodelay); \ + GLOBAL_ATOM_DECL(nodefrag); \ + GLOBAL_ATOM_DECL(nogroup); \ + GLOBAL_ATOM_DECL(none); \ + GLOBAL_ATOM_DECL(noopt); \ + GLOBAL_ATOM_DECL(nopush); \ + GLOBAL_ATOM_DECL(nosignal); \ + GLOBAL_ATOM_DECL(notrailers); \ + GLOBAL_ATOM_DECL(not_bound); \ + GLOBAL_ATOM_DECL(not_found); \ + GLOBAL_ATOM_DECL(num_general_errors); \ + GLOBAL_ATOM_DECL(not_owner); \ + GLOBAL_ATOM_DECL(num_threads); \ + GLOBAL_ATOM_DECL(num_unexpected_accepts); \ + GLOBAL_ATOM_DECL(num_unexpected_connects); \ + GLOBAL_ATOM_DECL(num_unexpected_reads); \ + GLOBAL_ATOM_DECL(num_unexpected_writes); \ + GLOBAL_ATOM_DECL(num_unknown_cmds); \ + GLOBAL_ATOM_DECL(oactive); \ + GLOBAL_ATOM_DECL(ok); \ + GLOBAL_ATOM_DECL(oob); \ + GLOBAL_ATOM_DECL(oobinline); \ + GLOBAL_ATOM_DECL(options); \ + GLOBAL_ATOM_DECL(origdstaddr); \ + GLOBAL_ATOM_DECL(otherhost); \ + GLOBAL_ATOM_DECL(outgoing); \ + GLOBAL_ATOM_DECL(packet); \ + GLOBAL_ATOM_DECL(partial_delivery_point); \ + GLOBAL_ATOM_DECL(passcred); \ + GLOBAL_ATOM_DECL(path); \ + GLOBAL_ATOM_DECL(peek); \ + GLOBAL_ATOM_DECL(peek_off); \ + GLOBAL_ATOM_DECL(peer_addr_params); \ + GLOBAL_ATOM_DECL(peer_auth_chunks); \ + GLOBAL_ATOM_DECL(peercred); \ + GLOBAL_ATOM_DECL(pktinfo); \ + GLOBAL_ATOM_DECL(pktoptions); \ + GLOBAL_ATOM_DECL(pkttype); \ + GLOBAL_ATOM_DECL(pointopoint); \ + GLOBAL_ATOM_DECL(port); \ + GLOBAL_ATOM_DECL(portrange); \ + GLOBAL_ATOM_DECL(portsel); \ + GLOBAL_ATOM_DECL(ppromisc); \ + GLOBAL_ATOM_DECL(primary_addr); \ + GLOBAL_ATOM_DECL(prim_file); \ + GLOBAL_ATOM_DECL(priority); \ + GLOBAL_ATOM_DECL(promisc); \ + GLOBAL_ATOM_DECL(pronet); \ + GLOBAL_ATOM_DECL(protocol); \ + GLOBAL_ATOM_DECL(pup); \ + GLOBAL_ATOM_DECL(raw); \ + GLOBAL_ATOM_DECL(rcvbuf); \ + GLOBAL_ATOM_DECL(rcvbufforce); \ + GLOBAL_ATOM_DECL(rcvlowat); \ + GLOBAL_ATOM_DECL(rcvtimeo); \ + GLOBAL_ATOM_DECL(rdm); \ + GLOBAL_ATOM_DECL(read_byte); \ + GLOBAL_ATOM_DECL(read_fails); \ + GLOBAL_ATOM_DECL(read_pkg); \ + GLOBAL_ATOM_DECL(read_tries); \ + GLOBAL_ATOM_DECL(read_waits); \ + GLOBAL_ATOM_DECL(recv); \ + GLOBAL_ATOM_DECL(recvdstaddr); \ + GLOBAL_ATOM_DECL(recverr); \ + GLOBAL_ATOM_DECL(recvfrom); \ + GLOBAL_ATOM_DECL(recvhoplimit); \ + GLOBAL_ATOM_DECL(recvif); \ + GLOBAL_ATOM_DECL(recvmsg); \ + GLOBAL_ATOM_DECL(recvopts); \ + GLOBAL_ATOM_DECL(recvorigdstaddr); \ + GLOBAL_ATOM_DECL(recvpktinfo); \ + GLOBAL_ATOM_DECL(recvtclass); \ + GLOBAL_ATOM_DECL(recvtos); \ + GLOBAL_ATOM_DECL(recvttl); \ + GLOBAL_ATOM_DECL(reliability); \ + GLOBAL_ATOM_DECL(renaming); \ + GLOBAL_ATOM_DECL(reset_streams); \ + GLOBAL_ATOM_DECL(retopts); \ + GLOBAL_ATOM_DECL(reuseaddr); \ + GLOBAL_ATOM_DECL(reuseport); \ + GLOBAL_ATOM_DECL(rights); \ + GLOBAL_ATOM_DECL(router_alert); \ + GLOBAL_ATOM_DECL(rthdr); \ + GLOBAL_ATOM_DECL(rtoinfo); \ + GLOBAL_ATOM_DECL(running); \ + GLOBAL_ATOM_DECL(rxq_ovfl); \ + GLOBAL_ATOM_DECL(scope_id); \ + GLOBAL_ATOM_DECL(sctp); \ + GLOBAL_ATOM_DECL(sec); \ + GLOBAL_ATOM_DECL(select); \ + GLOBAL_ATOM_DECL(select_failed); \ + GLOBAL_ATOM_DECL(select_sent); \ + GLOBAL_ATOM_DECL(send); \ + GLOBAL_ATOM_DECL(sendfile); \ + GLOBAL_ATOM_DECL(sendfile_byte); \ + GLOBAL_ATOM_DECL(sendfile_deferred_close); \ + GLOBAL_ATOM_DECL(sendfile_fails); \ + GLOBAL_ATOM_DECL(sendfile_max); \ + GLOBAL_ATOM_DECL(sendfile_pkg); \ + GLOBAL_ATOM_DECL(sendfile_pkg_max); \ + GLOBAL_ATOM_DECL(sendfile_tries); \ + GLOBAL_ATOM_DECL(sendfile_waits); \ + GLOBAL_ATOM_DECL(sendmsg); \ + GLOBAL_ATOM_DECL(sendsrcaddr); \ + GLOBAL_ATOM_DECL(sendto); \ + GLOBAL_ATOM_DECL(seqpacket); \ + GLOBAL_ATOM_DECL(setfib); \ + GLOBAL_ATOM_DECL(set_peer_primary_addr); \ + GLOBAL_ATOM_DECL(simplex); \ + GLOBAL_ATOM_DECL(slave); \ + GLOBAL_ATOM_DECL(slen); \ + GLOBAL_ATOM_DECL(sndbuf); \ + GLOBAL_ATOM_DECL(sndbufforce); \ + GLOBAL_ATOM_DECL(sndlowat); \ + GLOBAL_ATOM_DECL(sndtimeo); \ + GLOBAL_ATOM_DECL(sockaddr); \ + GLOBAL_ATOM_DECL(socket); \ + GLOBAL_ATOM_DECL(spec_dst); \ + GLOBAL_ATOM_DECL(staticarp); \ + GLOBAL_ATOM_DECL(state); \ + GLOBAL_ATOM_DECL(status); \ + GLOBAL_ATOM_DECL(stream); \ + GLOBAL_ATOM_DECL(syncnt); \ + GLOBAL_ATOM_DECL(tclass); \ + GLOBAL_ATOM_DECL(tcp); \ + GLOBAL_ATOM_DECL(throughput); \ + GLOBAL_ATOM_DECL(timestamp); \ + GLOBAL_ATOM_DECL(tos); \ + GLOBAL_ATOM_DECL(transparent); \ + GLOBAL_ATOM_DECL(timeout); \ + GLOBAL_ATOM_DECL(true); \ + GLOBAL_ATOM_DECL(trunc); \ + GLOBAL_ATOM_DECL(ttl); \ + GLOBAL_ATOM_DECL(tunnel); \ + GLOBAL_ATOM_DECL(tunnel6); \ + GLOBAL_ATOM_DECL(txqlen); \ + GLOBAL_ATOM_DECL(type); \ + GLOBAL_ATOM_DECL(udp); \ + GLOBAL_ATOM_DECL(unblock_source); \ + GLOBAL_ATOM_DECL(undefined); \ + GLOBAL_ATOM_DECL(unicast_hops); \ + GLOBAL_ATOM_DECL(unknown); \ + GLOBAL_ATOM_DECL(unspec); \ + GLOBAL_ATOM_DECL(up); \ + GLOBAL_ATOM_DECL(update_accept_context); \ + GLOBAL_ATOM_DECL(update_connect_context); \ + GLOBAL_ATOM_DECL(usec); \ + GLOBAL_ATOM_DECL(user); \ + GLOBAL_ATOM_DECL(user_timeout); \ + GLOBAL_ATOM_DECL(use_ext_recvinfo); \ + GLOBAL_ATOM_DECL(use_min_mtu); \ + GLOBAL_ATOM_DECL(use_registry); \ + GLOBAL_ATOM_DECL(value); \ + GLOBAL_ATOM_DECL(void); \ + GLOBAL_ATOM_DECL(v6only); \ + GLOBAL_ATOM_DECL(write_byte); \ + GLOBAL_ATOM_DECL(write_fails); \ + GLOBAL_ATOM_DECL(write_pkg); \ + GLOBAL_ATOM_DECL(write_tries); \ + GLOBAL_ATOM_DECL(write_waits); \ + GLOBAL_ATOM_DECL(zero) -struct ESockOpt -{ - int opt; // Option number - // Function to set option - ERL_NIF_TERM (*setopt) - (ErlNifEnv *env, ESockDescriptor *descP, - int level, int opt, ERL_NIF_TERM eVal); - // Function to get option - ERL_NIF_TERM (*getopt) - (ErlNifEnv *env, ESockDescriptor *descP, - int level, int opt); +/* *** Global error reason atoms *** */ +#define GLOBAL_ERROR_REASON_ATOMS \ + GLOBAL_ATOM_DECL(create_accept_socket); \ + GLOBAL_ATOM_DECL(eagain); \ + GLOBAL_ATOM_DECL(einval); \ + GLOBAL_ATOM_DECL(select_read); \ + GLOBAL_ATOM_DECL(select_write) - ERL_NIF_TERM *nameP; // Pointer to option name atom + +#define GLOBAL_ATOM_DECL(A) ERL_NIF_TERM esock_atom_##A +GLOBAL_ATOMS; +GLOBAL_ERROR_REASON_ATOMS; +#undef GLOBAL_ATOM_DECL +ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') + +/* *** Local atoms *** */ +#define LOCAL_ATOMS \ + LOCAL_ATOM_DECL(accepting); \ + LOCAL_ATOM_DECL(adaptation_layer); \ + LOCAL_ATOM_DECL(add); \ + LOCAL_ATOM_DECL(addr_unreach); \ + LOCAL_ATOM_DECL(address); \ + LOCAL_ATOM_DECL(adm_prohibited); \ + LOCAL_ATOM_DECL(association); \ + LOCAL_ATOM_DECL(assoc_id); \ + LOCAL_ATOM_DECL(authentication); \ + LOCAL_ATOM_DECL(boolean); \ + LOCAL_ATOM_DECL(bound); \ + LOCAL_ATOM_DECL(bufsz); \ + LOCAL_ATOM_DECL(close); \ + LOCAL_ATOM_DECL(closing); \ + LOCAL_ATOM_DECL(code); \ + LOCAL_ATOM_DECL(cookie_life); \ + LOCAL_ATOM_DECL(counter_wrap); \ + LOCAL_ATOM_DECL(ctype); \ + LOCAL_ATOM_DECL(data_io); \ + LOCAL_ATOM_DECL(debug_filename); \ + LOCAL_ATOM_DECL(del); \ + LOCAL_ATOM_DECL(dest_unreach); \ + LOCAL_ATOM_DECL(do); \ + LOCAL_ATOM_DECL(dont); \ + LOCAL_ATOM_DECL(dtor); \ + LOCAL_ATOM_DECL(exclude); \ + LOCAL_ATOM_DECL(false); \ + LOCAL_ATOM_DECL(frag_needed); \ + LOCAL_ATOM_DECL(gifaddr); \ + LOCAL_ATOM_DECL(gifbrdaddr); \ + LOCAL_ATOM_DECL(gifconf); \ + LOCAL_ATOM_DECL(gifdstaddr); \ + LOCAL_ATOM_DECL(gifflags); \ + LOCAL_ATOM_DECL(gifhwaddr); \ + LOCAL_ATOM_DECL(gifindex); \ + LOCAL_ATOM_DECL(gifmap); \ + LOCAL_ATOM_DECL(gifmtu); \ + LOCAL_ATOM_DECL(gifname); \ + LOCAL_ATOM_DECL(gifnetmask); \ + LOCAL_ATOM_DECL(giftxqlen); \ + LOCAL_ATOM_DECL(host_unknown); \ + LOCAL_ATOM_DECL(host_unreach); \ + LOCAL_ATOM_DECL(how); \ + LOCAL_ATOM_DECL(in4_sockaddr); \ + LOCAL_ATOM_DECL(in6_sockaddr); \ + LOCAL_ATOM_DECL(include); \ + LOCAL_ATOM_DECL(initial); \ + LOCAL_ATOM_DECL(interface); \ + LOCAL_ATOM_DECL(integer); \ + LOCAL_ATOM_DECL(ioctl_flags); \ + LOCAL_ATOM_DECL(ioctl_requests); \ + LOCAL_ATOM_DECL(iov_max); \ + LOCAL_ATOM_DECL(iow); \ + LOCAL_ATOM_DECL(io_backend); \ + LOCAL_ATOM_DECL(io_num_threads); \ + LOCAL_ATOM_DECL(listening); \ + LOCAL_ATOM_DECL(local_rwnd); \ + LOCAL_ATOM_DECL(map); \ + LOCAL_ATOM_DECL(max); \ + LOCAL_ATOM_DECL(max_attempts); \ + LOCAL_ATOM_DECL(max_init_timeo); \ + LOCAL_ATOM_DECL(max_instreams); \ + LOCAL_ATOM_DECL(asocmaxrxt); \ + LOCAL_ATOM_DECL(min); \ + LOCAL_ATOM_DECL(missing); \ + LOCAL_ATOM_DECL(mode); \ + LOCAL_ATOM_DECL(msg); \ + LOCAL_ATOM_DECL(msg_flags); \ + LOCAL_ATOM_DECL(mtu); \ + LOCAL_ATOM_DECL(multiaddr); \ + LOCAL_ATOM_DECL(net_unknown); \ + LOCAL_ATOM_DECL(net_unreach); \ + LOCAL_ATOM_DECL(nogroup); \ + LOCAL_ATOM_DECL(none); \ + LOCAL_ATOM_DECL(noroute); \ + LOCAL_ATOM_DECL(not_neighbour); \ + LOCAL_ATOM_DECL(null); \ + LOCAL_ATOM_DECL(num_acceptors); \ + LOCAL_ATOM_DECL(num_cnt_bits); \ + LOCAL_ATOM_DECL(num_dinet); \ + LOCAL_ATOM_DECL(num_dinet6); \ + LOCAL_ATOM_DECL(num_dlocal); \ + LOCAL_ATOM_DECL(num_outstreams); \ + LOCAL_ATOM_DECL(number_peer_destinations); \ + LOCAL_ATOM_DECL(num_pip); \ + LOCAL_ATOM_DECL(num_psctp); \ + LOCAL_ATOM_DECL(num_ptcp); \ + LOCAL_ATOM_DECL(num_pudp); \ + LOCAL_ATOM_DECL(num_readers); \ + LOCAL_ATOM_DECL(num_sockets); \ + LOCAL_ATOM_DECL(num_tdgrams); \ + LOCAL_ATOM_DECL(num_tseqpkgs); \ + LOCAL_ATOM_DECL(num_tstreams); \ + LOCAL_ATOM_DECL(num_writers); \ + LOCAL_ATOM_DECL(offender); \ + LOCAL_ATOM_DECL(onoff); \ + LOCAL_ATOM_DECL(options); \ + LOCAL_ATOM_DECL(origin); \ + LOCAL_ATOM_DECL(otp); \ + LOCAL_ATOM_DECL(otp_socket_option);\ + LOCAL_ATOM_DECL(owner); \ + LOCAL_ATOM_DECL(partial_delivery); \ + LOCAL_ATOM_DECL(peer_error); \ + LOCAL_ATOM_DECL(peer_rwnd); \ + LOCAL_ATOM_DECL(pkt_toobig); \ + LOCAL_ATOM_DECL(policy_fail); \ + LOCAL_ATOM_DECL(port); \ + LOCAL_ATOM_DECL(port_unreach); \ + LOCAL_ATOM_DECL(probe); \ + LOCAL_ATOM_DECL(protocols); \ + LOCAL_ATOM_DECL(rcvctrlbuf); \ + LOCAL_ATOM_DECL(read); \ + LOCAL_ATOM_DECL(read_pkg_max); \ + LOCAL_ATOM_DECL(read_waits); \ + LOCAL_ATOM_DECL(read_write); \ + LOCAL_ATOM_DECL(registry); \ + LOCAL_ATOM_DECL(reject_route); \ + LOCAL_ATOM_DECL(remote); \ + LOCAL_ATOM_DECL(rstates); \ + LOCAL_ATOM_DECL(selected); \ + LOCAL_ATOM_DECL(sender_dry); \ + LOCAL_ATOM_DECL(send_failure); \ + LOCAL_ATOM_DECL(shutdown); \ + LOCAL_ATOM_DECL(sifaddr); \ + LOCAL_ATOM_DECL(sifbrdaddr); \ + LOCAL_ATOM_DECL(sifdstaddr); \ + LOCAL_ATOM_DECL(sifflags); \ + LOCAL_ATOM_DECL(sifmtu); \ + LOCAL_ATOM_DECL(sifnetmask); \ + LOCAL_ATOM_DECL(siftxqlen); \ + LOCAL_ATOM_DECL(slist); \ + LOCAL_ATOM_DECL(sndctrlbuf); \ + LOCAL_ATOM_DECL(socket_debug); \ + LOCAL_ATOM_DECL(socket_level); \ + LOCAL_ATOM_DECL(socket_option); \ + LOCAL_ATOM_DECL(sourceaddr); \ + LOCAL_ATOM_DECL(time_exceeded); \ + LOCAL_ATOM_DECL(true); \ + LOCAL_ATOM_DECL(txstatus); \ + LOCAL_ATOM_DECL(txtime); \ + LOCAL_ATOM_DECL(want); \ + LOCAL_ATOM_DECL(write); \ + LOCAL_ATOM_DECL(write_pkg_max); \ + LOCAL_ATOM_DECL(wstates); \ + LOCAL_ATOM_DECL(zerocopy) + +/* Local error reason atoms + * Keep this (commented) for future use... + */ +/* +#define LOCAL_ERROR_REASON_ATOMS \ + LOCAL_ATOM_DECL(select_read); \ + LOCAL_ATOM_DECL(select_write) +*/ +#define LOCAL_ATOM_DECL(LA) static ERL_NIF_TERM atom_##LA +LOCAL_ATOMS; +// LOCAL_ERROR_REASON_ATOMS; +#undef LOCAL_ATOM_DECL + +/* *** Sockets *** */ +static ErlNifResourceType* esocks; +static ErlNifResourceTypeInit esockInit = { + esock_dtor, + esock_stop, + (ErlNifResourceDown*) esock_down }; -// qsort and bsearch helper -static int cmpESockOpt(const void *vpa, const void *vpb) { - struct ESockOpt *a, *b; - a = (struct ESockOpt *) vpa; - b = (struct ESockOpt *) vpb; - return a->opt < b->opt ? -1 : (a->opt > b->opt ? 1 : 0); -} +// Initiated when the nif is loaded +static ESockData data; + +/* Jump table for the I/O backend (async or sync) */ +static ESockIoBackend io_backend = {0}; + + +/* This, the test for NULL), is temporary until we have a win stub */ +#define ESOCK_IO_INIT(NUMT) \ + ((io_backend.init != NULL) ? \ + io_backend.init((NUMT), &data) : ESOCK_IO_ERR_UNSUPPORTED) +#define ESOCK_IO_FIN() \ + ((io_backend.finish != NULL) ? \ + io_backend.finish() : ESOCK_IO_ERR_UNSUPPORTED) + +#define ESOCK_IO_INFO(ENV) \ + ((io_backend.info != NULL) ? \ + io_backend.info((ENV)) : MKEMA((ENV))) +#define ESOCK_IO_CMD(ENV, CMD, CDATA) \ + ((io_backend.cmd != NULL) ? \ + io_backend.cmd((ENV), (CMD), (CDATA)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SUPPORTS_0(ENV) \ + ((io_backend.supports_0 != NULL) ? \ + io_backend.supports_0((ENV)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SUPPORTS_1(ENV, KEY) \ + ((io_backend.supports_1 != NULL) ? \ + io_backend.supports_1((ENV), (KEY)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) + +#define ESOCK_IO_OPEN_WITH_FD(ENV, FD, EOPTS) \ + ((io_backend.open_with_fd != NULL) ? \ + io_backend.open_with_fd((ENV), (FD), (EOPTS), &data) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_OPEN_PLAIN(ENV, D, T, P, EOPTS) \ + ((io_backend.open_plain != NULL) ? \ + io_backend.open_plain((ENV), (D), \ + (T), (P), (EOPTS), &data) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_BIND(ENV, D, SAP, AL) \ + ((io_backend.bind != NULL) ? \ + io_backend.bind((ENV), (D), (SAP), (AL)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_CONNECT(ENV, D, SR, CR, AP, AL) \ + ((io_backend.connect != NULL) ? \ + io_backend.connect((ENV), (D), \ + (SR), (CR), (AP), (AL)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_LISTEN(ENV, D, BL) \ + ((io_backend.listen != NULL) ? \ + io_backend.listen((ENV), (D), (BL)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_ACCEPT(ENV, D, SR, AR) \ + ((io_backend.accept != NULL) ? \ + io_backend.accept((ENV), (D), (SR), (AR)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SEND(ENV, D, SR, RF, L, F) \ + ((io_backend.send != NULL) ? \ + io_backend.send((ENV), (D), \ + (SR), (RF), (L), (F)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SENDTO(ENV, D, \ + SOCKR, SENDR, \ + DP, F, TAP, TAL) \ + ((io_backend.sendto != NULL) ? \ + io_backend.sendto((ENV), (D), \ + (SOCKR), (SENDR), \ + (DP), (F), (TAP), (TAL)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SENDMSG(ENV, D, \ + SOCKR, SENDR, EM, F, EIOV) \ + ((io_backend.sendmsg != NULL) ? \ + io_backend.sendmsg((ENV), (D), \ + (SOCKR), (SENDR), \ + (EM), (F), (EIOV), &data) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SENDFILE_START(ENV, D, \ + SOR, SNR, \ + O, CN, FR) \ + ((io_backend.sendfile_start != NULL) ? \ + io_backend.sendfile_start((ENV), (D), \ + (SOR), (SNR), \ + (O), (CN), (FR)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SENDFILE_CONT(ENV, D, \ + SOR, SNR, \ + O, CP) \ + ((io_backend.sendfile_cont != NULL) ? \ + io_backend.sendfile_cont((ENV), (D), \ + (SOR), (SNR), \ + (O), (CP)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SENDFILE_DC(ENV, D) \ + ((io_backend.sendfile_dc != NULL) ? \ + io_backend.sendfile_dc((ENV), (D)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_RECV(ENV, D, \ + SR, RR, L, F) \ + ((io_backend.recv != NULL) ? \ + io_backend.recv((ENV), (D), \ + (SR), (RR), (L), (F)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_RECVFROM(ENV, D, \ + SR, RR, L, F) \ + ((io_backend.recvfrom != NULL) ? \ + io_backend.recvfrom((ENV), (D), \ + (SR), (RR), (L), (F)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_RECVMSG(ENV, D, \ + SR, RR, BL, CL, F) \ + ((io_backend.recvmsg != NULL) ? \ + io_backend.recvmsg((ENV), (D), \ + (SR), (RR), \ + (BL), (CL), (F)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_CLOSE(ENV, D) \ + ((io_backend.close != NULL) ? \ + io_backend.close((ENV), (D)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_FIN_CLOSE(ENV, D) \ + ((io_backend.fin_close != NULL) ? \ + io_backend.fin_close((ENV), (D)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SHUTDOWN(ENV, D, H) \ + ((io_backend.shutdown != NULL) ? \ + io_backend.shutdown((ENV), (D), (H)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SOCKNAME(ENV, D) \ + ((io_backend.sockname != NULL) ? \ + io_backend.sockname((ENV), (D)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_PEERNAME(ENV, D) \ + ((io_backend.peername != NULL) ? \ + io_backend.peername((ENV), (D)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_CANCEL_CONNECT(ENV, D, OR) \ + ((io_backend.cancel_connect != NULL) ? \ + io_backend.cancel_connect((ENV), (D), (OR)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_CANCEL_ACCEPT(ENV, D, SR, OR) \ + ((io_backend.cancel_accept != NULL) ? \ + io_backend.cancel_accept((ENV), (D), (SR), (OR)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_CANCEL_SEND(ENV, D, SR, OR) \ + ((io_backend.cancel_send != NULL) ? \ + io_backend.cancel_send((ENV), (D), (SR), (OR)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_CANCEL_RECV(ENV, D, SR, OR) \ + ((io_backend.cancel_recv != NULL) ? \ + io_backend.cancel_recv((ENV), (D), (SR), (OR)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SETOPT(ENV, D, L, O, EV) \ + ((io_backend.setopt != NULL) ? \ + io_backend.setopt((ENV), (D), (L), (O), (EV)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SETOPT_NATIVE(ENV, D, L, O, EV) \ + ((io_backend.setopt_native != NULL) ? \ + io_backend.setopt_native((ENV), (D), (L), (O), (EV)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_SETOPT_OTP(ENV, D, L, O) \ + ((io_backend.setopt_otp != NULL) ? \ + io_backend.setopt_otp((ENV), (D), (L), (O)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_GETOPT(ENV, D, L, O) \ + ((io_backend.getopt != NULL) ? \ + io_backend.getopt((ENV), (D), (L), (O)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_GETOPT_NATIVE(ENV, D, L, O, VS) \ + ((io_backend.getopt_native != NULL) ? \ + io_backend.getopt_native((ENV), (D), (L), (O), (VS)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_GETOPT_OTP(ENV, D, EO) \ + ((io_backend.getopt_otp != NULL) ? \ + io_backend.getopt_otp((ENV), (D), (EO)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_IOCTL_2(ENV, D, R) \ + ((io_backend.ioctl_2 != NULL) ? \ + io_backend.ioctl_2((ENV), (D), (R)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_IOCTL_3(ENV, D, R, A) \ + ((io_backend.ioctl_3 != NULL) ? \ + io_backend.ioctl_3((ENV), (D), (R), (A)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) +#define ESOCK_IO_IOCTL_4(ENV, D, R, A1, A2) \ + ((io_backend.ioctl_4 != NULL) ? \ + io_backend.ioctl_4((ENV), (D), (R), (A1), (A2)) : \ + enif_raise_exception((ENV), MKA((ENV), "notsup"))) + +#define ESOCK_IO_DTOR(ENV, D) \ + ((io_backend.dtor != NULL) ? \ + io_backend.dtor((ENV), (D)) : ((void) (D))) +#define ESOCK_IO_STOP(ENV, D) \ + ((io_backend.stop != NULL) ? \ + io_backend.stop((ENV), (D)) : ((void) (D))) +#define ESOCK_IO_DOWN(ENV, D, PP, MP) \ + ((io_backend.down != NULL) ? \ + io_backend.down((ENV), (D), (PP), (MP)) : ((void) (D))) + +/* ------------------------------------------------------------------------ + * Socket option tables and handling + */ + /* SO_* options -------------------------------------------------------- */ static struct ESockOpt optLevelSocket[] = @@ -2597,11 +3332,11 @@ static struct ESockOpt optLevelIPV6[] = { #if defined(IPV6_RECVPKTINFO) || defined(IPV6_PKTINFO) #if defined(IPV6_RECVPKTINFO) - IPV6_RECVPKTINFO, + IPV6_RECVPKTINFO, #else - IPV6_PKTINFO, + IPV6_PKTINFO, #endif - esock_setopt_bool_opt, esock_getopt_bool_opt, + esock_setopt_bool_opt, esock_getopt_bool_opt, #else 0, NULL, NULL, #endif @@ -2663,7 +3398,7 @@ static struct ESockOpt optLevelIPV6[] = #endif &esock_atom_v6only} - }; + }; #endif // #ifdef HAVE_IPV6 @@ -2852,60 +3587,53 @@ static struct ESockOpt optLevelUDP[] = }; -/* Option levels table ------------------------------------------------- */ -struct ESockOptLevel -{ - int level; // Level number - - size_t num; // Number of options - - struct ESockOpt *opts; // Options table -}; +/* Option levels utility macro */ -// qsort and bsearch helper -static int cmpESockOptLevel(const void *vpa, const void *vpb) { - struct ESockOptLevel *a, *b; - a = (struct ESockOptLevel*) vpa; - b = (struct ESockOptLevel*) vpb; - return a->level < b->level ? -1 : (a->level > b->level ? 1 : 0); -} +#define OPT_LEVEL(Level, Opts, Name) {(Level), NUM(Opts), (Opts), (Name)} -#define OPT_LEVEL(Level, Opts) {(Level), NUM(Opts), (Opts)} /* Table --------------------------------------------------------------- */ static struct ESockOptLevel optLevels[] = { - OPT_LEVEL(SOL_SOCKET, optLevelSocket), + OPT_LEVEL(SOL_SOCKET, optLevelSocket, &esock_atom_socket), +#ifndef __WIN32__ #ifdef SOL_IP - OPT_LEVEL(SOL_IP, optLevelIP), + OPT_LEVEL(SOL_IP, optLevelIP, &esock_atom_ip), #else - OPT_LEVEL(IPPROTO_IP, optLevelIP), + OPT_LEVEL(IPPROTO_IP, optLevelIP, &esock_atom_ip), +#endif +#else + OPT_LEVEL(IPPROTO_IP, optLevelIP, &esock_atom_ip), #endif #ifdef HAVE_IPV6 +#ifndef __WIN32__ #ifdef SOL_IPV6 - OPT_LEVEL(SOL_IPV6, optLevelIPV6), + OPT_LEVEL(SOL_IPV6, optLevelIPV6, &esock_atom_ipv6), +#else + OPT_LEVEL(IPPROTO_IPV6, optLevelIPV6, &esock_atom_ipv6), +#endif #else - OPT_LEVEL(IPPROTO_IPV6, optLevelIPV6), + OPT_LEVEL(IPPROTO_IPV6, optLevelIPV6, &esock_atom_ipv6), #endif #endif // #ifdef HAVE_IPV6 #ifdef HAVE_SCTP - OPT_LEVEL(IPPROTO_SCTP, optLevelSCTP), + OPT_LEVEL(IPPROTO_SCTP, optLevelSCTP, &esock_atom_sctp), #endif // #ifdef HAVE_SCTP - OPT_LEVEL(IPPROTO_UDP, optLevelUDP), - OPT_LEVEL(IPPROTO_TCP, optLevelTCP) + OPT_LEVEL(IPPROTO_UDP, optLevelUDP, &esock_atom_udp), + OPT_LEVEL(IPPROTO_TCP, optLevelTCP, &esock_atom_tcp) }; #undef OPT_LEVEL /* Tables init (sorting) ----------------------------------------------- */ -#define ESOCK_SORT_TABLE(Array, Cmp) \ +#define ESOCK_SORT_TABLE(Array, Cmp) \ qsort((Array), NUM(Array), sizeof(*(Array)), (Cmp)) static void initOpts(void) { @@ -2922,945 +3650,74 @@ static void initOpts(void) { ESOCK_SORT_TABLE(optLevels, cmpESockOptLevel); } + +/* ------------------------------------------------------------------------ + * Socket option tables and handling + */ + +// qsort and bsearch helper(s) +static +int cmpESockOpt(const void *vpa, const void *vpb) +{ + struct ESockOpt *a, *b; + a = (struct ESockOpt *) vpa; + b = (struct ESockOpt *) vpb; + return a->opt < b->opt ? -1 : (a->opt > b->opt ? 1 : 0); +} + +static +int cmpESockOptLevel(const void *vpa, const void *vpb) +{ + struct ESockOptLevel *a, *b; + a = (struct ESockOptLevel*) vpa; + b = (struct ESockOptLevel*) vpb; + return a->level < b->level ? -1 : (a->level > b->level ? 1 : 0); +} + /* Option lookup in tables --------------------------------------------- */ -static struct ESockOpt *lookupOpt(int level, int opt) { +static +struct ESockOpt *lookupOpt(int level, int opt) +{ struct ESockOptLevel levelKey, *levelP; struct ESockOpt optKey; sys_memzero((char *) &levelKey, sizeof(levelKey)); levelKey.level = level; - levelP = - bsearch(&levelKey, optLevels, NUM(optLevels), sizeof(*optLevels), - cmpESockOptLevel); + levelP = bsearch(&levelKey, optLevels, NUM(optLevels), sizeof(*optLevels), + cmpESockOptLevel); if (levelP == NULL) return NULL; sys_memzero((char *) &optKey, sizeof(optKey)); optKey.opt = opt; - return - bsearch(&optKey, levelP->opts, levelP->num, sizeof(*levelP->opts), - cmpESockOpt); + return bsearch(&optKey, levelP->opts, levelP->num, sizeof(*levelP->opts), + cmpESockOpt); } -/* --------------------------------------------------------------------- */ - -#if defined(IP_TOS) -static BOOLEAN_T decode_ip_tos(ErlNifEnv* env, - ERL_NIF_TERM eVal, - int* val); -#endif -#if defined(IP_MTU_DISCOVER) -static BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv* env, - ERL_NIF_TERM eVal, - int* val); -#endif -#if defined(IP_MTU_DISCOVER) -static void encode_ip_pmtudisc(ErlNifEnv* env, - int val, - ERL_NIF_TERM* eVal); -#endif -#if defined(IPV6_MTU_DISCOVER) -static BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, - ERL_NIF_TERM eVal, - int* val); -#endif -#if defined(IPV6_MTU_DISCOVER) -static void encode_ipv6_pmtudisc(ErlNifEnv* env, - int val, - ERL_NIF_TERM* eVal); -#endif -#if defined(IPV6_MULTICAST_HOPS) || defined(IPV6_UNICAST_HOPS) -static -BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val); -#endif - -/* -static BOOLEAN_T decode_bool(ErlNifEnv* env, - ERL_NIF_TERM eVal, - BOOLEAN_T* val); -*/ -// static void encode_bool(BOOLEAN_T val, ERL_NIF_TERM* eVal); -static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val); - -#if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO) -static BOOLEAN_T decode_sctp_assoc_t(ErlNifEnv* env, - ERL_NIF_TERM eVal, - sctp_assoc_t* val); -static ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env, - sctp_assoc_t val); -#endif // #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO) - -static void esock_stop_handle_current(ErlNifEnv* env, - const char* role, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ESockRequestor* reqP); -static void inform_waiting_procs(ErlNifEnv* env, - const char* role, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ESockRequestQueue* q, - ERL_NIF_TERM reason); - -static int socket_setopt(int sock, - int level, - int opt, - const void* optVal, - const socklen_t optLen); - -static BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how); - -/* *** esock_activate_next_acceptor *** - * *** esock_activate_next_writer *** - * *** esock_activate_next_reader *** - * - * All the activate-next functions for acceptor, writer and reader - * have exactly the same API, so we apply some macro magic to simplify. - * They simply operates on dufferent data structures. - * - */ - -#define ACTIVATE_NEXT_FUNCS_DEFS \ - ACTIVATE_NEXT_FUNC_DEF(acceptor) \ - ACTIVATE_NEXT_FUNC_DEF(writer) \ - ACTIVATE_NEXT_FUNC_DEF(reader) - -#define ACTIVATE_NEXT_FUNC_DEF(F) \ - extern BOOLEAN_T esock_activate_next_##F(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ERL_NIF_TERM sockRef); -ACTIVATE_NEXT_FUNCS_DEFS -#undef ACTIVATE_NEXT_FUNC_DEF - -/* esock_acceptor_search4pid | esock_writer_search4pid | esock_reader_search4pid - * esock_acceptor_push | esock_writer_push | esock_reader_push - * esock_acceptor_pop | esock_writer_pop | esock_reader_pop - * esock_acceptor_unqueue | esock_writer_unqueue | esock_reader_unqueue - * - * All the queue operator functions (search4pid, push, pop - * and unqueue) for acceptor, writer and reader has exactly - * the same API, so we apply some macro magic to simplify. - */ - -#define ESOCK_OPERATOR_FUNCS_DEFS \ - ESOCK_OPERATOR_FUNCS_DEF(acceptor) \ - ESOCK_OPERATOR_FUNCS_DEF(writer) \ - ESOCK_OPERATOR_FUNCS_DEF(reader) - -#define ESOCK_OPERATOR_FUNCS_DEF(O) \ - extern BOOLEAN_T esock_##O##_search4pid(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ErlNifPid* pid); \ - extern void esock_##O##_push(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ErlNifPid pid, \ - ERL_NIF_TERM ref); \ - extern BOOLEAN_T esock_##O##_pop(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ESockRequestor* reqP); \ - extern BOOLEAN_T esock_##O##_unqueue(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ERL_NIF_TERM* refP, \ - const ErlNifPid* pidP); -ESOCK_OPERATOR_FUNCS_DEFS -#undef ESOCK_OPERATOR_FUNCS_DEF - -static BOOLEAN_T qsearch4pid(ErlNifEnv* env, - ESockRequestQueue* q, - ErlNifPid* pid); -static void qpush(ESockRequestQueue* q, - ESockRequestQueueElement* e); -static ESockRequestQueueElement* qpop(ESockRequestQueue* q); -static BOOLEAN_T qunqueue(ErlNifEnv* env, - ESockDescriptor* descP, - const char* slogan, - ESockRequestQueue* q, - ERL_NIF_TERM* refP, - const ErlNifPid* pidP); - -static void esock_down_ctrl(ErlNifEnv* env, - ESockDescriptor* descP, - const ErlNifPid* pidP); -static void esock_down_acceptor(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pidP, - const ErlNifMonitor* monP); -static void esock_down_writer(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pidP, - const ErlNifMonitor* monP); -static void esock_down_reader(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pidP, - const ErlNifMonitor* monP); - -#ifdef HAVE_SENDFILE -static void -esock_send_sendfile_deferred_close_msg(ErlNifEnv* env, - ESockDescriptor* descP); -#endif -static BOOLEAN_T esock_send_msg(ErlNifEnv* env, - ErlNifPid* pid, - ERL_NIF_TERM msg, - ErlNifEnv* msgEnv); - -static ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef); -static ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef); -static ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env, - ERL_NIF_TERM tag, - ERL_NIF_TERM sockRef); -static ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef, - ERL_NIF_TERM reason); -static ERL_NIF_TERM mk_wrap_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM cnt); -static ERL_NIF_TERM mk_close_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM closeRef); -static ERL_NIF_TERM mk_select_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM selectRef); -static ERL_NIF_TERM mk_socket_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM tag, - ERL_NIF_TERM info); -static ERL_NIF_TERM mk_socket(ErlNifEnv* env, - ERL_NIF_TERM sockRef); - -/* -static int esock_select_read(ErlNifEnv* env, - ErlNifEvent event, - void* obj, - const ErlNifPid* pidP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM selectRef); -static int esock_select_write(ErlNifEnv* env, - ErlNifEvent event, - void* obj, - const ErlNifPid* pidP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM selectRef); -static int esock_select_stop(ErlNifEnv* env, - ErlNifEvent event, - void* obj); -static int esock_select_cancel(ErlNifEnv* env, - ErlNifEvent event, - enum ErlNifSelectFlags mode, - void* obj); -*/ - -static char* extract_debug_filename(ErlNifEnv* env, - ERL_NIF_TERM map); - - -/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * - * * - * * - * End of non-__WIN32__ section a.k.a UNIX section * - * * - * * - * ---------------------------------------------------------------------- */ -#endif // #ifndef __WIN32__ - - -/* -#if defined(HAS_AF_LOCAL) || defined(SO_BINDTODEVICE) -static size_t my_strnlen(const char *s, size_t maxlen); -#endif -*/ - -static void esock_dtor(ErlNifEnv* env, void* obj); -static void esock_stop(ErlNifEnv* env, - void* obj, - ErlNifEvent fd, - int is_direct_call); -static void esock_down(ErlNifEnv* env, - void* obj, - const ErlNifPid* pidP, - const ErlNifMonitor* monP); - -static void esock_on_halt(void* priv_data); - -static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); - - - -#if HAVE_IN6 -# if ! defined(HAVE_IN6ADDR_ANY) || ! HAVE_IN6ADDR_ANY -# if HAVE_DECL_IN6ADDR_ANY_INIT -static const struct in6_addr in6addr_any = { { IN6ADDR_ANY_INIT } }; -# else -static const struct in6_addr in6addr_any = - { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; -# endif /* HAVE_IN6ADDR_ANY_INIT */ -# endif /* ! HAVE_DECL_IN6ADDR_ANY */ - -# if ! defined(HAVE_IN6ADDR_LOOPBACK) || ! HAVE_IN6ADDR_LOOPBACK -# if HAVE_DECL_IN6ADDR_LOOPBACK_INIT -static const struct in6_addr in6addr_loopback = - { { IN6ADDR_LOOPBACK_INIT } }; -# else -static const struct in6_addr in6addr_loopback = - { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }; -# endif /* HAVE_IN6ADDR_LOOPBACk_INIT */ -# endif /* ! HAVE_DECL_IN6ADDR_LOOPBACK */ -#endif /* HAVE_IN6 */ - - - -/* *** Global atoms *** - * Note that when an (global) atom is added here, it must also be added - * in the socket_int.h file! - */ -#define GLOBAL_ATOMS \ - GLOBAL_ATOM_DECL(abort); \ - GLOBAL_ATOM_DECL(accept); \ - GLOBAL_ATOM_DECL(acceptconn); \ - GLOBAL_ATOM_DECL(acceptfilter); \ - GLOBAL_ATOM_DECL(acc_success); \ - GLOBAL_ATOM_DECL(acc_fails); \ - GLOBAL_ATOM_DECL(acc_tries); \ - GLOBAL_ATOM_DECL(acc_waits); \ - GLOBAL_ATOM_DECL(adaption_layer); \ - GLOBAL_ATOM_DECL(addr); \ - GLOBAL_ATOM_DECL(addrform); \ - GLOBAL_ATOM_DECL(add_membership); \ - GLOBAL_ATOM_DECL(add_source_membership); \ - GLOBAL_ATOM_DECL(alen); \ - GLOBAL_ATOM_DECL(allmulti); \ - GLOBAL_ATOM_DECL(already); \ - GLOBAL_ATOM_DECL(any); \ - GLOBAL_ATOM_DECL(appletlk); \ - GLOBAL_ATOM_DECL(arcnet); \ - GLOBAL_ATOM_DECL(associnfo); \ - GLOBAL_ATOM_DECL(atm); \ - GLOBAL_ATOM_DECL(authhdr); \ - GLOBAL_ATOM_DECL(auth_active_key); \ - GLOBAL_ATOM_DECL(auth_asconf); \ - GLOBAL_ATOM_DECL(auth_chunk); \ - GLOBAL_ATOM_DECL(auth_delete_key); \ - GLOBAL_ATOM_DECL(auth_key); \ - GLOBAL_ATOM_DECL(auth_level); \ - GLOBAL_ATOM_DECL(autoclose); \ - GLOBAL_ATOM_DECL(automedia); \ - GLOBAL_ATOM_DECL(ax25); \ - GLOBAL_ATOM_DECL(bad_data); \ - GLOBAL_ATOM_DECL(bindtodevice); \ - GLOBAL_ATOM_DECL(block_source); \ - GLOBAL_ATOM_DECL(broadcast); \ - GLOBAL_ATOM_DECL(busy_poll); \ - GLOBAL_ATOM_DECL(cantconfig); \ - GLOBAL_ATOM_DECL(chaos); \ - GLOBAL_ATOM_DECL(checksum); \ - GLOBAL_ATOM_DECL(close); \ - GLOBAL_ATOM_DECL(closed); \ - GLOBAL_ATOM_DECL(cmsg_cloexec); \ - GLOBAL_ATOM_DECL(command); \ - GLOBAL_ATOM_DECL(confirm); \ - GLOBAL_ATOM_DECL(congestion); \ - GLOBAL_ATOM_DECL(connect); \ - GLOBAL_ATOM_DECL(connected); \ - GLOBAL_ATOM_DECL(connecting); \ - GLOBAL_ATOM_DECL(context); \ - GLOBAL_ATOM_DECL(cork); \ - GLOBAL_ATOM_DECL(credentials); \ - GLOBAL_ATOM_DECL(ctrl); \ - GLOBAL_ATOM_DECL(ctrunc); \ - GLOBAL_ATOM_DECL(data); \ - GLOBAL_ATOM_DECL(data_size); \ - GLOBAL_ATOM_DECL(debug); \ - GLOBAL_ATOM_DECL(default); \ - GLOBAL_ATOM_DECL(default_send_params); \ - GLOBAL_ATOM_DECL(delayed_ack_time); \ - GLOBAL_ATOM_DECL(dgram); \ - GLOBAL_ATOM_DECL(disable_fragments); \ - GLOBAL_ATOM_DECL(dlci); \ - GLOBAL_ATOM_DECL(domain); \ - GLOBAL_ATOM_DECL(dontfrag); \ - GLOBAL_ATOM_DECL(dontroute); \ - GLOBAL_ATOM_DECL(dormant); \ - GLOBAL_ATOM_DECL(drop_membership); \ - GLOBAL_ATOM_DECL(drop_source_membership); \ - GLOBAL_ATOM_DECL(dstopts); \ - GLOBAL_ATOM_DECL(dup); \ - GLOBAL_ATOM_DECL(dying); \ - GLOBAL_ATOM_DECL(dynamic); \ - GLOBAL_ATOM_DECL(echo); \ - GLOBAL_ATOM_DECL(eether); \ - GLOBAL_ATOM_DECL(efile); \ - GLOBAL_ATOM_DECL(egp); \ - GLOBAL_ATOM_DECL(enotsup); \ - GLOBAL_ATOM_DECL(eor); \ - GLOBAL_ATOM_DECL(error); \ - GLOBAL_ATOM_DECL(errqueue); \ - GLOBAL_ATOM_DECL(esp_network_level); \ - GLOBAL_ATOM_DECL(esp_trans_level); \ - GLOBAL_ATOM_DECL(ether); \ - GLOBAL_ATOM_DECL(eui64); \ - GLOBAL_ATOM_DECL(events); \ - GLOBAL_ATOM_DECL(explicit_eor); \ - GLOBAL_ATOM_DECL(faith); \ - GLOBAL_ATOM_DECL(false); \ - GLOBAL_ATOM_DECL(family); \ - GLOBAL_ATOM_DECL(fastroute); \ - GLOBAL_ATOM_DECL(flags); \ - GLOBAL_ATOM_DECL(flowinfo); \ - GLOBAL_ATOM_DECL(fragment_interleave); \ - GLOBAL_ATOM_DECL(freebind); \ - GLOBAL_ATOM_DECL(frelay); \ - GLOBAL_ATOM_DECL(get_peer_addr_info); \ - GLOBAL_ATOM_DECL(hatype); \ - GLOBAL_ATOM_DECL(hdrincl); \ - GLOBAL_ATOM_DECL(hmac_ident); \ - GLOBAL_ATOM_DECL(hoplimit); \ - GLOBAL_ATOM_DECL(hopopts); \ - GLOBAL_ATOM_DECL(host); \ - GLOBAL_ATOM_DECL(icmp); \ - GLOBAL_ATOM_DECL(icmp6); \ - GLOBAL_ATOM_DECL(ieee802); \ - GLOBAL_ATOM_DECL(ieee1394); \ - GLOBAL_ATOM_DECL(ifindex); \ - GLOBAL_ATOM_DECL(igmp); \ - GLOBAL_ATOM_DECL(implink); \ - GLOBAL_ATOM_DECL(index); \ - GLOBAL_ATOM_DECL(inet); \ - GLOBAL_ATOM_DECL(inet6); \ - GLOBAL_ATOM_DECL(infiniband); \ - GLOBAL_ATOM_DECL(info); \ - GLOBAL_ATOM_DECL(initmsg); \ - GLOBAL_ATOM_DECL(invalid); \ - GLOBAL_ATOM_DECL(integer_range); \ - GLOBAL_ATOM_DECL(iov); \ - GLOBAL_ATOM_DECL(ip); \ - GLOBAL_ATOM_DECL(ipcomp_level); \ - GLOBAL_ATOM_DECL(ipip); \ - GLOBAL_ATOM_DECL(ipv6); \ - GLOBAL_ATOM_DECL(i_want_mapped_v4_addr); \ - GLOBAL_ATOM_DECL(join_group); \ - GLOBAL_ATOM_DECL(keepalive); \ - GLOBAL_ATOM_DECL(keepcnt); \ - GLOBAL_ATOM_DECL(keepidle); \ - GLOBAL_ATOM_DECL(keepintvl); \ - GLOBAL_ATOM_DECL(kernel); \ - GLOBAL_ATOM_DECL(knowsepoch); \ - GLOBAL_ATOM_DECL(leave_group); \ - GLOBAL_ATOM_DECL(level); \ - GLOBAL_ATOM_DECL(linger); \ - GLOBAL_ATOM_DECL(link); \ - GLOBAL_ATOM_DECL(link0); \ - GLOBAL_ATOM_DECL(link1); \ - GLOBAL_ATOM_DECL(link2); \ - GLOBAL_ATOM_DECL(local); \ - GLOBAL_ATOM_DECL(localtlk); \ - GLOBAL_ATOM_DECL(local_auth_chunks); \ - GLOBAL_ATOM_DECL(loopback); \ - GLOBAL_ATOM_DECL(lowdelay); \ - GLOBAL_ATOM_DECL(lower_up); \ - GLOBAL_ATOM_DECL(mark); \ - GLOBAL_ATOM_DECL(master); \ - GLOBAL_ATOM_DECL(maxburst); \ - GLOBAL_ATOM_DECL(maxseg); \ - GLOBAL_ATOM_DECL(md5sig); \ - GLOBAL_ATOM_DECL(metricom); \ - GLOBAL_ATOM_DECL(mincost); \ - GLOBAL_ATOM_DECL(minttl); \ - GLOBAL_ATOM_DECL(monitor); \ - GLOBAL_ATOM_DECL(more); \ - GLOBAL_ATOM_DECL(msfilter); \ - GLOBAL_ATOM_DECL(mtu); \ - GLOBAL_ATOM_DECL(mtu_discover); \ - GLOBAL_ATOM_DECL(multicast); \ - GLOBAL_ATOM_DECL(multicast_all); \ - GLOBAL_ATOM_DECL(multicast_hops); \ - GLOBAL_ATOM_DECL(multicast_if); \ - GLOBAL_ATOM_DECL(multicast_loop); \ - GLOBAL_ATOM_DECL(multicast_ttl); \ - GLOBAL_ATOM_DECL(name); \ - GLOBAL_ATOM_DECL(netns); \ - GLOBAL_ATOM_DECL(netrom); \ - GLOBAL_ATOM_DECL(nlen); \ - GLOBAL_ATOM_DECL(noarp); \ - GLOBAL_ATOM_DECL(nodelay); \ - GLOBAL_ATOM_DECL(nodefrag); \ - GLOBAL_ATOM_DECL(nogroup); \ - GLOBAL_ATOM_DECL(none); \ - GLOBAL_ATOM_DECL(noopt); \ - GLOBAL_ATOM_DECL(nopush); \ - GLOBAL_ATOM_DECL(nosignal); \ - GLOBAL_ATOM_DECL(notrailers); \ - GLOBAL_ATOM_DECL(not_found); \ - GLOBAL_ATOM_DECL(not_owner); \ - GLOBAL_ATOM_DECL(oactive); \ - GLOBAL_ATOM_DECL(ok); \ - GLOBAL_ATOM_DECL(oob); \ - GLOBAL_ATOM_DECL(oobinline); \ - GLOBAL_ATOM_DECL(options); \ - GLOBAL_ATOM_DECL(origdstaddr); \ - GLOBAL_ATOM_DECL(otherhost); \ - GLOBAL_ATOM_DECL(outgoing); \ - GLOBAL_ATOM_DECL(packet); \ - GLOBAL_ATOM_DECL(partial_delivery_point); \ - GLOBAL_ATOM_DECL(passcred); \ - GLOBAL_ATOM_DECL(path); \ - GLOBAL_ATOM_DECL(peek); \ - GLOBAL_ATOM_DECL(peek_off); \ - GLOBAL_ATOM_DECL(peer_addr_params); \ - GLOBAL_ATOM_DECL(peer_auth_chunks); \ - GLOBAL_ATOM_DECL(peercred); \ - GLOBAL_ATOM_DECL(pktinfo); \ - GLOBAL_ATOM_DECL(pktoptions); \ - GLOBAL_ATOM_DECL(pkttype); \ - GLOBAL_ATOM_DECL(pointopoint); \ - GLOBAL_ATOM_DECL(port); \ - GLOBAL_ATOM_DECL(portrange); \ - GLOBAL_ATOM_DECL(portsel); \ - GLOBAL_ATOM_DECL(ppromisc); \ - GLOBAL_ATOM_DECL(primary_addr); \ - GLOBAL_ATOM_DECL(prim_file); \ - GLOBAL_ATOM_DECL(priority); \ - GLOBAL_ATOM_DECL(promisc); \ - GLOBAL_ATOM_DECL(pronet); \ - GLOBAL_ATOM_DECL(protocol); \ - GLOBAL_ATOM_DECL(pup); \ - GLOBAL_ATOM_DECL(raw); \ - GLOBAL_ATOM_DECL(rcvbuf); \ - GLOBAL_ATOM_DECL(rcvbufforce); \ - GLOBAL_ATOM_DECL(rcvlowat); \ - GLOBAL_ATOM_DECL(rcvtimeo); \ - GLOBAL_ATOM_DECL(rdm); \ - GLOBAL_ATOM_DECL(read_byte); \ - GLOBAL_ATOM_DECL(read_fails); \ - GLOBAL_ATOM_DECL(read_pkg); \ - GLOBAL_ATOM_DECL(read_tries); \ - GLOBAL_ATOM_DECL(recv); \ - GLOBAL_ATOM_DECL(recvdstaddr); \ - GLOBAL_ATOM_DECL(recverr); \ - GLOBAL_ATOM_DECL(recvfrom); \ - GLOBAL_ATOM_DECL(recvhoplimit); \ - GLOBAL_ATOM_DECL(recvif); \ - GLOBAL_ATOM_DECL(recvmsg); \ - GLOBAL_ATOM_DECL(recvopts); \ - GLOBAL_ATOM_DECL(recvorigdstaddr); \ - GLOBAL_ATOM_DECL(recvpktinfo); \ - GLOBAL_ATOM_DECL(recvtclass); \ - GLOBAL_ATOM_DECL(recvtos); \ - GLOBAL_ATOM_DECL(recvttl); \ - GLOBAL_ATOM_DECL(reliability); \ - GLOBAL_ATOM_DECL(renaming); \ - GLOBAL_ATOM_DECL(reset_streams); \ - GLOBAL_ATOM_DECL(retopts); \ - GLOBAL_ATOM_DECL(reuseaddr); \ - GLOBAL_ATOM_DECL(reuseport); \ - GLOBAL_ATOM_DECL(rights); \ - GLOBAL_ATOM_DECL(router_alert); \ - GLOBAL_ATOM_DECL(rthdr); \ - GLOBAL_ATOM_DECL(rtoinfo); \ - GLOBAL_ATOM_DECL(running); \ - GLOBAL_ATOM_DECL(rxq_ovfl); \ - GLOBAL_ATOM_DECL(scope_id); \ - GLOBAL_ATOM_DECL(sctp); \ - GLOBAL_ATOM_DECL(sec); \ - GLOBAL_ATOM_DECL(select); \ - GLOBAL_ATOM_DECL(select_failed); \ - GLOBAL_ATOM_DECL(select_sent); \ - GLOBAL_ATOM_DECL(send); \ - GLOBAL_ATOM_DECL(sendfile); \ - GLOBAL_ATOM_DECL(sendfile_byte); \ - GLOBAL_ATOM_DECL(sendfile_deferred_close); \ - GLOBAL_ATOM_DECL(sendfile_fails); \ - GLOBAL_ATOM_DECL(sendfile_max); \ - GLOBAL_ATOM_DECL(sendfile_pkg); \ - GLOBAL_ATOM_DECL(sendfile_pkg_max); \ - GLOBAL_ATOM_DECL(sendfile_tries); \ - GLOBAL_ATOM_DECL(sendfile_waits); \ - GLOBAL_ATOM_DECL(sendmsg); \ - GLOBAL_ATOM_DECL(sendsrcaddr); \ - GLOBAL_ATOM_DECL(sendto); \ - GLOBAL_ATOM_DECL(seqpacket); \ - GLOBAL_ATOM_DECL(setfib); \ - GLOBAL_ATOM_DECL(set_peer_primary_addr); \ - GLOBAL_ATOM_DECL(simplex); \ - GLOBAL_ATOM_DECL(slave); \ - GLOBAL_ATOM_DECL(slen); \ - GLOBAL_ATOM_DECL(sndbuf); \ - GLOBAL_ATOM_DECL(sndbufforce); \ - GLOBAL_ATOM_DECL(sndlowat); \ - GLOBAL_ATOM_DECL(sndtimeo); \ - GLOBAL_ATOM_DECL(socket); \ - GLOBAL_ATOM_DECL(spec_dst); \ - GLOBAL_ATOM_DECL(staticarp); \ - GLOBAL_ATOM_DECL(state); \ - GLOBAL_ATOM_DECL(status); \ - GLOBAL_ATOM_DECL(stream); \ - GLOBAL_ATOM_DECL(syncnt); \ - GLOBAL_ATOM_DECL(tclass); \ - GLOBAL_ATOM_DECL(tcp); \ - GLOBAL_ATOM_DECL(throughput); \ - GLOBAL_ATOM_DECL(timestamp); \ - GLOBAL_ATOM_DECL(tos); \ - GLOBAL_ATOM_DECL(transparent); \ - GLOBAL_ATOM_DECL(timeout); \ - GLOBAL_ATOM_DECL(true); \ - GLOBAL_ATOM_DECL(trunc); \ - GLOBAL_ATOM_DECL(ttl); \ - GLOBAL_ATOM_DECL(tunnel); \ - GLOBAL_ATOM_DECL(tunnel6); \ - GLOBAL_ATOM_DECL(type); \ - GLOBAL_ATOM_DECL(udp); \ - GLOBAL_ATOM_DECL(unblock_source); \ - GLOBAL_ATOM_DECL(undefined); \ - GLOBAL_ATOM_DECL(unicast_hops); \ - GLOBAL_ATOM_DECL(unspec); \ - GLOBAL_ATOM_DECL(up); \ - GLOBAL_ATOM_DECL(usec); \ - GLOBAL_ATOM_DECL(user); \ - GLOBAL_ATOM_DECL(user_timeout); \ - GLOBAL_ATOM_DECL(use_ext_recvinfo); \ - GLOBAL_ATOM_DECL(use_min_mtu); \ - GLOBAL_ATOM_DECL(use_registry); \ - GLOBAL_ATOM_DECL(value); \ - GLOBAL_ATOM_DECL(void); \ - GLOBAL_ATOM_DECL(v6only); \ - GLOBAL_ATOM_DECL(write_byte); \ - GLOBAL_ATOM_DECL(write_fails); \ - GLOBAL_ATOM_DECL(write_pkg); \ - GLOBAL_ATOM_DECL(write_tries); \ - GLOBAL_ATOM_DECL(write_waits); \ - GLOBAL_ATOM_DECL(zero) - - - -/* *** Global error reason atoms *** */ -#define GLOBAL_ERROR_REASON_ATOMS \ - GLOBAL_ATOM_DECL(eagain); \ - GLOBAL_ATOM_DECL(einval); \ - GLOBAL_ATOM_DECL(select_read); \ - GLOBAL_ATOM_DECL(select_write) - - -#define GLOBAL_ATOM_DECL(A) ERL_NIF_TERM esock_atom_##A -GLOBAL_ATOMS; -GLOBAL_ERROR_REASON_ATOMS; -#undef GLOBAL_ATOM_DECL -ERL_NIF_TERM esock_atom_socket_tag; // This has a "special" name ('$socket') - -/* *** Local atoms *** */ -#define LOCAL_ATOMS \ - LOCAL_ATOM_DECL(accepting); \ - LOCAL_ATOM_DECL(adaptation_layer); \ - LOCAL_ATOM_DECL(add); \ - LOCAL_ATOM_DECL(addr_unreach); \ - LOCAL_ATOM_DECL(address); \ - LOCAL_ATOM_DECL(adm_prohibited); \ - LOCAL_ATOM_DECL(association); \ - LOCAL_ATOM_DECL(assoc_id); \ - LOCAL_ATOM_DECL(authentication); \ - LOCAL_ATOM_DECL(base_addr); \ - LOCAL_ATOM_DECL(boolean); \ - LOCAL_ATOM_DECL(bound); \ - LOCAL_ATOM_DECL(bufsz); \ - LOCAL_ATOM_DECL(close); \ - LOCAL_ATOM_DECL(closing); \ - LOCAL_ATOM_DECL(code); \ - LOCAL_ATOM_DECL(cookie_life); \ - LOCAL_ATOM_DECL(counter_wrap); \ - LOCAL_ATOM_DECL(counters); \ - LOCAL_ATOM_DECL(ctype); \ - LOCAL_ATOM_DECL(data_io); \ - LOCAL_ATOM_DECL(debug_filename); \ - LOCAL_ATOM_DECL(del); \ - LOCAL_ATOM_DECL(dest_unreach); \ - LOCAL_ATOM_DECL(dma); \ - LOCAL_ATOM_DECL(do); \ - LOCAL_ATOM_DECL(dont); \ - LOCAL_ATOM_DECL(dtor); \ - LOCAL_ATOM_DECL(exclude); \ - LOCAL_ATOM_DECL(false); \ - LOCAL_ATOM_DECL(frag_needed); \ - LOCAL_ATOM_DECL(gifaddr); \ - LOCAL_ATOM_DECL(gifbrdaddr); \ - LOCAL_ATOM_DECL(gifconf); \ - LOCAL_ATOM_DECL(gifdstaddr); \ - LOCAL_ATOM_DECL(gifflags); \ - LOCAL_ATOM_DECL(gifhwaddr); \ - LOCAL_ATOM_DECL(gifindex); \ - LOCAL_ATOM_DECL(gifmap); \ - LOCAL_ATOM_DECL(gifmtu); \ - LOCAL_ATOM_DECL(gifname); \ - LOCAL_ATOM_DECL(gifnetmask); \ - LOCAL_ATOM_DECL(giftxqlen); \ - LOCAL_ATOM_DECL(host_unknown); \ - LOCAL_ATOM_DECL(host_unreach); \ - LOCAL_ATOM_DECL(how); \ - LOCAL_ATOM_DECL(in4_sockaddr); \ - LOCAL_ATOM_DECL(in6_sockaddr); \ - LOCAL_ATOM_DECL(include); \ - LOCAL_ATOM_DECL(initial); \ - LOCAL_ATOM_DECL(interface); \ - LOCAL_ATOM_DECL(integer); \ - LOCAL_ATOM_DECL(ioctl_flags); \ - LOCAL_ATOM_DECL(ioctl_requests); \ - LOCAL_ATOM_DECL(iov_max); \ - LOCAL_ATOM_DECL(iow); \ - LOCAL_ATOM_DECL(io_num_threads); \ - LOCAL_ATOM_DECL(irq); \ - LOCAL_ATOM_DECL(listening); \ - LOCAL_ATOM_DECL(local_rwnd); \ - LOCAL_ATOM_DECL(map); \ - LOCAL_ATOM_DECL(max); \ - LOCAL_ATOM_DECL(max_attempts); \ - LOCAL_ATOM_DECL(max_init_timeo); \ - LOCAL_ATOM_DECL(max_instreams); \ - LOCAL_ATOM_DECL(asocmaxrxt); \ - LOCAL_ATOM_DECL(mem_end); \ - LOCAL_ATOM_DECL(mem_start); \ - LOCAL_ATOM_DECL(min); \ - LOCAL_ATOM_DECL(missing); \ - LOCAL_ATOM_DECL(mode); \ - LOCAL_ATOM_DECL(msg); \ - LOCAL_ATOM_DECL(msg_flags); \ - LOCAL_ATOM_DECL(mtu); \ - LOCAL_ATOM_DECL(multiaddr); \ - LOCAL_ATOM_DECL(net_unknown); \ - LOCAL_ATOM_DECL(net_unreach); \ - LOCAL_ATOM_DECL(nogroup); \ - LOCAL_ATOM_DECL(none); \ - LOCAL_ATOM_DECL(noroute); \ - LOCAL_ATOM_DECL(not_neighbour); \ - LOCAL_ATOM_DECL(null); \ - LOCAL_ATOM_DECL(num_acceptors); \ - LOCAL_ATOM_DECL(num_cnt_bits); \ - LOCAL_ATOM_DECL(num_dinet); \ - LOCAL_ATOM_DECL(num_dinet6); \ - LOCAL_ATOM_DECL(num_dlocal); \ - LOCAL_ATOM_DECL(num_outstreams); \ - LOCAL_ATOM_DECL(number_peer_destinations); \ - LOCAL_ATOM_DECL(num_pip); \ - LOCAL_ATOM_DECL(num_psctp); \ - LOCAL_ATOM_DECL(num_ptcp); \ - LOCAL_ATOM_DECL(num_pudp); \ - LOCAL_ATOM_DECL(num_readers); \ - LOCAL_ATOM_DECL(num_sockets); \ - LOCAL_ATOM_DECL(num_tdgrams); \ - LOCAL_ATOM_DECL(num_tseqpkgs); \ - LOCAL_ATOM_DECL(num_tstreams); \ - LOCAL_ATOM_DECL(num_writers); \ - LOCAL_ATOM_DECL(offender); \ - LOCAL_ATOM_DECL(onoff); \ - LOCAL_ATOM_DECL(options); \ - LOCAL_ATOM_DECL(origin); \ - LOCAL_ATOM_DECL(otp); \ - LOCAL_ATOM_DECL(otp_socket_option);\ - LOCAL_ATOM_DECL(owner); \ - LOCAL_ATOM_DECL(partial_delivery); \ - LOCAL_ATOM_DECL(peer_error); \ - LOCAL_ATOM_DECL(peer_rwnd); \ - LOCAL_ATOM_DECL(pkt_toobig); \ - LOCAL_ATOM_DECL(policy_fail); \ - LOCAL_ATOM_DECL(port); \ - LOCAL_ATOM_DECL(port_unreach); \ - LOCAL_ATOM_DECL(probe); \ - LOCAL_ATOM_DECL(protocols); \ - LOCAL_ATOM_DECL(rcvctrlbuf); \ - LOCAL_ATOM_DECL(read); \ - LOCAL_ATOM_DECL(read_pkg_max); \ - LOCAL_ATOM_DECL(read_waits); \ - LOCAL_ATOM_DECL(read_write); \ - LOCAL_ATOM_DECL(registry); \ - LOCAL_ATOM_DECL(reject_route); \ - LOCAL_ATOM_DECL(remote); \ - LOCAL_ATOM_DECL(rstates); \ - LOCAL_ATOM_DECL(selected); \ - LOCAL_ATOM_DECL(sender_dry); \ - LOCAL_ATOM_DECL(send_failure); \ - LOCAL_ATOM_DECL(shutdown); \ - LOCAL_ATOM_DECL(sifaddr); \ - LOCAL_ATOM_DECL(sifbrdaddr); \ - LOCAL_ATOM_DECL(sifdstaddr); \ - LOCAL_ATOM_DECL(sifflags); \ - LOCAL_ATOM_DECL(sifmtu); \ - LOCAL_ATOM_DECL(sifnetmask); \ - LOCAL_ATOM_DECL(siftxqlen); \ - LOCAL_ATOM_DECL(slist); \ - LOCAL_ATOM_DECL(sndctrlbuf); \ - LOCAL_ATOM_DECL(sockaddr); \ - LOCAL_ATOM_DECL(socket_debug); \ - LOCAL_ATOM_DECL(socket_level); \ - LOCAL_ATOM_DECL(socket_option); \ - LOCAL_ATOM_DECL(sourceaddr); \ - LOCAL_ATOM_DECL(time_exceeded); \ - LOCAL_ATOM_DECL(true); \ - LOCAL_ATOM_DECL(txqlen); \ - LOCAL_ATOM_DECL(txstatus); \ - LOCAL_ATOM_DECL(txtime); \ - LOCAL_ATOM_DECL(want); \ - LOCAL_ATOM_DECL(write); \ - LOCAL_ATOM_DECL(write_pkg_max); \ - LOCAL_ATOM_DECL(wstates); \ - LOCAL_ATOM_DECL(zerocopy) - -/* Local error reason atoms - * Keep this (commented) for future use... +/* These three (inline) functions are primarily intended for debugging, + * that is, to make it easy to add debug printouts. */ -/* -#define LOCAL_ERROR_REASON_ATOMS \ - LOCAL_ATOM_DECL(select_read); \ - LOCAL_ATOM_DECL(select_write) -*/ -#define LOCAL_ATOM_DECL(LA) static ERL_NIF_TERM atom_##LA -LOCAL_ATOMS; -// LOCAL_ERROR_REASON_ATOMS; -#undef LOCAL_ATOM_DECL - -/* *** Sockets *** */ -static ErlNifResourceType* esocks; -static ErlNifResourceTypeInit esockInit = { - esock_dtor, - esock_stop, - (ErlNifResourceDown*) esock_down -}; - -// Initiated when the nif is loaded -static ESockData data; - -/* Jump table for the I/O backend (async or sync) */ -static ESockIoBackend io_backend = {0}; +// static ESOCK_INLINE void esock_clear_env(const char* slogan, ErlNifEnv* env) +extern void esock_clear_env(const char* slogan, ErlNifEnv* env) +{ + // ESOCK_DBG_PRINTF( TRUE, ("SOCKET", "env clear - %s: 0x%lX\r\n", slogan, env) ); -/* This, the test for NULL), is temporary until we have a win stub */ -#define ESOCK_IO_INIT(NUMT) \ - ((io_backend.init != NULL) ? \ - io_backend.init((NUMT)) : ESOCK_IO_ERR_UNSUPPORTED) -#define ESOCK_IO_FIN() \ - ((io_backend.finish != NULL) ? \ - io_backend.finish() : ESOCK_IO_ERR_UNSUPPORTED) - -#define ESOCK_IO_OPEN_WITH_FD(ENV, FD, EOPTS) \ - ((io_backend.open_with_fd != NULL) ? \ - io_backend.open_with_fd((ENV), (FD), (EOPTS), &data) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_OPEN_PLAIN(ENV, D, T, P, EOPTS) \ - ((io_backend.open_plain != NULL) ? \ - io_backend.open_plain((ENV), (D), \ - (T), (P), (EOPTS), &data) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_BIND(ENV, D, SAP, AL) \ - ((io_backend.bind != NULL) ? \ - io_backend.bind((ENV), (D), (SAP), (AL)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_CONNECT(ENV, D, SR, CR, AP, AL) \ - ((io_backend.connect != NULL) ? \ - io_backend.connect((ENV), (D), \ - (SR), (CR), (AP), (AL)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_LISTEN(ENV, D, BL) \ - ((io_backend.listen != NULL) ? \ - io_backend.listen((ENV), (D), (BL)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_ACCEPT(ENV, D, SR, AR) \ - ((io_backend.accept != NULL) ? \ - io_backend.accept((ENV), (D), (SR), (AR)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_SEND(ENV, D, SR, RF, L, F) \ - ((io_backend.send != NULL) ? \ - io_backend.send((ENV), (D), \ - (SR), (RF), (L), (F)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_SENDTO(ENV, D, \ - SOCKR, SENDR, \ - DP, F, TAP, TAL) \ - ((io_backend.sendto != NULL) ? \ - io_backend.sendto((ENV), (D), \ - (SOCKR), (SENDR), \ - (DP), (F), (TAP), (TAL)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_SENDMSG(ENV, D, \ - SOCKR, SENDR, EM, F, EIOV) \ - ((io_backend.sendmsg != NULL) ? \ - io_backend.sendmsg((ENV), (D), \ - (SOCKR), (SENDR), \ - (EM), (F), (EIOV), &data) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_SENDFILE_START(ENV, D, \ - SOR, SNR, \ - O, CN, FR) \ - ((io_backend.sendfile_start != NULL) ? \ - io_backend.sendfile_start((ENV), (D), \ - (SOR), (SNR), \ - (O), (CN), (FR)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_SENDFILE_CONT(ENV, D, \ - SOR, SNR, \ - O, CP) \ - ((io_backend.sendfile_cont != NULL) ? \ - io_backend.sendfile_cont((ENV), (D), \ - (SOR), (SNR), \ - (O), (CP)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_SENDFILE_DC(ENV, D) \ - ((io_backend.sendfile_dc != NULL) ? \ - io_backend.sendfile_dc((ENV), (D)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_RECV(ENV, D, \ - SR, RR, L, F) \ - ((io_backend.recv != NULL) ? \ - io_backend.recv((ENV), (D), \ - (SR), (RR), (L), (F)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_RECVFROM(ENV, D, \ - SR, RR, L, F) \ - ((io_backend.recvfrom != NULL) ? \ - io_backend.recvfrom((ENV), (D), \ - (SR), (RR), (L), (F)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_RECVMSG(ENV, D, \ - SR, RR, BL, CL, F) \ - ((io_backend.recvmsg != NULL) ? \ - io_backend.recvmsg((ENV), (D), \ - (SR), (RR), \ - (BL), (CL), (F)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_CLOSE(ENV, D) \ - ((io_backend.close != NULL) ? \ - io_backend.close((ENV), (D)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_FIN_CLOSE(ENV, D) \ - ((io_backend.fin_close != NULL) ? \ - io_backend.fin_close((ENV), (D)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_SHUTDOWN(ENV, D, H) \ - ((io_backend.shutdown != NULL) ? \ - io_backend.shutdown((ENV), (D), (H)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_SOCKNAME(ENV, D) \ - ((io_backend.sockname != NULL) ? \ - io_backend.sockname((ENV), (D)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) -#define ESOCK_IO_PEERNAME(ENV, D) \ - ((io_backend.peername != NULL) ? \ - io_backend.peername((ENV), (D)) : \ - enif_raise_exception(env, MKA(env, "notsup"))) + SGDBG( ("SOCKET", "env clear - %s: 0x%lX\r\n", slogan, env) ); + if (env != NULL) enif_clear_env(env); +} -/* These two (inline) functions are primarily intended for debugging, - * that is, to make it easy to add debug printouts. - */ // static ESOCK_INLINE void esock_free_env(const char* slogan, ErlNifEnv* env) extern void esock_free_env(const char* slogan, ErlNifEnv* env) { + // ESOCK_DBG_PRINTF( TRUE, ("SOCKET", "env free - %s: 0x%lX\r\n", slogan, env) ); + SGDBG( ("SOCKET", "env free - %s: 0x%lX\r\n", slogan, env) ); - // esock_dbg_printf("SOCK ENV", "free - %s: 0x%lX\r\n", slogan, env); if (env != NULL) enif_free_env(env); } @@ -3869,12 +3726,13 @@ extern void esock_free_env(const char* slogan, ErlNifEnv* env) // static ESOCK_INLINE ErlNifEnv* esock_alloc_env(const char* slogan) extern ErlNifEnv* esock_alloc_env(const char* slogan) { - ErlNifEnv* env; + ErlNifEnv* env = enif_alloc_env(); - ESOCK_ASSERT( (env = enif_alloc_env()) != NULL); + // ESOCK_DBG_PRINTF( TRUE, ("SOCKET", "env alloc - %s: 0x%lX\r\n", slogan, env) ); SGDBG( ("SOCKET", "env alloc - %s: 0x%lX\r\n", slogan, env) ); - // esock_dbg_printf("SOCK ENV", "alloc - %s: 0x%lX\r\n", slogan, env); + + ESOCK_ASSERT( env != NULL ); return env; } @@ -3938,9 +3796,6 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ERL_NIF_TERM info; ESockDescriptor* descP; @@ -3972,7 +3827,6 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, MUNLOCK(descP->readMtx); return info; -#endif } @@ -3983,7 +3837,6 @@ ERL_NIF_TERM nif_info(ErlNifEnv* env, * actually a counter, the num_cnt_bits. This is the "size" of each counter, * in number of bits: 16 | 24 | 32 | 48 | 64. */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_global_info(ErlNifEnv* env) { @@ -4040,14 +3893,29 @@ ERL_NIF_TERM esock_global_info(ErlNifEnv* env) atom_socket_debug, esock_atom_use_registry, atom_iow, - atom_counters, - atom_iov_max}, + esock_atom_counters, + atom_iov_max, + atom_io_backend}, vals[] = {dbg, sockDbg, useReg, iow, gcnt, - iovMax}, + iovMax, + ESOCK_IO_INFO(env) + /* This mess is just a temporary hack + * and shall be replaced by a callback + * function (eventually). + * That function should return a 'term' (a map). + */ + /* +#ifdef __WIN32__ + MKA(env, "win_esaio") +#else + MKA(env, "unix_essio") +#endif + */ + }, info; unsigned int numKeys = NUM(keys), @@ -4060,7 +3928,6 @@ ERL_NIF_TERM esock_global_info(ErlNifEnv* env) } } } -#endif // #ifndef __WIN32__ /* @@ -4076,7 +3943,6 @@ ERL_NIF_TERM esock_global_info(ErlNifEnv* env) * writers: The number of current and waiting writers * acceptors: The number of current and waiting acceptors */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, ESockDescriptor* descP) @@ -4102,7 +3968,7 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, atom_rstates, atom_wstates, atom_ctype, - atom_counters, + esock_atom_counters, atom_num_readers, atom_num_writers, atom_num_acceptors}; @@ -4133,13 +3999,11 @@ ERL_NIF_TERM esock_socket_info(ErlNifEnv* env, return info; } } -#endif // #ifndef __WIN32__ /* * Encode the socket domain */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv* env, ESockDescriptor* descP) @@ -4150,13 +4014,11 @@ ERL_NIF_TERM esock_socket_info_domain(ErlNifEnv* env, esock_encode_domain(env, domain, &edomain); return edomain; } -#endif // #ifndef __WIN32__ /* * Encode the socket type */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_socket_info_type(ErlNifEnv* env, ESockDescriptor* descP) @@ -4168,16 +4030,14 @@ ERL_NIF_TERM esock_socket_info_type(ErlNifEnv* env, return etype; } -#endif // #ifndef __WIN32__ /* * Encode the socket "create type" - * That is "show" how this socket was created: + * That is; "show" how this socket was created: * * normal | fromfd | {fromfd, integer()} */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env, ESockDescriptor* descP) @@ -4190,7 +4050,7 @@ ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env, "\r\n", descP->sock, descP->origFD, B2S(descP->closeOnClose)) ); - if (descP->origFD > 0) { + if (descP->origFD != INVALID_SOCKET) { /* Created from other FD */ if (descP->closeOnClose) { /* We *have* dup'ed: {fromfd, integer()} */ @@ -4210,16 +4070,12 @@ ERL_NIF_TERM esock_socket_info_ctype(ErlNifEnv* env, return ctype; } -#endif // #ifndef __WIN32__ /* * Encode the socket "state" - * That is "show" how this socket was created: - * - * normal | fromfd | {fromfd, integer()} + * This is a list of atoms, one for each valid 'state' value. */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env, unsigned int state) @@ -4294,7 +4150,7 @@ ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env, if ((state & ESOCK_STATE_DTOR) != 0) { /* - SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> dror" + SSDBG( descP, ("SOCKET", "esock_socket_info_state {%d} -> dtor" "\r\n", descP->sock) ); */ TARRAY_ADD(estate, atom_dtor); @@ -4304,13 +4160,11 @@ ERL_NIF_TERM esock_socket_info_state(ErlNifEnv* env, return estateList; } -#endif // #ifndef __WIN32__ /* * Collect all counters for a socket. */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env, ESockDescriptor* descP) @@ -4399,7 +4253,6 @@ ERL_NIF_TERM esock_socket_info_counters(ErlNifEnv* env, return cnts; } -#endif // #ifndef __WIN32__ @@ -4427,9 +4280,6 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ERL_NIF_TERM command, cdata, result; ESOCK_ASSERT( argc == 1 ); @@ -4449,10 +4299,10 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env, SGDBG( ("SOCKET", "nif_command -> " "\r\n command: %T" - "\r\n cdata: %T" + "\r\n cdata: %T" "\r\n", command, cdata) ); - result = esock_command(env, command, cdata); + result = ESOCK_IO_CMD(env, command, cdata); SGDBG( ("SOCKET", "nif_command -> done with result: " "\r\n %T" @@ -4460,14 +4310,11 @@ ERL_NIF_TERM nif_command(ErlNifEnv* env, return result; -#endif // #ifdef __WIN32__ #else } -#ifndef __WIN32__ static -ERL_NIF_TERM -esock_command(ErlNifEnv* env, ERL_NIF_TERM command, ERL_NIF_TERM cdata) +ERL_NIF_TERM esock_command(ErlNifEnv* env, ERL_NIF_TERM command, ERL_NIF_TERM cdata) { int cmp; @@ -4489,10 +4336,8 @@ esock_command(ErlNifEnv* env, ERL_NIF_TERM command, ERL_NIF_TERM cdata) return esock_raise_invalid(env, MKT2(env, esock_atom_command, command)); } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ static ERL_NIF_TERM esock_command_debug(ErlNifEnv* env, ERL_NIF_TERM cdata) { @@ -4502,15 +4347,12 @@ ERL_NIF_TERM esock_command_debug(ErlNifEnv* env, ERL_NIF_TERM cdata) if (esock_decode_bool(cdata, &data.dbg)) result = esock_atom_ok; else - result = - esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata)); + result = esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata)); return result; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ static ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env, ERL_NIF_TERM cdata) { @@ -4518,8 +4360,7 @@ ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env, ERL_NIF_TERM cdata) /* The data *should* be a boolean() */ if (! esock_decode_bool(cdata, &dbg)) - return - esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata)); + return esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata)); MLOCK(data.cntMtx); data.sockDbg = dbg; @@ -4527,10 +4368,8 @@ ERL_NIF_TERM esock_command_socket_debug(ErlNifEnv* env, ERL_NIF_TERM cdata) return esock_atom_ok;; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ static ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env, ERL_NIF_TERM cdata) @@ -4539,17 +4378,14 @@ ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env, /* The data *should* be a boolean() */ if (! esock_decode_bool(cdata, &useReg)) - return - esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata)); + return esock_raise_invalid(env, MKT2(env, esock_atom_data, cdata)); MLOCK(data.cntMtx); data.useReg = useReg; MUNLOCK(data.cntMtx); - return esock_atom_ok;; + return esock_atom_ok; } -#endif - @@ -4559,14 +4395,14 @@ ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env, * * Calculate how many readers | writers | acceptors we have for this socket. * Current requestor + any waiting requestors (of the type). - * + * Note that "Current requestor" is *not* used on Windows. */ #ifndef __WIN32__ #define ESOCK_INFO_REQ_FUNCS \ ESOCK_INFO_REQ_FUNC_DECL(readers, currentReaderP, readersQ) \ - ESOCK_INFO_REQ_FUNC_DECL(writers, currentWriterP, writersQ) \ + ESOCK_INFO_REQ_FUNC_DECL(writers, currentWriterP, writersQ) \ ESOCK_INFO_REQ_FUNC_DECL(acceptors, currentAcceptorP, acceptorsQ) #define ESOCK_INFO_REQ_FUNC_DECL(F, CRP, Q) \ @@ -4576,32 +4412,49 @@ ERL_NIF_TERM esock_command_use_socket_registry(ErlNifEnv* env, { \ return socket_info_reqs(env, descP, descP->CRP, &descP->Q); \ } +#else + +#define ESOCK_INFO_REQ_FUNCS \ + ESOCK_INFO_REQ_FUNC_DECL(readers, readersQ) \ + ESOCK_INFO_REQ_FUNC_DECL(writers, writersQ) \ + ESOCK_INFO_REQ_FUNC_DECL(acceptors, acceptorsQ) + +#define ESOCK_INFO_REQ_FUNC_DECL(F, Q) \ + static \ + ERL_NIF_TERM esock_socket_info_##F(ErlNifEnv* env, \ + ESockDescriptor* descP) \ + { \ + return socket_info_reqs(env, descP, &descP->Q); \ + } + +#endif + ESOCK_INFO_REQ_FUNCS #undef ESOCK_INFO_REQ_FUNC_DECL static ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env, ESockDescriptor* descP, +#ifndef __WIN32__ ESockRequestor* currentRequestorP, +#endif ESockRequestQueue* q) { - ESockRequestQueueElement* tmp; - ERL_NIF_TERM info; - unsigned int cnt = 0; + ERL_NIF_TERM info; + unsigned int cnt; +#ifdef __WIN32__ + cnt = 0; +#else if (currentRequestorP != NULL) { // We have an active requestor! - cnt++; - - // And add all the waiting requestors - tmp = q->first; - while (tmp != NULL) { - cnt++; - tmp = tmp->nextP; - } + cnt = 1; + } else { + cnt = 0; } +#endif - info = MKUI(env, cnt); + info = MKUI(env, cnt + qlength(q)); SSDBG( descP, ("SOCKET", "socket_info_reqs -> done with" "\r\n info: %T" @@ -4610,8 +4463,6 @@ ERL_NIF_TERM socket_info_reqs(ErlNifEnv* env, return info; } -#endif // #ifndef __WIN32__ - /* ---------------------------------------------------------------------- * nif_supports @@ -4641,29 +4492,28 @@ ERL_NIF_TERM nif_supports(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else SGDBG( ("SOCKET", "nif_supports -> entry with %d args\r\n", argc) ); /* Extract arguments and perform preliminary validation */ if (argc == 0) - return esock_supports_0(env); + return ESOCK_IO_SUPPORTS_0(env); - ESOCK_ASSERT( argc == 1 ); + if (argc == 1) + return ESOCK_IO_SUPPORTS_1(env, argv[0]); + + return esock_make_error(env, esock_atom_einval); - return esock_supports_1(env, argv[0]); -#endif } + /* esock_supports - what features do we support? * * This gives information about what features actually * work on the current platform. */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_supports_0(ErlNifEnv* env) { @@ -4711,9 +4561,9 @@ ERL_NIF_TERM esock_supports_0(ErlNifEnv* env) TARRAY_TOLIST(opts, env, &opts_list); return opts_list; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ + + static ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key) { @@ -4739,37 +4589,52 @@ ERL_NIF_TERM esock_supports_1(ErlNifEnv* env, ERL_NIF_TERM key) return result; } -#endif // #ifndef __WIN32__ - -#ifndef __WIN32__ static ERL_NIF_TERM esock_supports_msg_flags(ErlNifEnv* env) { size_t n; ERL_NIF_TERM result; result = MKEL(env); - for (n = 0; n < NUM(msg_flags); n++) { + for (n = 0; n < esock_msg_flags_length; n++) { result = MKC(env, MKT2(env, - *(msg_flags[n].name), - MKI(env, msg_flags[n].flag)), + *(esock_msg_flags[n].name), + MKI(env, esock_msg_flags[n].flag)), result); } return result; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ static ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env) { ERL_NIF_TERM protocols; +#ifndef __WIN32__ +#if defined(SOL_IP) + int protoIP = SOL_IP; +#else + int protoIP = IPPROTO_IP; +#endif +#else + int protoIP = IPPROTO_IP; +#endif +#if defined(HAVE_IPV6) +#ifndef __WIN32__ +#if defined(SOL_IPV6) + int protoIPV6 = SOL_IPV6; +#else + int protoIPV6 = IPPROTO_IPV6; +#endif +#else + int protoIPV6 = IPPROTO_IPV6; +#endif +#endif protocols = MKEL(env); @@ -4809,29 +4674,13 @@ ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env) protocols = MKC(env, - MKT2(env, - MKL1(env, esock_atom_ip), - MKI(env, -#ifdef SOL_IP - SOL_IP -#else - IPPROTO_IP -#endif - )), + MKT2(env, MKL1(env, esock_atom_ip), MKI(env, protoIP)), protocols); #ifdef HAVE_IPV6 protocols = MKC(env, - MKT2(env, - MKL1(env, esock_atom_ipv6), - MKI(env, -#ifdef SOL_IPV6 - SOL_IPV6 -#else - IPPROTO_IPV6 -#endif - )), + MKT2(env, MKL1(env, esock_atom_ipv6), MKI(env, protoIPV6)), protocols); #endif @@ -4854,11 +4703,9 @@ ERL_NIF_TERM esock_supports_protocols(ErlNifEnv* env) return protocols; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ static ERL_NIF_TERM esock_supports_ioctl_requests(ErlNifEnv* env) { @@ -4942,36 +4789,31 @@ ERL_NIF_TERM esock_supports_ioctl_requests(ErlNifEnv* env) return requests; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ -static ERL_NIF_TERM esock_supports_ioctl_flags(ErlNifEnv* env) +static +ERL_NIF_TERM esock_supports_ioctl_flags(ErlNifEnv* env) { size_t n; ERL_NIF_TERM result; result = MKEL(env); - for (n = 0; n < NUM(ioctl_flags); n++) { + for (n = 0; n < esock_ioctl_flags_length; n++) { result = MKC(env, MKT2(env, - *(ioctl_flags[n].name), - MKI(env, ioctl_flags[n].flag)), + *(esock_ioctl_flags[n].name), + MKI(env, esock_ioctl_flags[n].flag)), result); } return result; } -#endif // #ifndef __WIN32__ - -#ifndef __WIN32__ - static ERL_NIF_TERM esock_supports_options(ErlNifEnv* env) { @@ -5010,7 +4852,6 @@ ERL_NIF_TERM esock_supports_options(ErlNifEnv* env) return levels; } -#endif // #ifndef __WIN32__ @@ -5059,9 +4900,6 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ERL_NIF_TERM result; SGDBG( ("SOCKET", "nif_open -> " @@ -5145,10 +4983,38 @@ ERL_NIF_TERM nif_open(ErlNifEnv* env, return result; -#endif // #ifdef __WIN32__ #else } +extern +BOOLEAN_T esock_open_is_debug(ErlNifEnv* env, + ERL_NIF_TERM eopts, + BOOLEAN_T def) +{ + return esock_get_bool_from_map(env, eopts, esock_atom_debug, def); +} + +extern +BOOLEAN_T esock_open_use_registry(ErlNifEnv* env, + ERL_NIF_TERM eopts, + BOOLEAN_T def) +{ + return esock_get_bool_from_map(env, eopts, esock_atom_use_registry, def); +} + +extern +BOOLEAN_T esock_open_which_protocol(SOCKET sock, int* proto) +{ +#if defined(SO_PROTOCOL) + if (esock_getopt_int(sock, SOL_SOCKET, SO_PROTOCOL, proto)) + return TRUE; +#endif + return FALSE; +} + + + + /* ---------------------------------------------------------------------- * nif_bind * @@ -5164,9 +5030,6 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM eSockAddr, ret; ESockAddress sockAddr; @@ -5184,7 +5047,7 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, eSockAddr = argv[1]; if (! esock_decode_sockaddr(env, eSockAddr, &sockAddr, &addrLen)) - return esock_make_invalid(env, atom_sockaddr); + return esock_make_invalid(env, esock_atom_sockaddr); MLOCK(descP->readMtx); @@ -5204,7 +5067,6 @@ ERL_NIF_TERM nif_bind(ErlNifEnv* env, MUNLOCK(descP->readMtx); return ret; -#endif // #ifdef __WIN32__ #else } @@ -5227,9 +5089,6 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM res, sockRef, connRef; ESockAddress addr, *addrP; @@ -5253,14 +5112,14 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, return enif_make_badarg(env); if (! esock_decode_sockaddr(env, eSockAddr, &addr, &addrLen)) - return esock_make_invalid(env, atom_sockaddr); + return esock_make_invalid(env, esock_atom_sockaddr); addrP = &addr; MLOCK(descP->writeMtx); SSDBG( descP, ("SOCKET", "nif_connect(%T), {%d0x%X} ->" - "\r\n ConnRef: %T" + "\r\n ConnRef: %T" "\r\n SockAddr: %T" "\r\n", sockRef, descP->sock, descP->writeState, @@ -5270,7 +5129,7 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, ESOCK_ASSERT( argc == 1 ); connRef = esock_atom_undefined; - addrP = NULL; + addrP = NULL; addrLen = 0; MLOCK(descP->writeMtx); @@ -5292,7 +5151,6 @@ ERL_NIF_TERM nif_connect(ErlNifEnv* env, return res; -#endif // #ifdef __WIN32__ #else } @@ -5313,9 +5171,6 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; int backlog; ERL_NIF_TERM ret; @@ -5354,11 +5209,52 @@ ERL_NIF_TERM nif_listen(ErlNifEnv* env, MUNLOCK(descP->readMtx); return ret; -#endif // #ifdef __WIN32__ #else } +/* ======================================================================== + */ +static +ERL_NIF_TERM esock_listen(ErlNifEnv* env, + ESockDescriptor* descP, + int backlog) +{ + + /* + * Verify that we are in the proper state + */ + + SSDBG( descP, + ("SOCKET", "esock_listen(%d) -> verify open\r\n", descP->sock) ); + if (! IS_OPEN(descP->readState)) + return esock_make_error_closed(env); + +#if defined(__WIN32__) + SSDBG( descP, + ("SOCKET", "esock_listen(%d) -> verify bound\r\n", descP->sock) ); + if (! IS_BOUND(descP->writeState)) + return esock_make_error(env, esock_atom_not_bound); +#endif + + /* + * And attempt to make socket listening + */ + + SSDBG( descP, ("SOCKET", "esock_listen(%d) -> try listen with" + "\r\n backlog: %d" + "\r\n", descP->sock, backlog) ); + + if ((sock_listen(descP->sock, backlog)) < 0) + return esock_make_error_errno(env, sock_errno()); + + descP->readState |= ESOCK_STATE_LISTENING; + + return esock_atom_ok; + +} + + /* ---------------------------------------------------------------------- * nif_accept @@ -5376,10 +5272,6 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else - ESockDescriptor* descP; ERL_NIF_TERM sockRef, ref, res; @@ -5397,6 +5289,7 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, MLOCK(descP->readMtx); +#ifndef __WIN32__ SSDBG( descP, ("SOCKET", "nif_accept%T), {%d,0x%X} ->" "\r\n ReqRef: %T" @@ -5413,6 +5306,16 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, ESOCK_MON2TERM(env, &descP->currentAcceptor.mon), descP->currentAcceptor.env, descP->currentAcceptor.ref) ); +#else + SSDBG( descP, + ("SOCKET", "nif_accept%T), {%d,0x%X} ->" + "\r\n ReqRef: %T" + "\r\n First Acceptor addr: %p" + "\r\n", + sockRef, descP->sock, descP->readState, + ref, + descP->acceptorsQ.first) ); +#endif res = ESOCK_IO_ACCEPT(env, descP, sockRef, ref); @@ -5424,10 +5327,10 @@ ERL_NIF_TERM nif_accept(ErlNifEnv* env, return res; -#endif // #ifdef __WIN32__ #else } + /* ---------------------------------------------------------------------- * nif_send * @@ -5446,9 +5349,6 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM sockRef, sendRef; ErlNifBinary sndData; @@ -5511,9 +5411,9 @@ ERL_NIF_TERM nif_send(ErlNifEnv* env, SGDBG( ("SOCKET", "nif_send -> done with result: " "\r\n %T" "\r\n", res) ); + return res; -#endif // #ifdef __WIN32__ #else } @@ -5536,9 +5436,6 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM sockRef, sendRef; ErlNifBinary sndData; @@ -5583,7 +5480,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, "nif_sendto(%T), {%d} -> sockaddr decode failed \r\n", sockRef, descP->sock) ); - return esock_make_invalid(env, atom_sockaddr); + return esock_make_invalid(env, esock_atom_sockaddr); } MLOCK(descP->writeMtx); @@ -5609,7 +5506,6 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, return res; -#endif // if defined(__WIN32__) } @@ -5624,6 +5520,7 @@ ERL_NIF_TERM nif_sendto(ErlNifEnv* env, * Msg - Message - map() with data and (maybe) control and dest * Flags - Send flags as an integer(). * SendRef - A unique id reference() for this (send) request. + * IOV - List of binaries */ static @@ -5631,9 +5528,6 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ERL_NIF_TERM res, sockRef, sendRef, eMsg, eIOV; ESockDescriptor* descP; int flags; @@ -5686,7 +5580,6 @@ ERL_NIF_TERM nif_sendmsg(ErlNifEnv* env, return res; -#endif // #ifdef __WIN32__ #else } @@ -5783,7 +5676,7 @@ nif_sendfile(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#if defined(__WIN32__) || !defined(HAVE_SENDFILE) +#if !defined(HAVE_SENDFILE) return enif_raise_exception(env, MKA(env, "notsup")); #else ESockDescriptor *descP; @@ -5898,7 +5791,7 @@ nif_sendfile(ErlNifEnv* env, return res; -#endif // #if defined(__WIN32__) || !defined(HAVE_SENDFILE) +#endif // !defined(HAVE_SENDFILE) } @@ -5925,9 +5818,6 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; ErlNifUInt64 elen; @@ -5992,7 +5882,6 @@ ERL_NIF_TERM nif_recv(ErlNifEnv* env, return res; -#endif // #ifdef __WIN32__ #else } @@ -6026,9 +5915,6 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; ErlNifUInt64 elen; @@ -6063,8 +5949,7 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, if (! a1ok) return esock_make_error_integer_range(env, argv[1]); - return - esock_make_error_integer_range(env, argv[2]); + return esock_make_error_integer_range(env, argv[2]); } len = (ssize_t) elen; if (elen != (ErlNifUInt64) len) @@ -6104,7 +5989,6 @@ ERL_NIF_TERM nif_recvfrom(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; -#endif // #ifdef __WIN32__ #else } @@ -6143,9 +6027,6 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM sockRef, recvRef; ErlNifUInt64 eBufSz, eCtrlSz; @@ -6233,7 +6114,6 @@ ERL_NIF_TERM nif_recvmsg(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; -#endif // #ifdef __WIN32__ #else } @@ -6252,9 +6132,6 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM res; @@ -6291,178 +6168,9 @@ ERL_NIF_TERM nif_close(ErlNifEnv* env, "\r\n", argv[0], res) ); return res; -#endif // #ifdef __WIN32__ #else } -/* Prepare for close - return whether stop is scheduled - */ -#ifndef __WIN32__ // TEMPORARY - check the prim_socket_int.h file! -extern -BOOLEAN_T esock_do_stop(ErlNifEnv* env, - ESockDescriptor* descP) -{ - BOOLEAN_T ret; - int sres; - ERL_NIF_TERM sockRef; - - sockRef = enif_make_resource(env, descP); - - if (IS_SELECTED(descP)) { - ESOCK_ASSERT( (sres = esock_select_stop(env, - (ErlNifEvent) descP->sock, - descP)) - >= 0 ); - if ((sres & ERL_NIF_SELECT_STOP_CALLED) != 0) { - /* The socket is no longer known by the select machinery - * - it may be closed - */ - ret = FALSE; - } else { - ESOCK_ASSERT( (sres & ERL_NIF_SELECT_STOP_SCHEDULED) != 0 ); - /* esock_stop() is scheduled - * - socket may be removed by esock_stop() or later - */ - ret = TRUE; - } - } else { - sres = 0; - /* The socket has never been used in the select machinery - * - it may be closed - */ - ret = FALSE; - } - - /* +++++++ Current and waiting Writers +++++++ */ - - if (descP->currentWriterP != NULL) { - - /* We have a current Writer; was it deselected? - */ - - if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) { - - /* The current Writer will not get a select message - * - send it an abort message - */ - - esock_stop_handle_current(env, - "writer", - descP, sockRef, &descP->currentWriter); - } - - /* Inform the waiting Writers (in the same way) */ - - SSDBG( descP, - ("SOCKET", - "esock_do_stop {%d} -> handle waiting writer(s)\r\n", - descP->sock) ); - - inform_waiting_procs(env, "writer", - descP, sockRef, &descP->writersQ, esock_atom_closed); - - descP->currentWriterP = NULL; - } - - /* +++++++ Connector +++++++ - * Note that there should not be Writers and a Connector - * at the same time so the check for if the - * current Writer/Connecter was deselected is only correct - * under that assumption - */ - - if (descP->connectorP != NULL) { - - /* We have a Connector; was it deselected? - */ - - if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) { - - /* The Connector will not get a select message - * - send it an abort message - */ - - esock_stop_handle_current(env, - "connector", - descP, sockRef, &descP->connector); - } - - descP->connectorP = NULL; - } - - /* +++++++ Current and waiting Readers +++++++ */ - - if (descP->currentReaderP != NULL) { - - /* We have a current Reader; was it deselected? - */ - - if (sres & ERL_NIF_SELECT_READ_CANCELLED) { - - /* The current Reader will not get a select message - * - send it an abort message - */ - - esock_stop_handle_current(env, - "reader", - descP, sockRef, &descP->currentReader); - } - - /* Inform the Readers (in the same way) */ - - SSDBG( descP, - ("SOCKET", - "esock_do_stop {%d} -> handle waiting reader(s)\r\n", - descP->sock) ); - - inform_waiting_procs(env, "writer", - descP, sockRef, &descP->readersQ, esock_atom_closed); - - descP->currentReaderP = NULL; - } - - /* +++++++ Current and waiting Acceptors +++++++ - * - * Note that there should not be Readers and Acceptors - * at the same time so the check for if the - * current Reader/Acceptor was deselected is only correct - * under that assumption - */ - - if (descP->currentAcceptorP != NULL) { - - /* We have a current Acceptor; was it deselected? - */ - - if (sres & ERL_NIF_SELECT_READ_CANCELLED) { - - /* The current Acceptor will not get a select message - * - send it an abort message - */ - - esock_stop_handle_current(env, - "acceptor", - descP, sockRef, &descP->currentAcceptor); - } - - /* Inform the waiting Acceptor (in the same way) */ - - SSDBG( descP, - ("SOCKET", - "esock_do_stop {%d} -> handle waiting acceptors(s)\r\n", - descP->sock) ); - - inform_waiting_procs(env, "acceptor", - descP, sockRef, &descP->acceptorsQ, esock_atom_closed); - - descP->currentAcceptorP = NULL; - } - - return ret; -} -#endif // #ifndef __WIN32__ - - /* ---------------------------------------------------------------------- * nif_finalize_close * @@ -6480,9 +6188,6 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, { ESockDescriptor* descP; ERL_NIF_TERM result; -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else /* Extract arguments and perform preliminary validation */ @@ -6496,8 +6201,11 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, MLOCK(descP->writeMtx); SSDBG( descP, - ("SOCKET", "nif_finalize_close(%T), {%d,0x%X}\r\n", - argv[0], descP->sock, descP->readState) ); + ("SOCKET", "nif_finalize_close(%T, %d) -> " + "\r\n ReadState: 0x%X" + "\r\n WriteState: 0x%X" + "\r\n", + argv[0], descP->sock, descP->readState, descP->writeState) ); result = ESOCK_IO_FIN_CLOSE(env, descP); @@ -6509,11 +6217,9 @@ ERL_NIF_TERM nif_finalize_close(ErlNifEnv* env, MUNLOCK(descP->readMtx); return result; -#endif // #ifdef __WIN32__ #else } -#ifndef __WIN32__ // TEMPORARY - check the prim_socket_int.h file! extern int esock_close_socket(ErlNifEnv* env, ESockDescriptor* descP, @@ -6536,7 +6242,6 @@ int esock_close_socket(ErlNifEnv* env, * finalize_close */ descP->sock = INVALID_SOCKET; - descP->event = INVALID_EVENT; descP->readState |= ESOCK_STATE_CLOSED; descP->writeState |= ESOCK_STATE_CLOSED; esock_dec_socket(descP->domain, descP->type, descP->protocol); @@ -6545,12 +6250,14 @@ int esock_close_socket(ErlNifEnv* env, enif_clear_env(descP->meta.env); descP->meta.ref = esock_atom_undefined; - sock_close_event(descP->event); if (descP->closeOnClose) { if (unlock) { MUNLOCK(descP->writeMtx); MUNLOCK(descP->readMtx); } + SSDBG( descP, + ("SOCKET", "esock_close_socket(%d) -> " + "try socket close\r\n", sock) ); if (sock_close(sock) != 0) err = sock_errno(); if (unlock) { @@ -6561,8 +6268,8 @@ int esock_close_socket(ErlNifEnv* env, if (err != 0) { SSDBG( descP, - ("SOCKET", "esock_close_socket {%d} -> %d\r\n", - sock, err) ); + ("SOCKET", "esock_close_socket(%d) -> %s (%d)\r\n", + sock, erl_errno_id(err), err) ); } /* (maybe) Update the registry */ @@ -6573,8 +6280,6 @@ int esock_close_socket(ErlNifEnv* env, return err; } -#endif // #ifndef __WIN32__ - @@ -6594,9 +6299,6 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM ehow, res; int how; @@ -6632,7 +6334,24 @@ ERL_NIF_TERM nif_shutdown(ErlNifEnv* env, "\r\n", argv[0], res) ); return res; -#endif // #ifdef __WIN32__ #else +} + + + +/* ======================================================================== + */ +static +ERL_NIF_TERM esock_shutdown(ErlNifEnv* env, + ESockDescriptor* descP, + int how) +{ + if (! IS_OPEN(descP->readState)) + return esock_make_error_closed(env); + + if (sock_shutdown(descP->sock, how) == 0) + return esock_atom_ok; + else + return esock_make_error_errno(env, sock_errno()); } @@ -6660,64 +6379,73 @@ ERL_NIF_TERM nif_setopt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP = NULL; + ERL_NIF_TERM esock, elevel, eopt, eval, enval; int level, opt, nativeValue; - ERL_NIF_TERM eVal; ESOCK_ASSERT( argc == 5 ); - SGDBG( ("SOCKET", "nif_setopt -> entry with argc: %d\r\n", argc) ); + esock = argv[0]; + elevel = argv[1]; + eopt = argv[2]; + eval = argv[3]; + enval = argv[4]; + + SGDBG( ("SOCKET", + "nif_setopt -> entry with argc: %d" + "\r\n esock: %T" + "\r\n elevel: %T" + "\r\n eopt: %T" + "\r\n eval: %T" + "\r\n enval: %T" + "\r\n", argc, esock, elevel, eopt, eval, enval) ); /* Extract arguments and perform preliminary validation */ - if ((! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) || - (! GET_INT(env, argv[4], &nativeValue))) { - // + if ((! ESOCK_GET_RESOURCE(env, esock, (void**) &descP)) || + (! GET_INT(env, enval, &nativeValue))) { SGDBG( ("SOCKET", "nif_setopt -> failed initial arg check\r\n") ); return enif_make_badarg(env); } - if (! GET_INT(env, argv[2], &opt)) { + + if (! GET_INT(env, eopt, &opt)) { SSDBG( descP, ("SOCKET", "nif_setopt -> failed initial arg check\r\n") ); - if (! IS_INTEGER(env, argv[2])) + if (! IS_INTEGER(env, eopt)) return enif_make_badarg(env); else - return esock_make_error_integer_range(env, argv[2]); - } - eVal = argv[3]; - - if (esock_decode_level(env, argv[1], &level)) { - if (nativeValue == 0) - return esock_setopt(env, descP, level, opt, eVal); - else - return esock_setopt_native(env, descP, level, opt, eVal); + return esock_make_error_integer_range(env, eopt); } - if (COMPARE(argv[1], atom_otp) == 0) { + if (COMPARE(elevel, atom_otp) == 0) { if (nativeValue == 0) { - return esock_setopt_otp(env, descP, opt, eVal); + return ESOCK_IO_SETOPT_OTP(env, descP, opt, eval); } else { SSDBG( descP, ("SOCKET", "nif_setopt -> failed arg check\r\n") ); return enif_make_badarg(env); } } + if (esock_decode_level(env, elevel, &level)) { + if (nativeValue == 0) + return ESOCK_IO_SETOPT(env, descP, level, opt, eval); + else + return ESOCK_IO_SETOPT_NATIVE(env, descP, level, opt, eval); + } + SGDBG( ("SOCKET", "nif_setopt -> failed arg check\r\n") ); - if (IS_INTEGER(env, argv[1])) - return esock_make_error_integer_range(env, argv[1]); + if (IS_INTEGER(env, elevel)) + return esock_make_error_integer_range(env, elevel); else return enif_make_badarg(env); -#endif // #ifdef __WIN32__ #else + } /* esock_setopt_otp - Handle OTP (level) options */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env, ESockDescriptor* descP, @@ -6801,13 +6529,12 @@ ERL_NIF_TERM esock_setopt_otp(ErlNifEnv* env, return result; } -#endif // #ifndef __WIN32__ /* esock_setopt_otp_debug - Handle the OTP (level) debug options */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_otp_debug(ErlNifEnv* env, ESockDescriptor* descP, @@ -6830,12 +6557,12 @@ ERL_NIF_TERM esock_setopt_otp_debug(ErlNifEnv* env, return esock_atom_ok; } -#endif // #ifndef __WIN32__ + /* esock_setopt_otp_iow - Handle the OTP (level) iow options */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_otp_iow(ErlNifEnv* env, ESockDescriptor* descP, @@ -6858,13 +6585,13 @@ ERL_NIF_TERM esock_setopt_otp_iow(ErlNifEnv* env, return esock_atom_ok; } -#endif // #ifndef __WIN32__ + /* esock_setopt_otp_ctrl_proc - Handle the OTP (level) * controlling_process options */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv* env, ESockDescriptor* descP, @@ -6933,7 +6660,12 @@ ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv* env, enif_set_pid_undefined(&descP->ctrlPid); - esock_down_ctrl(env, descP, &newCtrlPid); + /* Shall we use an function pointer argument instead? */ +#ifndef __WIN32__ + essio_down_ctrl(env, descP, &newCtrlPid); +#else + esaio_down_ctrl(env, descP, &newCtrlPid); +#endif descP->readState |= ESOCK_STATE_CLOSING; descP->writeState |= ESOCK_STATE_CLOSING; @@ -6946,7 +6678,7 @@ ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv* env, return esock_atom_ok; } -#endif // #ifndef __WIN32__ + /* esock_setopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option @@ -6956,8 +6688,9 @@ ERL_NIF_TERM esock_setopt_otp_ctrl_proc(ErlNifEnv* env, * {N :: pos_integer(), Sz :: default | pos_integer()} * * Where N is the max number of reads. + * Note that on Windows the tuple variant is not allowed! */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env, ESockDescriptor* descP, @@ -6965,7 +6698,9 @@ ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env, { const ERL_NIF_TERM* t; // The array of the elements of the tuple int tsz; // The size of the tuple - should be 2 +#ifndef __WIN32__ unsigned int n; +#endif size_t bufSz; ssize_t z; @@ -6981,6 +6716,22 @@ ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env, return esock_make_error_closed(env); } + +#ifdef __WIN32__ + + if (!esock_decode_bufsz(env, + eVal, + ESOCK_RECV_BUFFER_SIZE_DEFAULT, + &bufSz)) { + SSDBG( descP, + ("SOCKET", + "esock_setopt_otp_rcvbuf(%d) -> done invalid\r\n", + descP->sock) ); + return esock_make_invalid(env, esock_atom_value); + } + +#else + if (esock_decode_bufsz(env, eVal, ESOCK_RECV_BUFFER_SIZE_DEFAULT, @@ -6992,21 +6743,25 @@ ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env, (! GET_UINT(env, t[0], &n)) || (n == 0) || (! esock_decode_bufsz(env, t[1], - ESOCK_RECV_BUFFER_SIZE_DEFAULT, + ESOCK_RECV_BUFFER_SIZE_DEFAULT, &bufSz))) { SSDBG( descP, ("SOCKET", "esock_setopt_otp_rcvbuf {%d} -> done invalid\r\n", - descP->sock) ); + descP->sock) ); return esock_make_invalid(env, esock_atom_value); } } +#endif + // We do not want a buffer size that does not fit in ssize_t z = bufSz; if (bufSz != (size_t) z) return esock_make_invalid(env, esock_atom_value); +#ifndef __WIN32__ descP->rNum = n; +#endif descP->rBufSz = bufSz; SSDBG( descP, @@ -7015,12 +6770,12 @@ ERL_NIF_TERM esock_setopt_otp_rcvbuf(ErlNifEnv* env, return esock_atom_ok; } -#endif // #ifndef __WIN32__ + /* esock_setopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_otp_rcvctrlbuf(ErlNifEnv* env, ESockDescriptor* descP, @@ -7059,12 +6814,12 @@ ERL_NIF_TERM esock_setopt_otp_rcvctrlbuf(ErlNifEnv* env, return esock_atom_ok; } -#endif // #ifndef __WIN32__ + /* esock_setopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_otp_sndctrlbuf(ErlNifEnv* env, ESockDescriptor* descP, @@ -7103,12 +6858,12 @@ ERL_NIF_TERM esock_setopt_otp_sndctrlbuf(ErlNifEnv* env, return esock_atom_ok; } -#endif // #ifndef __WIN32__ + /* esock_setopt_otp_meta - Handle the OTP (level) meta options */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_otp_meta(ErlNifEnv* env, ESockDescriptor* descP, @@ -7146,12 +6901,12 @@ ERL_NIF_TERM esock_setopt_otp_meta(ErlNifEnv* env, return esock_atom_ok; } -#endif // #ifndef __WIN32__ + /* esock_setopt_otp_use_registry - Handle the OTP (level) use_registry option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_otp_use_registry(ErlNifEnv* env, ESockDescriptor* descP, @@ -7187,13 +6942,13 @@ ERL_NIF_TERM esock_setopt_otp_use_registry(ErlNifEnv* env, return esock_atom_ok; } -#endif // #ifndef __WIN32__ + /* The option has *not* been encoded. Instead it has been provided * in "native mode" (value is a binary, an integer or a boolean). */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env, ESockDescriptor* descP, @@ -7247,13 +7002,13 @@ ERL_NIF_TERM esock_setopt_native(ErlNifEnv* env, MUNLOCK(descP->writeMtx); return result; } -#endif // #ifndef __WIN32__ + /* esock_setopt - A "proper" level (option) has been specified, * and we have an value of known encoding */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt(ErlNifEnv* env, ESockDescriptor* descP, @@ -7315,10 +7070,8 @@ ERL_NIF_TERM esock_setopt(ErlNifEnv* env, MUNLOCK(descP->writeMtx); return result; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ #if defined(SO_BINDTODEVICE) static ERL_NIF_TERM esock_setopt_so_bindtodevice(ErlNifEnv* env, @@ -7330,10 +7083,8 @@ ERL_NIF_TERM esock_setopt_so_bindtodevice(ErlNifEnv* env, return esock_setopt_str_opt(env, descP, level, opt, IFNAMSIZ, eVal); } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ #if defined(SO_LINGER) static ERL_NIF_TERM esock_setopt_linger(ErlNifEnv* env, @@ -7361,7 +7112,7 @@ ERL_NIF_TERM esock_setopt_linger(ErlNifEnv* env, } if ((! esock_decode_bool(eOnOff, &onOff)) || - (! GET_INT(env, eLinger, &val.l_linger)) || + (! GET_INT(env, eLinger, (int*) &val.l_linger)) || (val.l_linger < 0)) { return esock_make_invalid(env, esock_atom_value); } @@ -7371,11 +7122,9 @@ ERL_NIF_TERM esock_setopt_linger(ErlNifEnv* env, &val, sizeof(val)); } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ #if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) /* esock_setopt_msfilter - Level IP MSFILTER option @@ -7470,14 +7219,12 @@ BOOLEAN_T decode_msfilter_mode(ErlNifEnv* env, } #endif // #if defined(IP_MSFILTER) && defined(IP_MSFILTER_SIZE) -#endif // #ifndef __WIN32__ /* esock_setopt_ip_mtu_discover - Level IP MTU_DISCOVER option * * The value is an atom of the type ip_pmtudisc(). */ -#ifndef __WIN32__ #if defined(IP_MTU_DISCOVER) static ERL_NIF_TERM esock_setopt_ip_mtu_discover(ErlNifEnv* env, @@ -7495,7 +7242,6 @@ ERL_NIF_TERM esock_setopt_ip_mtu_discover(ErlNifEnv* env, &val, sizeof(val)); } #endif // #if defined(IP_MTU_DISCOVER) -#endif // #ifndef __WIN32__ @@ -7503,7 +7249,6 @@ ERL_NIF_TERM esock_setopt_ip_mtu_discover(ErlNifEnv* env, * * The value is either the atom 'any' or a 4-tuple. */ -#ifndef __WIN32__ #if defined(IP_MULTICAST_IF) static ERL_NIF_TERM esock_setopt_multicast_if(ErlNifEnv* env, @@ -7526,11 +7271,11 @@ ERL_NIF_TERM esock_setopt_multicast_if(ErlNifEnv* env, return result; } #endif -#endif // #ifndef __WIN32__ + /* esock_setopt_tos - Level IP TOS option */ -#ifndef __WIN32__ + #if defined(IP_TOS) static ERL_NIF_TERM esock_setopt_tos(ErlNifEnv* env, @@ -7553,8 +7298,6 @@ ERL_NIF_TERM esock_setopt_tos(ErlNifEnv* env, return result; } #endif -#endif // #ifndef __WIN32__ - @@ -7563,7 +7306,7 @@ ERL_NIF_TERM esock_setopt_tos(ErlNifEnv* env, * The attribute 'interface' is either the atom 'any' or a 4-tuple * (IPv4 address). */ -#ifndef __WIN32__ + #if defined(IP_ADD_MEMBERSHIP) || defined(IP_DROP_MEMBERSHIP) static ERL_NIF_TERM esock_setopt_in_update_membership(ErlNifEnv* env, @@ -7614,7 +7357,6 @@ ERL_NIF_TERM esock_setopt_in_update_membership(ErlNifEnv* env, return esock_make_invalid(env, esock_atom_value); } #endif -#endif // #ifndef __WIN32__ /* The value is a map with three attributes: multiaddr, interface and @@ -7624,7 +7366,7 @@ ERL_NIF_TERM esock_setopt_in_update_membership(ErlNifEnv* env, * The attribute 'sourceaddr' is always a 4-tuple (IPv4 address). * (IPv4 address). */ -#ifndef __WIN32__ + #if defined(IP_ADD_SOURCE_MEMBERSHIP) || \ defined(IP_DROP_SOURCE_MEMBERSHIP) || \ defined(IP_BLOCK_SOURCE) || \ @@ -7659,15 +7401,11 @@ ERL_NIF_TERM esock_setopt_in_update_source(ErlNifEnv* env, return esock_make_invalid(env, esock_atom_value); } #endif -#endif // #ifndef __WIN32__ #if defined(HAVE_IPV6) - - -#ifndef __WIN32__ #if defined(IPV6_ADDRFORM) static ERL_NIF_TERM esock_setopt_addrform(ErlNifEnv* env, @@ -7694,7 +7432,6 @@ ERL_NIF_TERM esock_setopt_addrform(ErlNifEnv* env, &domain, sizeof(domain)); } #endif -#endif // #ifndef __WIN32__ @@ -7702,7 +7439,7 @@ ERL_NIF_TERM esock_setopt_addrform(ErlNifEnv* env, * * The value is an atom of the type ipv6_pmtudisc(). */ -#ifndef __WIN32__ + #if defined(IPV6_MTU_DISCOVER) static ERL_NIF_TERM esock_setopt_ipv6_mtu_discover(ErlNifEnv* env, @@ -7720,10 +7457,9 @@ ERL_NIF_TERM esock_setopt_ipv6_mtu_discover(ErlNifEnv* env, &val, sizeof(val)); } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ + #if defined(IPV6_MULTICAST_HOPS) static ERL_NIF_TERM esock_setopt_hops(ErlNifEnv* env, @@ -7741,10 +7477,9 @@ ERL_NIF_TERM esock_setopt_hops(ErlNifEnv* env, &hops, sizeof(hops)); } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ + #if defined(IPV6_ADD_MEMBERSHIP) || defined(IPV6_DROP_MEMBERSHIP) static ERL_NIF_TERM esock_setopt_in6_update_membership(ErlNifEnv* env, @@ -7793,7 +7528,6 @@ ERL_NIF_TERM esock_setopt_in6_update_membership(ErlNifEnv* env, return esock_make_invalid(env, esock_atom_value); } #endif -#endif // #ifndef __WIN32__ #endif // defined(HAVE_IPV6) @@ -7803,7 +7537,6 @@ ERL_NIF_TERM esock_setopt_in6_update_membership(ErlNifEnv* env, /* esock_setopt_tcp_congestion - Level TCP CONGESTION option */ -#ifndef __WIN32__ #if defined(TCP_CONGESTION) static ERL_NIF_TERM esock_setopt_tcp_congestion(ErlNifEnv* env, @@ -7817,17 +7550,13 @@ ERL_NIF_TERM esock_setopt_tcp_congestion(ErlNifEnv* env, return esock_setopt_str_opt(env, descP, level, opt, max, eVal); } #endif -#endif // #ifndef __WIN32__ - #if defined(HAVE_SCTP) - - /* esock_setopt_sctp_associnfo - Level SCTP ASSOCINFO option */ -#ifndef __WIN32__ + #if defined(SCTP_ASSOCINFO) static ERL_NIF_TERM esock_setopt_sctp_associnfo(ErlNifEnv* env, @@ -7908,12 +7637,12 @@ ERL_NIF_TERM esock_setopt_sctp_associnfo(ErlNifEnv* env, return esock_make_invalid(env, esock_atom_value); } #endif -#endif // #ifndef __WIN32__ + /* esock_setopt_sctp_events - Level SCTP EVENTS option */ -#ifndef __WIN32__ + #if defined(SCTP_EVENTS) static ERL_NIF_TERM esock_setopt_sctp_events(ErlNifEnv* env, @@ -8011,12 +7740,12 @@ static int esock_setopt_sctp_event(ErlNifEnv *env, return 0; } #endif -#endif // #ifndef __WIN32__ + /* esock_setopt_sctp_initmsg - Level SCTP INITMSG option */ -#ifndef __WIN32__ + #if defined(SCTP_INITMSG) static ERL_NIF_TERM esock_setopt_sctp_initmsg(ErlNifEnv* env, @@ -8079,12 +7808,12 @@ ERL_NIF_TERM esock_setopt_sctp_initmsg(ErlNifEnv* env, return esock_make_invalid(env, esock_atom_value); } #endif -#endif // #ifndef __WIN32__ + /* esock_setopt_sctp_rtoinfo - Level SCTP RTOINFO option */ -#ifndef __WIN32__ + #if defined(SCTP_RTOINFO) static ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv* env, @@ -8138,9 +7867,6 @@ ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv* env, return esock_make_invalid(env, esock_atom_value); } #endif -#endif // #ifndef __WIN32__ - - #endif // defined(HAVE_SCTP) @@ -8149,7 +7875,7 @@ ERL_NIF_TERM esock_setopt_sctp_rtoinfo(ErlNifEnv* env, /* esock_setopt_bool_opt - set an option that has an (integer) bool value */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_bool_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -8167,12 +7893,12 @@ ERL_NIF_TERM esock_setopt_bool_opt(ErlNifEnv* env, return esock_setopt_level_opt(env, descP, level, opt, &ival, sizeof(ival)); } -#endif // #ifndef __WIN32__ + /* esock_setopt_int_opt - set an option that has an integer value */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -8192,12 +7918,12 @@ ERL_NIF_TERM esock_setopt_int_opt(ErlNifEnv* env, } return result; } -#endif // #ifndef __WIN32__ + /* esock_setopt_str_opt - set an option that has an string value */ -#ifndef __WIN32__ + #if defined(USE_SETOPT_STR_OPT) static ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv* env, @@ -8228,12 +7954,12 @@ ERL_NIF_TERM esock_setopt_str_opt(ErlNifEnv* env, return result; } #endif -#endif // #ifndef __WIN32__ + /* esock_setopt_timeval_opt - set an option that has an (timeval) bool value */ -#ifndef __WIN32__ + #if (defined(SO_RCVTIMEO) || defined(SO_SNDTIMEO)) \ && defined(ESOCK_USE_RCVSNDTIMEO) static @@ -8270,10 +7996,8 @@ ERL_NIF_TERM esock_setopt_timeval_opt(ErlNifEnv* env, } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env, ESockDescriptor* descP, int level, @@ -8286,7 +8010,7 @@ static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env, else return esock_atom_ok; } -#endif // #ifndef __WIN32__ + /* +++ socket_setopt +++ @@ -8312,7 +8036,6 @@ static ERL_NIF_TERM esock_setopt_level_opt(ErlNifEnv* env, * user feeling socket options are independent. * </PaN> */ -#ifndef __WIN32__ static int socket_setopt(int sock, int level, int opt, const void* optVal, const socklen_t optLen) @@ -8373,7 +8096,6 @@ int socket_setopt(int sock, int level, int opt, return res; } -#endif // #ifndef __WIN32__ @@ -8397,58 +8119,67 @@ ERL_NIF_TERM nif_getopt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; + ERL_NIF_TERM esock, elevel, eopt, evspec; int level, opt; ESOCK_ASSERT( (argc == 3) || (argc == 4) ); - SGDBG( ("SOCKET", "nif_getopt -> entry with argc: %d\r\n", argc) ); + esock = argv[0]; + elevel = argv[1]; + eopt = argv[2]; + evspec = ((argc == 4) ? argv[3] : esock_atom_undefined); - if (! ESOCK_GET_RESOURCE(env, argv[0], (void**) &descP)) { - SGDBG( ("SOCKET", "nif_getopt -> failed initial args check\r\n") ); + SGDBG( ("SOCKET", + "nif_getopt -> entry with argc: %d" + "\r\n esock: %T" + "\r\n elevel: %T" + "\r\n eopt: %T" + "\r\n evspec: %T" + "\r\n", argc, esock, elevel, eopt, evspec) ); + + if (! ESOCK_GET_RESOURCE(env, esock, (void**) &descP)) { + SGDBG( ("SOCKET", + "nif_getopt -> failed initial args check - sock\r\n") ); return enif_make_badarg(env); } - if (! GET_INT(env, argv[2], &opt)) { + if (! GET_INT(env, eopt, &opt)) { SSDBG( descP, - ("SOCKET", "nif_getopt -> failed initial args check\r\n") ); - if (! IS_INTEGER(env, argv[2])) + ("SOCKET", + "nif_getopt -> failed initial args check - opt\r\n") ); + if (! IS_INTEGER(env, eopt)) return enif_make_badarg(env); else - return esock_make_error_integer_range(env, argv[2]); + return esock_make_error_integer_range(env, eopt); } - if (esock_decode_level(env, argv[1], &level)) { + if ((COMPARE(elevel, atom_otp) == 0) && + (argc == 3)) { + return ESOCK_IO_GETOPT_OTP(env, descP, opt) ; + } + + if (esock_decode_level(env, elevel, &level)) { if (argc == 4) { - ERL_NIF_TERM valueSpec = argv[3]; - return esock_getopt_native(env, descP, level, opt, valueSpec); + return ESOCK_IO_GETOPT_NATIVE(env, descP, level, opt, evspec); } else { - return esock_getopt(env, descP, level, opt); + return ESOCK_IO_GETOPT(env, descP, level, opt); } } - if ((COMPARE(argv[1], atom_otp) == 0) && - (argc == 3)) { - return esock_getopt_otp(env, descP, opt) ; - } - SGDBG( ("SOCKET", "nif_getopt -> failed args check\r\n") ); - if (IS_INTEGER(env, argv[1])) - return esock_make_error_integer_range(env, argv[1]); + if (IS_INTEGER(env, elevel)) + return esock_make_error_integer_range(env, elevel); else return enif_make_badarg(env); -#endif // #ifdef __WIN32__ #else } /* esock_getopt_otp - Handle OTP (level) options */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env, ESockDescriptor* descP, @@ -8562,11 +8293,11 @@ ERL_NIF_TERM esock_getopt_otp(ErlNifEnv* env, return result; } -#endif // #ifndef __WIN32__ + /* esock_getopt_otp_debug - Handle the OTP (level) debug option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_debug(ErlNifEnv* env, ESockDescriptor* descP) @@ -8584,11 +8315,11 @@ ERL_NIF_TERM esock_getopt_otp_debug(ErlNifEnv* env, return esock_make_ok2(env, eVal); } -#endif // #ifndef __WIN32__ + /* esock_getopt_otp_iow - Handle the OTP (level) iow option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_iow(ErlNifEnv* env, ESockDescriptor* descP) @@ -8611,12 +8342,12 @@ ERL_NIF_TERM esock_getopt_otp_iow(ErlNifEnv* env, return esock_make_ok2(env, eVal); } -#endif // #ifndef __WIN32__ + /* esock_getopt_otp_ctrl_proc - Handle the OTP (level) controlling_process option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_ctrl_proc(ErlNifEnv* env, ESockDescriptor* descP) @@ -8640,12 +8371,11 @@ ERL_NIF_TERM esock_getopt_otp_ctrl_proc(ErlNifEnv* env, return esock_make_ok2(env, eVal); } -#endif // #ifndef __WIN32__ /* esock_getopt_otp_rcvbuf - Handle the OTP (level) rcvbuf option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv* env, ESockDescriptor* descP) @@ -8659,6 +8389,9 @@ ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv* env, return esock_make_error_closed(env); } +#ifdef __WIN32__ + eVal = MKUL(env, (unsigned long) descP->rBufSz); +#else if (descP->rNum == 0) { eVal = MKUL(env, (unsigned long) descP->rBufSz); } else { @@ -8666,6 +8399,7 @@ ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv* env, MKI(env, descP->rNum), MKUL(env, (unsigned long) descP->rBufSz)); } +#endif SSDBG( descP, ("SOCKET", "esock_getopt_otp_rcvbuf {%d} ->" @@ -8674,12 +8408,12 @@ ERL_NIF_TERM esock_getopt_otp_rcvbuf(ErlNifEnv* env, return esock_make_ok2(env, eVal); } -#endif // #ifndef __WIN32__ + /* esock_getopt_otp_rcvctrlbuf - Handle the OTP (level) rcvctrlbuf option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_rcvctrlbuf(ErlNifEnv* env, ESockDescriptor* descP) @@ -8703,12 +8437,12 @@ ERL_NIF_TERM esock_getopt_otp_rcvctrlbuf(ErlNifEnv* env, return esock_make_ok2(env, eVal); } -#endif // #ifndef __WIN32__ + /* esock_getopt_otp_sndctrlbuf - Handle the OTP (level) sndctrlbuf option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_sndctrlbuf(ErlNifEnv* env, ESockDescriptor* descP) @@ -8732,12 +8466,12 @@ ERL_NIF_TERM esock_getopt_otp_sndctrlbuf(ErlNifEnv* env, return esock_make_ok2(env, eVal); } -#endif // #ifndef __WIN32__ + /* esock_getopt_otp_fd - Handle the OTP (level) fd option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_fd(ErlNifEnv* env, ESockDescriptor* descP) @@ -8760,12 +8494,12 @@ ERL_NIF_TERM esock_getopt_otp_fd(ErlNifEnv* env, return esock_make_ok2(env, eVal); } -#endif // #ifndef __WIN32__ + /* esock_getopt_otp_meta - Handle the OTP (level) meta option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_meta(ErlNifEnv* env, ESockDescriptor* descP) @@ -8788,12 +8522,12 @@ ERL_NIF_TERM esock_getopt_otp_meta(ErlNifEnv* env, return esock_make_ok2(env, eVal); } -#endif // #ifndef __WIN32__ + /* esock_getopt_otp_use_registry - Handle the OTP (level) use_registry option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_use_registry(ErlNifEnv* env, ESockDescriptor* descP) @@ -8802,13 +8536,13 @@ ERL_NIF_TERM esock_getopt_otp_use_registry(ErlNifEnv* env, return esock_make_ok2(env, eVal); } -#endif + /* * esock_getopt_otp_domain - Handle the OTP (level) domain option */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv* env, ESockDescriptor* descP) @@ -8832,7 +8566,6 @@ ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv* env, return result; } -#endif // #ifndef __WIN32__ @@ -8841,7 +8574,7 @@ ERL_NIF_TERM esock_getopt_otp_domain(ErlNifEnv* env, /* * esock_getopt_otp_type - Handle the OTP (level) type options. */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_type(ErlNifEnv* env, ESockDescriptor* descP) @@ -8865,13 +8598,13 @@ ERL_NIF_TERM esock_getopt_otp_type(ErlNifEnv* env, return result; } -#endif // #ifndef __WIN32__ + /* * esock_getopt_otp_protocol - Handle the OTP (level) protocol options. */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_protocol(ErlNifEnv* env, ESockDescriptor* descP) @@ -8895,13 +8628,13 @@ ERL_NIF_TERM esock_getopt_otp_protocol(ErlNifEnv* env, return result; } -#endif // #ifndef __WIN32__ + /* * esock_getopt_otp_dtp - Handle the OTP (level) type options. */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_otp_dtp(ErlNifEnv* env, ESockDescriptor* descP) @@ -8928,7 +8661,6 @@ ERL_NIF_TERM esock_getopt_otp_dtp(ErlNifEnv* env, return result; } -#endif // #ifndef __WIN32__ #endif // #if 0 @@ -8936,7 +8668,7 @@ ERL_NIF_TERM esock_getopt_otp_dtp(ErlNifEnv* env, /* How to decode the value is specified with valueSpec */ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env, ESockDescriptor* descP, @@ -9013,32 +8745,31 @@ ERL_NIF_TERM esock_getopt_native(ErlNifEnv* env, MUNLOCK(descP->readMtx); return result; } -#endif // #ifndef __WIN32__ + /* esock_getopt - An option that we know how to decode */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_getopt(ErlNifEnv* env, ESockDescriptor* descP, int level, int opt) { - ERL_NIF_TERM result; + ERL_NIF_TERM result; const struct ESockOpt *optP; MLOCK(descP->readMtx); SSDBG( descP, ("SOCKET", "esock_getopt {%d} -> entry with" - "\r\n level: %d" - "\r\n opt: %d" + "\r\n level: %d" + "\r\n opt: %d" "\r\n", descP->sock, level, opt) ); if (! IS_OPEN(descP->readState)) { SSDBG( descP, - ("SOCKET", "esock_getopt {%d} -> done closed\r\n", + ("SOCKET", "esock_getopt {%d} -> done when closed\r\n", descP->sock) ); MUNLOCK(descP->readMtx); return esock_make_error_closed(env); @@ -9075,10 +8806,8 @@ ERL_NIF_TERM esock_getopt(ErlNifEnv* env, MUNLOCK(descP->readMtx); return result; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ #if defined(SO_BINDTODEVICE) static ERL_NIF_TERM esock_getopt_so_bindtodevice(ErlNifEnv* env, @@ -9089,10 +8818,8 @@ ERL_NIF_TERM esock_getopt_so_bindtodevice(ErlNifEnv* env, return esock_getopt_str_opt(env, descP, level, opt, IFNAMSIZ+1, FALSE); } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ #if defined(SO_DOMAIN) static ERL_NIF_TERM esock_getopt_sock_domain(ErlNifEnv* env, @@ -9114,10 +8841,8 @@ ERL_NIF_TERM esock_getopt_sock_domain(ErlNifEnv* env, return result; } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ #if defined(SO_LINGER) static ERL_NIF_TERM esock_getopt_linger(ErlNifEnv* env, @@ -9132,7 +8857,11 @@ ERL_NIF_TERM esock_getopt_linger(ErlNifEnv* env, sys_memzero((void *) &val, sizeof(val)); +#ifdef __WIN32__ + res = sock_getopt(descP->sock, level, opt, (char*) &val, &valSz); +#else res = sock_getopt(descP->sock, level, opt, &val, &valSz); +#endif if (res != 0) { result = esock_make_error_errno(env, sock_errno()); @@ -9154,11 +8883,9 @@ ERL_NIF_TERM esock_getopt_linger(ErlNifEnv* env, return result; } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ #if defined(SO_TYPE) static ERL_NIF_TERM esock_getopt_sock_type(ErlNifEnv* env, @@ -9180,10 +8907,8 @@ ERL_NIF_TERM esock_getopt_sock_type(ErlNifEnv* env, return result; } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ #if defined(SO_PROTOCOL) static ERL_NIF_TERM esock_getopt_sock_protocol(ErlNifEnv* env, @@ -9216,10 +8941,8 @@ ERL_NIF_TERM esock_getopt_sock_protocol(ErlNifEnv* env, return result; } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ /* esock_getopt_ip_mtu_discover - Level IP MTU_DISCOVER option */ #if defined(IP_MTU_DISCOVER) @@ -9244,12 +8967,11 @@ ERL_NIF_TERM esock_getopt_ip_mtu_discover(ErlNifEnv* env, } #endif -#endif // #ifndef __WIN32__ /* esock_getopt_multicast_if - Level IP MULTICAST_IF option */ -#ifndef __WIN32__ + #if defined(IP_MULTICAST_IF) static ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv* env, @@ -9265,7 +8987,11 @@ ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv* env, sys_memzero((void *) &ifAddr, ifAddrSz); +#ifdef __WIN32__ + res = sock_getopt(descP->sock, level, opt, (char*) &ifAddr, &ifAddrSz); +#else res = sock_getopt(descP->sock, level, opt, &ifAddr, &ifAddrSz); +#endif if (res != 0) { result = esock_make_error_errno(env, sock_errno()); @@ -9278,12 +9004,12 @@ ERL_NIF_TERM esock_getopt_multicast_if(ErlNifEnv* env, } #endif -#endif // #ifndef __WIN32__ + /* esock_getopt_tos - Level IP TOS option */ -#ifndef __WIN32__ + #if defined(IP_TOS) static ERL_NIF_TERM esock_getopt_tos(ErlNifEnv* env, @@ -9303,15 +9029,14 @@ ERL_NIF_TERM esock_getopt_tos(ErlNifEnv* env, return result; } #endif -#endif // #ifndef __WIN32__ -#if defined(HAVE_IPV6) +#if defined(HAVE_IPV6) /* esock_getopt_ipv6_mtu_discover - Level IPv6 MTU_DISCOVER option */ -#ifndef __WIN32__ + #if defined(IPV6_MTU_DISCOVER) static ERL_NIF_TERM esock_getopt_ipv6_mtu_discover(ErlNifEnv* env, @@ -9334,16 +9059,13 @@ ERL_NIF_TERM esock_getopt_ipv6_mtu_discover(ErlNifEnv* env, } #endif -#endif // #ifndef __WIN32__ - #endif // defined(HAVE_IPV6) - /* esock_getopt_tcp_congestion - Level TCP CONGESTION option */ -#ifndef __WIN32__ + #if defined(TCP_CONGESTION) static ERL_NIF_TERM esock_getopt_tcp_congestion(ErlNifEnv* env, @@ -9356,14 +9078,11 @@ ERL_NIF_TERM esock_getopt_tcp_congestion(ErlNifEnv* env, return esock_getopt_str_opt(env, descP, level, opt, max, TRUE); } #endif -#endif // #ifndef __WIN32__ #if defined(HAVE_SCTP) - - /* esock_getopt_sctp_associnfo - Level SCTP ASSOCINFO option * * <KOLLA> @@ -9527,7 +9246,6 @@ ERL_NIF_TERM esock_getopt_sctp_rtoinfo(ErlNifEnv* env, /* esock_getopt_bool_opt - get an (integer) bool option */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -9546,12 +9264,10 @@ ERL_NIF_TERM esock_getopt_bool_opt(ErlNifEnv* env, } return result; } -#endif // #ifndef __WIN32__ /* esock_getopt_int_opt - get an integer option */ -#ifndef __WIN32__ static ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -9565,13 +9281,11 @@ ERL_NIF_TERM esock_getopt_int_opt(ErlNifEnv* env, return esock_make_ok2(env, MKI(env, val)); } -#endif // #ifndef __WIN32__ /* esock_getopt_int - get an integer option */ -#ifndef __WIN32__ extern BOOLEAN_T esock_getopt_int(SOCKET sock, int level, @@ -9581,17 +9295,19 @@ BOOLEAN_T esock_getopt_int(SOCKET sock, int val = 0; SOCKOPTLEN_T valSz = sizeof(val); +#ifdef __WIN32__ + if (sock_getopt(sock, level, opt, (char*) &val, &valSz) != 0) +#else if (sock_getopt(sock, level, opt, &val, &valSz) != 0) +#endif return FALSE; *valP = val; return TRUE; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ static ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -9638,10 +9354,9 @@ ERL_NIF_TERM esock_getopt_size_opt(ErlNifEnv* env, return result; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ + static ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv* env, ESockDescriptor* descP, @@ -9684,7 +9399,7 @@ ERL_NIF_TERM esock_getopt_bin_opt(ErlNifEnv* env, return result; } -#endif // #ifndef __WIN32__ + /* esock_getopt_timeval_opt - get an timeval option @@ -9736,11 +9451,11 @@ ERL_NIF_TERM esock_getopt_timeval_opt(ErlNifEnv* env, * Some platforms (seen on ppc Linux 2.6.29-3.ydl61.3) * may return 0 as the cmsg_len if the cmsg is to be ignored. */ -#define LEN_CMSG_DATA(__CMSG__) \ - ((__CMSG__)->cmsg_len < sizeof (struct cmsghdr) ? 0 : \ - (__CMSG__)->cmsg_len - ((char*)CMSG_DATA(__CMSG__) - (char*)(__CMSG__))) -#define NEXT_CMSG_HDR(__CMSG__) \ - ((struct cmsghdr*)(((char*)(__CMSG__)) + CMSG_SPACE(LEN_CMSG_DATA(__CMSG__)))) +#define ESOCK_LEN_CMSG_DATA(__CMSG__) \ + ((__CMSG__)->cmsg_len < sizeof (struct cmsghdr) ? 0 : \ + (__CMSG__)->cmsg_len - ((char*)ESOCK_CMSG_DATA(__CMSG__) - (char*)(__CMSG__))) +#define ESOCK_NEXT_CMSG_HDR(__CMSG__) \ + ((struct cmsghdr*)(((char*)(__CMSG__)) + ESOCK_CMSG_SPACE(ESOCK_LEN_CMSG_DATA(__CMSG__)))) static ERL_NIF_TERM esock_getopt_pktoptions(ErlNifEnv* env, @@ -9772,8 +9487,8 @@ ERL_NIF_TERM esock_getopt_pktoptions(ErlNifEnv* env, for (endOfBuf = (struct cmsghdr*)(cmsgs.data + cmsgs.size), currentP = (struct cmsghdr*)(cmsgs.data); (currentP != NULL) && (currentP < endOfBuf); - currentP = NEXT_CMSG_HDR(currentP)) { - unsigned char* dataP = UCHARP(CMSG_DATA(currentP)); + currentP = ESOCK_NEXT_CMSG_HDR(currentP)) { + unsigned char* dataP = UCHARP(ESOCK_CMSG_DATA(currentP)); size_t dataPos = dataP - cmsgs.data; size_t dataLen = (UCHARP(currentP) + currentP->cmsg_len) - dataP; @@ -9936,9 +9651,6 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM res; @@ -9967,7 +9679,52 @@ ERL_NIF_TERM nif_sockname(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; -#endif // #ifdef __WIN32__ #else +} + + + +/* ======================================================================== + */ + +static +ERL_NIF_TERM esock_sockname(ErlNifEnv* env, + ESockDescriptor* descP) +{ + ESockAddress sa; + ESockAddress* saP = &sa; +#ifdef __WIN32__ + int sz = sizeof(ESockAddress); +#else + SOCKLEN_T sz = sizeof(ESockAddress); +#endif + + if (! IS_OPEN(descP->readState)) + return esock_make_error_closed(env); + + SSDBG( descP, + ("SOCKET", "esock_sockname {%d} -> open - try get sockname\r\n", + descP->sock) ); + + sys_memzero((char*) saP, sz); + if (sock_name(descP->sock, (struct sockaddr*) saP, &sz) < 0) { + return esock_make_error_errno(env, sock_errno()); + } else { + ERL_NIF_TERM esa; + + SSDBG( descP, + ("SOCKET", "esock_sockname {%d} -> " + "got sockname - try decode\r\n", + descP->sock) ); + + esock_encode_sockaddr(env, saP, (SOCKLEN_T) sz, &esa); + + SSDBG( descP, + ("SOCKET", "esock_sockname {%d} -> decoded: " + "\r\n %T\r\n", + descP->sock, esa) ); + + return esock_make_ok2(env, esa); + } } @@ -9987,9 +9744,6 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM res; @@ -10018,7 +9772,52 @@ ERL_NIF_TERM nif_peername(ErlNifEnv* env, MUNLOCK(descP->readMtx); return res; -#endif // #ifdef __WIN32__ #else +} + + + +/* ======================================================================== + */ + +static +ERL_NIF_TERM esock_peername(ErlNifEnv* env, + ESockDescriptor* descP) +{ + ESockAddress sa; + ESockAddress* saP = &sa; +#ifdef __WIN32__ + int sz = sizeof(ESockAddress); +#else + SOCKLEN_T sz = sizeof(ESockAddress); +#endif + + if (! IS_OPEN(descP->readState)) + return esock_make_error_closed(env); + + SSDBG( descP, + ("SOCKET", "esock_peername {%d} -> open - try get peername\r\n", + descP->sock) ); + + sys_memzero((char*) saP, sz); + if (sock_peer(descP->sock, (struct sockaddr*) saP, &sz) < 0) { + return esock_make_error_errno(env, sock_errno()); + } else { + ERL_NIF_TERM esa; + + SSDBG( descP, + ("SOCKET", "esock_peername {%d} -> " + "got peername - try decode\r\n", + descP->sock) ); + + esock_encode_sockaddr(env, saP, (SOCKLEN_T) sz, &esa); + + SSDBG( descP, + ("SOCKET", "esock_peername {%d} -> decoded: " + "\r\n %T\r\n", + descP->sock, esa) ); + + return esock_make_ok2(env, esa); + } } @@ -10047,9 +9846,6 @@ ERL_NIF_TERM nif_ioctl(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM res; unsigned long req; @@ -10072,49 +9868,54 @@ ERL_NIF_TERM nif_ioctl(ErlNifEnv* env, ("SOCKET", "nif_ioctl(%T) {%d} -> ioctl request %d" "\r\n", argv[0], descP->sock, req) ); + /* Is this really enough? Why not the write mutex also? */ MLOCK(descP->readMtx); if (! IS_OPEN(descP->readState)) { res = esock_make_error_closed(env); } else { - if (argc == 2) { - - /* Only one request with this number of arguments: gifconf - * Socket and request (=gifconf) - */ - - /* Two arguments: Socket and get request */ - res = esock_ioctl1(env, descP, req); - - } else if (argc == 3) { - - /* (Currently) All *other* get requests has 3 arguments - * Socket, request and name/index - */ - - ERL_NIF_TERM earg = argv[2]; - - /* Two arguments: request and arg */ - res = esock_ioctl2(env, descP, req, earg); - - } else if (argc == 4) { - - /* (Currently) Set requests has 4 arguments - * Socket, request, name and value - */ - - ERL_NIF_TERM earg1 = argv[2]; // (currently) Name - ERL_NIF_TERM earg2 = argv[3]; // Value - - /* Three arguments: request, arg1 (name) and arg2 (value) */ - res = esock_ioctl3(env, descP, req, earg1, earg2); - - } else { - - res = esock_make_error(env, esock_atom_einval); - - } + switch (argc) { + case 2: + /* Only one request with this number of arguments: gifconf + * Socket and request (=gifconf) + */ + + /* Two arguments: socket and request */ + res = ESOCK_IO_IOCTL_2(env, descP, req); + break; + + case 3: + /* (Currently) All *other* get requests has 3 arguments + * Socket, request and name/index + */ + { + ERL_NIF_TERM earg = argv[2]; + + /* Three arguments: socket, request and arg */ + res = ESOCK_IO_IOCTL_3(env, descP, req, earg); + } + break; + + case 4: + /* (Currently) Set requests has 4 arguments + * Socket, request, name and value + */ + { + ERL_NIF_TERM earg1 = argv[2]; // (currently) Name + ERL_NIF_TERM earg2 = argv[3]; // Value + + res = ESOCK_IO_IOCTL_4(env, descP, req, earg1, earg2); + } + break; + + default: + /* This is just to protect against programming errors, + * since we have an assert above! + */ + res = esock_make_error(env, esock_atom_einval); + break; + } } MUNLOCK(descP->readMtx); @@ -10124,1132 +9925,8 @@ ERL_NIF_TERM nif_ioctl(ErlNifEnv* env, argv[0], descP->sock, res) ); return res; -#endif // #ifdef __WIN32__ #else -} - - - -#ifndef __WIN32__ - -static -ERL_NIF_TERM esock_ioctl1(ErlNifEnv* env, - ESockDescriptor* descP, - unsigned long req) -{ - switch (req) { - -#if defined(SIOCGIFCONF) - case SIOCGIFCONF: - return esock_ioctl_gifconf(env, descP); - break; -#endif - - default: - return esock_make_error(env, esock_atom_enotsup); - break; - } - -} - - -/* The type and value of 'arg' depend on the request, - * which we have not yet "analyzed". - * - * Request arg arg type - * ------- ------- -------- - * gifname ifindex integer - * gifindex name string - * gifflags name string - * gifaddr name string - * gifdstaddr name string - * gifbdraddr name string - * gifnetmask name string - * gifmtu name string - * gifhwaddr name string - * gifmap name string - * giftxqlen name string - */ -static -ERL_NIF_TERM esock_ioctl2(ErlNifEnv* env, - ESockDescriptor* descP, - unsigned long req, - ERL_NIF_TERM arg) -{ - /* This for *get* requests */ - - switch (req) { - -#if defined(SIOCGIFNAME) - case SIOCGIFNAME: - return esock_ioctl_gifname(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFINDEX) - case SIOCGIFINDEX: - return esock_ioctl_gifindex(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFFLAGS) - case SIOCGIFFLAGS: - return esock_ioctl_gifflags(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFADDR) - case SIOCGIFADDR: - return esock_ioctl_gifaddr(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFDSTADDR) - case SIOCGIFDSTADDR: - return esock_ioctl_gifdstaddr(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFBRDADDR) - case SIOCGIFBRDADDR: - return esock_ioctl_gifbrdaddr(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFNETMASK) - case SIOCGIFNETMASK: - return esock_ioctl_gifnetmask(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFMTU) - case SIOCGIFMTU: - return esock_ioctl_gifmtu(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) - case SIOCGIFHWADDR: - return esock_ioctl_gifhwaddr(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) - case SIOCGIFMAP: - return esock_ioctl_gifmap(env, descP, arg); - break; -#endif - -#if defined(SIOCGIFTXQLEN) - case SIOCGIFTXQLEN: - return esock_ioctl_giftxqlen(env, descP, arg); - break; -#endif - - default: - return esock_make_error(env, esock_atom_enotsup); - break; - } - -} - - -/* The type and value of arg(s) depend on the request, - * which we have not yet "analyzed". - * - * Request arg1 arg1 type arg2 arg2 type - * ------- ------- --------- ------ --------- - * sifflags name string Flags #{IntFlag := boolean()} - * IntFlag is the native flag - * sifaddr name string Addr sockaddr() - * sifdstaddr name string DstAddr sockaddr() - * sifbrdaddr name string BrdAddr sockaddr() - * sifnetmask name string NetMask sockaddr() - * gifmtu name string MTU integer() - * sifhwaddr name string HwAddr sockaddr() - * giftxqlen name string Len integer() - */ -static -ERL_NIF_TERM esock_ioctl3(ErlNifEnv* env, - ESockDescriptor* descP, - unsigned long req, - ERL_NIF_TERM ename, - ERL_NIF_TERM eval) -{ - - switch (req) { - -#if defined(SIOCSIFFLAGS) - case SIOCSIFFLAGS: - return esock_ioctl_sifflags(env, descP, ename, eval); - break; -#endif - -#if defined(SIOCSIFADDR) - case SIOCSIFADDR: - return esock_ioctl_sifaddr(env, descP, ename, eval); - break; -#endif - -#if defined(SIOCSIFDSTADDR) - case SIOCSIFDSTADDR: - return esock_ioctl_sifdstaddr(env, descP, ename, eval); - break; -#endif - -#if defined(SIOCSIFBRDADDR) - case SIOCSIFBRDADDR: - return esock_ioctl_sifbrdaddr(env, descP, ename, eval); - break; -#endif - -#if defined(SIOCSIFNETMASK) - case SIOCSIFNETMASK: - return esock_ioctl_sifnetmask(env, descP, ename, eval); - break; -#endif - -#if defined(SIOCSIFMTU) - case SIOCSIFMTU: - return esock_ioctl_sifmtu(env, descP, ename, eval); - break; -#endif - -#if defined(SIOCSIFTXQLEN) - case SIOCSIFTXQLEN: - return esock_ioctl_siftxqlen(env, descP, ename, eval); - break; -#endif - - default: - return esock_make_error(env, esock_atom_enotsup); - break; - } - -} - - -/* =========================================================================== - * The implemented (ioctl) get requests falls into three grops: - * - * 1) gifconf - Takes no argument other then the request - * 2) gifname - Takes the interface index (integer) as an argument - * 3) other - All other (get) requests takes the interface name (string) - * as the argument. - * - * The functions defined using the macros below are all in the third (3) - * group. - * - */ - -/* *** esock_ioctl_gifindex *** */ -#if defined(SIOCGIFINDEX) -#if defined(ESOCK_USE_IFINDEX) -#define IOCTL_GIFINDEX_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_ifindex) -#elif defined(ESOCK_USE_INDEX) -#define IOCTL_GIFINDEX_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_index) -#else -#define IOCTL_GIFINDEX_FUNC_DECL -#endif -#else -#define IOCTL_GIFINDEX_FUNC_DECL -#endif - -/* *** esock_ioctl_gifflags *** */ -#if defined(SIOCGIFFLAGS) -#define IOCTL_GIFFLAGS_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifflags, SIOCGIFFLAGS, flags, ifreq.ifr_flags) -#else -#define IOCTL_GIFFLAGS_FUNC_DECL -#endif - -/* *** esock_ioctl_gifaddr *** */ -#if defined(SIOCGIFADDR) -#define IOCTL_GIFADDR_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifaddr, SIOCGIFADDR, ifraddr, &ifreq.ifr_addr) -#else -#define IOCTL_GIFADDR_FUNC_DECL -#endif - -/* *** esock_ioctl_gifdstaddr *** */ -#if defined(SIOCGIFDSTADDR) -#define IOCTL_GIFDSTADDR_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifdstaddr, SIOCGIFDSTADDR, ifraddr, &ifreq.ifr_dstaddr) -#else -#define IOCTL_GIFDSTADDR_FUNC_DECL -#endif - -/* *** esock_ioctl_gifbrdaddr *** */ -#if defined(SIOCGIFBRDADDR) -#define IOCTL_GIFBRDADDR_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifbrdaddr, SIOCGIFBRDADDR, ifraddr, &ifreq.ifr_broadaddr) -#else -#define IOCTL_GIFBRDADDR_FUNC_DECL -#endif - -/* *** esock_ioctl_gifnetmask *** */ -#if defined(SIOCGIFNETMASK) -#ifdef __linux__ -#define IOCTL_GIFNETMASK_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_netmask) -#else -#define IOCTL_GIFNETMASK_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_addr) -#endif -#else -#define IOCTL_GIFNETMASK_FUNC_DECL -#endif - -/* *** esock_ioctl_gifmtu *** */ -#if defined(SIOCGIFMTU) -#define IOCTL_GIFMTU_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifmtu, SIOCGIFMTU, ivalue, ifreq.ifr_mtu) -#else -#define IOCTL_GIFMTU_FUNC_DECL -#endif - -/* *** esock_ioctl_gifhwaddr *** */ -#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) -#define IOCTL_GIFHWADDR_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifhwaddr, SIOCGIFHWADDR, hwaddr, &ifreq.ifr_hwaddr) -#else -#define IOCTL_GIFHWADDR_FUNC_DECL -#endif - -/* *** esock_ioctl_gifmap *** */ -#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) -#define IOCTL_GIFMAP_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(gifmap, SIOCGIFMAP, ifrmap, &ifreq.ifr_map) -#else -#define IOCTL_GIFMAP_FUNC_DECL -#endif - -/* *** esock_ioctl_giftxqlen *** */ -#if defined(SIOCGIFTXQLEN) -#define IOCTL_GIFTXQLEN_FUNC_DECL \ - IOCTL_GET_REQUEST_DECL(giftxqlen, SIOCGIFTXQLEN, ivalue, ifreq.ifr_qlen) -#else -#define IOCTL_GIFTXQLEN_FUNC_DECL -#endif - -#define IOCTL_GET_FUNCS \ - IOCTL_GIFINDEX_FUNC_DECL \ - IOCTL_GIFFLAGS_FUNC_DECL \ - IOCTL_GIFADDR_FUNC_DECL \ - IOCTL_GIFDSTADDR_FUNC_DECL \ - IOCTL_GIFBRDADDR_FUNC_DECL \ - IOCTL_GIFNETMASK_FUNC_DECL \ - IOCTL_GIFMTU_FUNC_DECL \ - IOCTL_GIFHWADDR_FUNC_DECL \ - IOCTL_GIFMAP_FUNC_DECL \ - IOCTL_GIFTXQLEN_FUNC_DECL - -#define IOCTL_GET_REQUEST_DECL(OR, R, EF, UV) \ - static \ - ERL_NIF_TERM esock_ioctl_##OR(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ERL_NIF_TERM ename) \ - { \ - ERL_NIF_TERM result; \ - struct ifreq ifreq; \ - char* ifn = NULL; \ - int nlen; \ - \ - SSDBG( descP, ("SOCKET", "esock_ioctl_" #OR " {%d} -> entry with" \ - "\r\n (e)Name: %T" \ - "\r\n", descP->sock, ename) ); \ - \ - if (!esock_decode_string(env, ename, &ifn)) \ - return enif_make_badarg(env); \ - \ - nlen = esock_strnlen(ifn, IFNAMSIZ); \ - \ - sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \ - sys_memcpy(ifreq.ifr_name, ifn, \ - (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \ - \ - SSDBG( descP, \ - ("SOCKET", \ - "esock_ioctl_" #OR " {%d} -> try ioctl\r\n", \ - descP->sock) ); \ - \ - if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \ - int saveErrno = sock_errno(); \ - ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \ - \ - SSDBG( descP, \ - ("SOCKET", "esock_ioctl_" #OR " {%d} -> failure: " \ - "\r\n reason: %T (%d)" \ - "\r\n", descP->sock, reason, saveErrno) ); \ - \ - result = esock_make_error(env, reason); \ - \ - } else { \ - SSDBG( descP, \ - ("SOCKET", "esock_ioctl_" #OR " {%d} -> encode value\r\n", \ - descP->sock) ); \ - result = encode_ioctl_##EF(env, descP, UV); \ - } \ - \ - FREE(ifn); \ - \ - return result; \ - \ - } -IOCTL_GET_FUNCS -#undef IOCTL_GET_FUNCS - - -/* =========================================================================== - * The "rest" of the implemented (ioctl) get requests - * - * These (get) requests could not be 'generated' by the macros above. - */ - -static -ERL_NIF_TERM esock_ioctl_gifconf(ErlNifEnv* env, - ESockDescriptor* descP) -{ - struct ifconf ifc; - int ifc_len = 0; - int buflen = 100 * sizeof(struct ifreq); - char *buf = MALLOC(buflen); - ERL_NIF_TERM result; - - SSDBG( descP, ("SOCKET", "esock_ioctl_gifconf {%d} -> entry\r\n", descP->sock) ); - - for (;;) { - ifc.ifc_len = buflen; - ifc.ifc_buf = buf; - if (ioctl(descP->sock, SIOCGIFCONF, (char *) &ifc) < 0) { - int saveErrno = sock_errno(); - - SSDBG( descP, - ("SOCKET", "esock_ioctl_gifconf {%d} -> failure: " - "\r\n errno: %d (%s)" - "\r\n", descP->sock, saveErrno, erl_errno_id(saveErrno)) ); - - if (saveErrno != EINVAL || ifc_len) { - ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); - FREE(buf); - return esock_make_error(env, reason); - } - } else { - if (ifc.ifc_len == ifc_len) break; /* buf large enough */ - ifc_len = ifc.ifc_len; - } - buflen += 10 * sizeof(struct ifreq); - buf = (char *) REALLOC(buf, buflen); - } - - result = encode_ioctl_ifconf(env, descP, &ifc); - - FREE(ifc.ifc_buf); - - return result; -} - - -#if defined(SIOCGIFNAME) -static -ERL_NIF_TERM esock_ioctl_gifname(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM eidx) -{ - ERL_NIF_TERM result; - struct ifreq ifreq; - int index; - - SSDBG( descP, ("SOCKET", "esock_ioctl_gifname {%d} -> entry with" - "\r\n (e)Index: %T" - "\r\n", descP->sock, eidx) ); - - if (!GET_INT(env, eidx, &index)) - return enif_make_badarg(env); - - ifreq.ifr_ifindex = index; - - SSDBG( descP, - ("SOCKET", "esock_ioctl_gifname {%d} -> try ioctl\r\n", descP->sock) ); - - if (ioctl(descP->sock, SIOCGIFNAME, (char *) &ifreq) < 0) { - int saveErrno = sock_errno(); - ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); - - SSDBG( descP, - ("SOCKET", "esock_ioctl_gifname {%d} -> failure: " - "\r\n reason: %T (%d)" - "\r\n", descP->sock, reason, saveErrno) ); - - result = esock_make_error(env, reason); - - } else { - SSDBG( descP, - ("SOCKET", "esock_ioctl_gifname {%d} -> encode name\r\n", - descP->sock) ); - - result = esock_make_ok2(env, encode_ioctl_ifreq_name(env, ifreq.ifr_name)); - } - - SSDBG( descP, - ("SOCKET", "esock_ioctl_gifname {%d} -> done with" - "\r\n result: %T" - "\r\n", - descP->sock, result) ); - - return result; - -} -#endif - - - - -/* =========================================================================== - * The implemented (ioctl) set requests: - * - */ - -/* *** esock_ioctl_sifaddr *** */ -#if defined(SIOCSIFADDR) -#define IOCTL_SIFADDR_FUNC_DECL \ - IOCTL_SET_REQUEST_DECL(sifaddr, SIOCSIFADDR, sockaddr, \ - ((ESockAddress*) &ifreq.ifr_addr)) -#else -#define IOCTL_SIFADDR_FUNC_DECL -#endif - -/* *** esock_ioctl_sifdstaddr *** */ -#if defined(SIOCSIFDSTADDR) -#define IOCTL_SIFDSTADDR_FUNC_DECL \ - IOCTL_SET_REQUEST_DECL(sifdstaddr, SIOCSIFDSTADDR, sockaddr, \ - ((ESockAddress*) &ifreq.ifr_dstaddr)) -#else -#define IOCTL_SIFDSTADDR_FUNC_DECL -#endif - -/* *** esock_ioctl_sifbrdaddr *** */ -#if defined(SIOCSIFBRDADDR) -#define IOCTL_SIFBRDADDR_FUNC_DECL \ - IOCTL_SET_REQUEST_DECL(sifbrdaddr, SIOCSIFBRDADDR, sockaddr, \ - ((ESockAddress*) &ifreq.ifr_broadaddr)) -#else -#define IOCTL_SIFBRDADDR_FUNC_DECL -#endif - -/* *** esock_ioctl_sifnetmask *** */ -#if defined(SIOCSIFNETMASK) -#ifdef __linux__ -#define IOCTL_SIFNETMASK_FUNC_DECL \ - IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \ - ((ESockAddress*) &ifreq.ifr_netmask)) -#else -#define IOCTL_SIFNETMASK_FUNC_DECL \ - IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \ - ((ESockAddress*) &ifreq.ifr_addr)) -#endif -#else -#define IOCTL_SIFNETMASK_FUNC_DECL -#endif - -/* *** esock_ioctl_sifmtu *** - * On some platforms, MTU is an unsigned int - */ -#if defined(SIOCSIFMTU) -#define IOCTL_SIFMTU_FUNC_DECL \ - IOCTL_SET_REQUEST_DECL(sifmtu, SIOCSIFMTU, mtu, (int*) &ifreq.ifr_mtu) -#else -#define IOCTL_SIFMTU_FUNC_DECL -#endif - -/* *** esock_ioctl_siftxqlen *** */ -#if defined(SIOCSIFTXQLEN) -#define IOCTL_SIFTXQLEN_FUNC_DECL \ - IOCTL_SET_REQUEST_DECL(siftxqlen, SIOCSIFTXQLEN, txqlen, &ifreq.ifr_qlen) -#else -#define IOCTL_SIFTXQLEN_FUNC_DECL -#endif - -#define IOCTL_SET_FUNCS \ - IOCTL_SIFADDR_FUNC_DECL \ - IOCTL_SIFDSTADDR_FUNC_DECL \ - IOCTL_SIFBRDADDR_FUNC_DECL \ - IOCTL_SIFNETMASK_FUNC_DECL \ - IOCTL_SIFMTU_FUNC_DECL \ - IOCTL_SIFTXQLEN_FUNC_DECL - -#define IOCTL_SET_REQUEST_DECL(OR, R, DF, UVP) \ - static \ - ERL_NIF_TERM esock_ioctl_##OR(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ERL_NIF_TERM ename, \ - ERL_NIF_TERM evalue) \ - { \ - ERL_NIF_TERM result; \ - struct ifreq ifreq; \ - char* ifn = NULL; \ - int nlen; \ - \ - SSDBG( descP, ("SOCKET", "esock_ioctl_" #OR " {%d} -> entry with" \ - "\r\n (e)Name: %T" \ - "\r\n (e)Value: %T" \ - "\r\n", descP->sock, ename, evalue) ); \ - \ - if (!esock_decode_string(env, ename, &ifn)) { \ - \ - SSDBG( descP, \ - ("SOCKET", "esock_ioctl_" #OR " {%d} -> failed decode name" \ - "\r\n", descP->sock) ); \ - \ - return enif_make_badarg(env); \ - } \ - \ - if (! decode_ioctl_##DF(env, descP, evalue, UVP)) { \ - \ - SSDBG( descP, \ - ("SOCKET", "esock_ioctl_" #OR " {%d} -> failed decode addr" \ - "\r\n", descP->sock) ); \ - \ - return esock_make_invalid(env, atom_##DF); \ - } \ - \ - nlen = esock_strnlen(ifn, IFNAMSIZ); \ - \ - sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \ - sys_memcpy(ifreq.ifr_name, ifn, \ - (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \ - \ - SSDBG( descP, \ - ("SOCKET", "esock_ioctl_" #OR " {%d} -> try ioctl\r\n", \ - descP->sock) ); \ - \ - if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \ - int saveErrno = sock_errno(); \ - ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \ - \ - SSDBG( descP, \ - ("SOCKET", "esock_ioctl_" #OR " {%d} -> failure: " \ - "\r\n reason: %T (%d)" \ - "\r\n", descP->sock, reason, saveErrno) ); \ - \ - result = esock_make_error(env, reason); \ - \ - } else { \ - SSDBG( descP, \ - ("SOCKET", "esock_ioctl_" #OR " {%d} -> " \ - "addr successfully set\r\n", \ - descP->sock) ); \ - result = esock_atom_ok; \ - } \ - \ - FREE(ifn); \ - \ - return result; \ - \ - } - -IOCTL_SET_FUNCS -#undef IOCTL_SET_FUNCS - - -/* =========================================================================== - * The "rest" of the implemented (ioctl) set requests - * - * These (set) requests could not be 'generated' by the macros above. - */ - -#if defined(SIOCSIFFLAGS) -static -ERL_NIF_TERM esock_ioctl_sifflags(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM ename, - ERL_NIF_TERM eflags) -{ - ERL_NIF_TERM result; - struct ifreq ifreq; - char* ifn = NULL; - int nlen; - - SSDBG( descP, ("SOCKET", "esock_ioctl_sifflags {%d} -> entry with" - "\r\n (e)Name: %T" - "\r\n (e)Flags: %T" - "\r\n", descP->sock, ename, eflags) ); - - if (!esock_decode_string(env, ename, &ifn)) { - - SSDBG( descP, - ("SOCKET", "esock_ioctl_sifflags {%d} -> failed decode name" - "\r\n", descP->sock) ); - - return enif_make_badarg(env); - } - - // Make sure the length of the string is valid! - nlen = esock_strnlen(ifn, IFNAMSIZ); - - sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); // Just in case - sys_memcpy(ifreq.ifr_name, ifn, - (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); - - SSDBG( descP, - ("SOCKET", "esock_ioctl_sifflags {%d} -> try (get) ioctl\r\n", - descP->sock) ); - - if (ioctl(descP->sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) { - int saveErrno = sock_errno(); - ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); - - SSDBG( descP, - ("SOCKET", "esock_ioctl_sifflags {%d} -> " - "failure: failed reading *current* flags" - "\r\n reason: %T (%d)" - "\r\n", descP->sock, reason, saveErrno) ); - - result = esock_make_error(env, reason); - - } else { - - SSDBG( descP, - ("SOCKET", "esock_ioctl_sifflags {%d} -> (local) update flags\r\n", - descP->sock) ); - - if (decode_ioctl_flags(env, descP, eflags, &ifreq.ifr_flags)) { - - SSDBG( descP, - ("SOCKET", "esock_ioctl_sifflags {%d} -> try (set) ioctl\r\n", - descP->sock) ); - - if (ioctl(descP->sock, SIOCSIFFLAGS, (char *) &ifreq) < 0) { - int saveErrno = sock_errno(); - ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); - - SSDBG( descP, - ("SOCKET", "esock_ioctl_sifflags {%d} -> failure: " - "\r\n reason: %T (%d)" - "\r\n", descP->sock, reason, saveErrno) ); - - result = esock_make_error(env, reason); - - } else { - SSDBG( descP, - ("SOCKET", "esock_ioctl_sifflags {%d} -> " - "updated flags successfully set\r\n", - descP->sock) ); - result = esock_atom_ok; - } - - /* We know that if esock_decode_string is successful, - * we have "some" form of string, and therefor memory - * has been allocated (and need to be freed)... */ - FREE(ifn); - - } else { - result = enif_make_badarg(env); - } - } - - SSDBG( descP, - ("SOCKET", "esock_ioctl_sifflags {%d} -> done with result: " - "\r\n %T" - "\r\n", - descP->sock, result) ); - - return result; - -} -#endif - - - -/* =========================================================================== - * ioctl utility functions - * - */ - -static -ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env, - ESockDescriptor* descP, - struct ifconf* ifcP) -{ - ERL_NIF_TERM result; - unsigned int len = ((ifcP == NULL) ? 0 : - (ifcP->ifc_len / sizeof(struct ifreq))); - - SSDBG( descP, - ("SOCKET", "encode_ioctl_ifconf -> entry (when len = %d)\r\n", len) ); - - if (len > 0) { - ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); - unsigned int i = 0; - struct ifreq* p = ifcP->ifc_req; - - for (i = 0 ; i < len ; i++) { - SSDBG( descP, - ("SOCKET", "encode_ioctl_ifconf -> encode ifreq entry %d\r\n", i) ); - array[i] = encode_ioctl_ifconf_ifreq(env, descP, &p[i]); - } - - SSDBG( descP, - ("SOCKET", "encode_ioctl_ifconf -> all entries encoded\r\n", i) ); - - result = esock_make_ok2(env, MKLA(env, array, len)); - FREE(array); - - } else { - - result = esock_make_ok2(env, MKEL(env)); - - } - - return result; -} - - -#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) -static -ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env, - ESockDescriptor* descP, - struct ifmap* mapP) -{ - ERL_NIF_TERM mapKeys[] = {atom_mem_start, - atom_mem_end, - atom_base_addr, - atom_irq, - atom_dma, - atom_port}; - ERL_NIF_TERM mapVals[] = {MKUL(env, mapP->mem_start), - MKUL(env, mapP->mem_end), - MKUI(env, mapP->base_addr), - MKUI(env, mapP->irq), - MKUI(env, mapP->dma), - MKUI(env, mapP->port)}; - unsigned int numMapKeys = NUM(mapKeys); - unsigned int numMapVals = NUM(mapVals); - ERL_NIF_TERM emap; - - ESOCK_ASSERT( numMapVals == numMapKeys ); - ESOCK_ASSERT( MKMA(env, mapKeys, mapVals, numMapKeys, &emap) ); - - SSDBG( descP, ("SOCKET", "encode_ioctl_ifrmap -> done with" - "\r\n Map: %T" - "\r\n", emap) ); - - return esock_make_ok2(env, emap);; -} -#endif - - -#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) -static -ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env, - ESockDescriptor* descP, - struct sockaddr* addrP) -{ - ERL_NIF_TERM eaddr; - SOCKLEN_T sz = sizeof(struct sockaddr); - - esock_encode_hwsockaddr(env, addrP, sz, &eaddr); - - SSDBG( descP, ("SOCKET", "encode_ioctl_ifraddr -> done with" - "\r\n Sock Addr: %T" - "\r\n", eaddr) ); - - return esock_make_ok2(env, eaddr);; -} -#endif - - -static -ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env, - ESockDescriptor* descP, - struct sockaddr* addrP) -{ - ERL_NIF_TERM eaddr; - - esock_encode_sockaddr(env, (ESockAddress*) addrP, -1, &eaddr); - - SSDBG( descP, ("SOCKET", "encode_ioctl_ifraddr -> done with" - "\r\n Sock Addr: %T" - "\r\n", eaddr) ); - - return esock_make_ok2(env, eaddr);; -} - - -static -ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env, - ESockDescriptor* descP, - short flags) -{ - int i, flag, num = NUM(ioctl_flags); - ERL_NIF_TERM eflags, eflag; - SocketTArray ta = TARRAY_CREATE(20); // Just to be on the safe side - - if (flags == 0) { - eflags = MKEL(env); - } else { - for (i = 0; (i < num) && (flags != 0); i++) { - flag = ioctl_flags[i].flag; - if ((flag != 0) && ((flags & flag) == flag)) { - eflag = *(ioctl_flags[i].name); - flags &= ~flag; - - SSDBG( descP, ("SOCKET", "encode_ioctl_flags {%d} -> " - "\r\n i: %d" - "\r\n found flag: %T (%d)" - "\r\n remaining flags: %d" - "\r\n", descP->sock, i, eflag, flag, flags) ); - - TARRAY_ADD(ta, eflag); - } - } - if (flags != 0) { - - SSDBG( descP, ("SOCKET", "encode_ioctl_flags {%d} -> unknown flag(s): %d" - "\r\n", descP->sock, flags) ); - - TARRAY_ADD(ta, MKI(env, flags)); - } - - TARRAY_TOLIST(ta, env, &eflags); - } - - - SSDBG( descP, ("SOCKET", "encode_ioctl_flags -> done with" - "\r\n Flags: %T (%d)" - "\r\n", eflags, flags) ); - - return esock_make_ok2(env, eflags);; -} - - -static -BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM eaddr, - ESockAddress* addr) -{ - SOCKLEN_T addrLen; - BOOLEAN_T result; - - result = esock_decode_sockaddr(env, eaddr, (ESockAddress*) addr, &addrLen); - - VOID(addrLen); - - SSDBG( descP, - ("SOCKET", "esock_decode_ioctl_sockaddr {%d} -> decode result: %s" - "\r\n", descP->sock, B2S(result)) ); - - return result; -} - - -static -BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM emtu, - int* mtu) -{ - BOOLEAN_T result; - - if (! GET_INT(env, emtu, mtu)) { - result = FALSE; - } else { - result = TRUE; - } - - SSDBG( descP, - ("SOCKET", "esock_decode_ioctl_mtu {%d} -> decode result: %s" - "\r\n", descP->sock, B2S(result)) ); - - return result; -} - - -#if defined(SIOCSIFTXQLEN) -static -BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM etxqlen, - int* txqlen) -{ - return decode_ioctl_ivalue(env, descP, etxqlen, txqlen); -} -#endif - -/* All uses of the function should be added. For instance: - * #if defined(SIOCGIFTXQLEN) || defined(FOOBAR) || defined(YXA) - */ -#if defined(SIOCGIFTXQLEN) -static -BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM eivalue, - int* ivalue) -{ - BOOLEAN_T result; - - if (! GET_INT(env, eivalue, ivalue)) { - result = FALSE; - } else { - result = TRUE; - } - - SSDBG( descP, - ("SOCKET", "esock_decode_ioctl_ivalue {%d} -> decode result: %s" - "\r\n", descP->sock, B2S(result)) ); - - return result; } -#endif - - -static -BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM eflags, - short* flags) -{ - ERL_NIF_TERM key, value; - ErlNifMapIterator iter; - int tmpFlags = (int) *flags; // Current value - int flag; - - SSDBG( descP, - ("SOCKET", "decode_ioctl_flags {%d} -> entry with" - "\r\n flags: %d" - "\r\n", - descP->sock, tmpFlags) ); - - enif_map_iterator_create(env, eflags, &iter, ERL_NIF_MAP_ITERATOR_FIRST); - - while (enif_map_iterator_get_pair(env, &iter, &key, &value)) { - - /* Convert key (eflag) to int */ - if (! GET_INT(env, key, &flag)) { - enif_map_iterator_destroy(env, &iter); - return FALSE; - } - - // Update flag - if (COMPARE(value, esock_atom_true) == 0) { - SSDBG( descP, - ("SOCKET", "decode_ioctl_flags {%d} -> set %d\r\n", - descP->sock, flag) ); - tmpFlags |= flag; - } else { - SSDBG( descP, - ("SOCKET", "decode_ioctl_flags {%d} -> reset %d\r\n", - descP->sock, flag) ); - tmpFlags &= ~flag; - } - - enif_map_iterator_next(env, &iter); - } - - enif_map_iterator_destroy(env, &iter); - - SSDBG( descP, - ("SOCKET", "decode_ioctl_flags {%d} -> done with" - "\r\n (new) flags: %d" - "\r\n", - descP->sock, tmpFlags) ); - *flags = (short) tmpFlags; - - return TRUE; -} - - -static -ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env, - ESockDescriptor* descP, - int ivalue) -{ - ERL_NIF_TERM eivalue = MKI(env, ivalue); - - SSDBG( descP, ("SOCKET", "encode_ioctl_ivalue -> done with" - "\r\n iValue: %T (%d)" - "\r\n", eivalue, ivalue) ); - - return esock_make_ok2(env, eivalue);; -} - -static -ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env, - ESockDescriptor* descP, - struct ifreq* ifrP) -{ - ERL_NIF_TERM ename, eaddr; - - ESOCK_ASSERT( ifrP != NULL ); - - SSDBG( descP, ("SOCKET", "encode_ioctl_ifconf_ifreq -> encode name\r\n") ); - ename = encode_ioctl_ifreq_name(env, ifrP->ifr_name); - - SSDBG( descP, ("SOCKET", "encode_ioctl_ifconf_ifreq -> encode sockaddr\r\n") ); - eaddr = encode_ioctl_ifreq_sockaddr(env, &ifrP->ifr_addr); - - SSDBG( descP, ("SOCKET", "encode_ioctl_ifconf_ifreq -> make ifreq map with" - "\r\n Name: %T" - "\r\n Sock Addr: %T" - "\r\n", ename, eaddr) ); - return make_ifreq(env, ename, esock_atom_addr, eaddr); -} - -static -ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env, - char* name) -{ - return ((name == NULL) ? esock_atom_undefined : MKS(env, name)); -} - -static -ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env, struct sockaddr* sa) -{ - ERL_NIF_TERM esa; - - if (sa != NULL) { - - esock_encode_sockaddr(env, (ESockAddress*) sa, -1, &esa); - - } else { - - esa = esock_atom_undefined; - - } - - return esa; -} - - -/* The ifreq structure *always* contain a name - * and *one* other element. The second element - * depend on the ioctl request. - */ -static -ERL_NIF_TERM make_ifreq(ErlNifEnv* env, - ERL_NIF_TERM name, - ERL_NIF_TERM key2, - ERL_NIF_TERM val2) -{ - ERL_NIF_TERM keys[2]; - ERL_NIF_TERM vals[2]; - ERL_NIF_TERM res; - - keys[0] = esock_atom_name; - vals[0] = name; - - keys[1] = key2; - vals[1] = val2; - - ESOCK_ASSERT( MKMA(env, keys, vals, NUM(keys), &res) ); - - return res; -} - -#endif // #ifndef __WIN32__ @@ -11261,7 +9938,8 @@ ERL_NIF_TERM make_ifreq(ErlNifEnv* env, * * Arguments: * Socket (ref) - Points to the socket descriptor. - * Operation (atom) - What kind of operation (accept, send, ...) is to be cancelled + * Operation (atom) - What kind of operation (accept, send, ...) + * is to be cancelled * Ref (ref) - Unique id for the operation */ static @@ -11269,9 +9947,6 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { -#ifdef __WIN32__ - return enif_raise_exception(env, MKA(env, "notsup")); -#else ESockDescriptor* descP; ERL_NIF_TERM op, sockRef, opRef; @@ -11294,11 +9969,9 @@ ERL_NIF_TERM nif_cancel(ErlNifEnv* env, return esock_cancel(env, descP, op, sockRef, opRef); -#endif // #ifdef __WIN32__ #else } -#ifndef __WIN32__ static ERL_NIF_TERM esock_cancel(ErlNifEnv* env, ESockDescriptor* descP, @@ -11306,7 +9979,8 @@ ERL_NIF_TERM esock_cancel(ErlNifEnv* env, ERL_NIF_TERM sockRef, ERL_NIF_TERM opRef) { - int cmp; + ERL_NIF_TERM result; + int cmp; /* <KOLLA> * @@ -11318,36 +9992,71 @@ ERL_NIF_TERM esock_cancel(ErlNifEnv* env, */ /* Hand crafted binary search */ - if ((cmp = COMPARE(op, esock_atom_recvmsg)) == 0) - return esock_cancel_recv(env, descP, sockRef, opRef); + if ((cmp = COMPARE(op, esock_atom_recvmsg)) == 0) { + MLOCK(descP->readMtx); + result = ESOCK_IO_CANCEL_RECV(env, descP, sockRef, opRef); + MUNLOCK(descP->readMtx); + return result; + } if (cmp < 0) { - if ((cmp = COMPARE(op, esock_atom_recv)) == 0) - return esock_cancel_recv(env, descP, sockRef, opRef); + if ((cmp = COMPARE(op, esock_atom_recv)) == 0) { + MLOCK(descP->readMtx); + result = ESOCK_IO_CANCEL_RECV(env, descP, sockRef, opRef); + MUNLOCK(descP->readMtx); + return result; + } if (cmp < 0) { - if (COMPARE(op, esock_atom_connect) == 0) - return esock_cancel_connect(env, descP, opRef); - if (COMPARE(op, esock_atom_accept) == 0) - return esock_cancel_accept(env, descP, sockRef, opRef); + if (COMPARE(op, esock_atom_connect) == 0) { + MLOCK(descP->writeMtx); + result = ESOCK_IO_CANCEL_CONNECT(env, descP, opRef); + MUNLOCK(descP->writeMtx); + return result; + } + if (COMPARE(op, esock_atom_accept) == 0) { + MLOCK(descP->readMtx); + result = ESOCK_IO_CANCEL_ACCEPT(env, descP, sockRef, opRef); + MUNLOCK(descP->readMtx); + return result; + } } else { - if (COMPARE(op, esock_atom_recvfrom) == 0) - return esock_cancel_recv(env, descP, sockRef, opRef); + if (COMPARE(op, esock_atom_recvfrom) == 0) { + MLOCK(descP->readMtx); + result = ESOCK_IO_CANCEL_RECV(env, descP, sockRef, opRef); + MUNLOCK(descP->readMtx); + return result; + } } } else { - if ((cmp = COMPARE(op, esock_atom_sendmsg)) == 0) - return esock_cancel_send(env, descP, sockRef, opRef); + if ((cmp = COMPARE(op, esock_atom_sendmsg)) == 0) { + MLOCK(descP->writeMtx); + result = ESOCK_IO_CANCEL_SEND(env, descP, sockRef, opRef); + MUNLOCK(descP->writeMtx); + return result; + } if (cmp < 0) { - if (COMPARE(op, esock_atom_send) == 0) - return esock_cancel_send(env, descP, sockRef, opRef); - if (COMPARE(op, esock_atom_sendfile) == 0) - return esock_cancel_send(env, descP, sockRef, opRef); + if (COMPARE(op, esock_atom_send) == 0) { + MLOCK(descP->writeMtx); + result = ESOCK_IO_CANCEL_SEND(env, descP, sockRef, opRef); + MUNLOCK(descP->writeMtx); + return result; + } + if (COMPARE(op, esock_atom_sendfile) == 0) { + MLOCK(descP->writeMtx); + result = ESOCK_IO_CANCEL_SEND(env, descP, sockRef, opRef); + MUNLOCK(descP->writeMtx); + return result; + } } else { - if (COMPARE(op, esock_atom_sendto) == 0) - return esock_cancel_send(env, descP, sockRef, opRef); + if (COMPARE(op, esock_atom_sendto) == 0) { + MLOCK(descP->writeMtx); + result = ESOCK_IO_CANCEL_SEND(env, descP, sockRef, opRef); + MUNLOCK(descP->writeMtx); + return result; + } } } { - ERL_NIF_TERM result; const char *reason; MLOCK(descP->readMtx); @@ -11373,430 +10082,10 @@ ERL_NIF_TERM esock_cancel(ErlNifEnv* env, return result; } } -#endif // #ifndef __WIN32__ - - - -/* *** esock_cancel_connect *** - * - * - */ -#ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_connect(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef) -{ - ERL_NIF_TERM res; - ErlNifPid self; - - ESOCK_ASSERT( enif_self(env, &self) != NULL ); - - MLOCK(descP->writeMtx); - - if (! IS_OPEN(descP->writeState)) { - - res = esock_make_error_closed(env); - - } else if ((descP->connectorP == NULL) || - (COMPARE_PIDS(&self, &descP->connector.pid) != 0) || - (COMPARE(opRef, descP->connector.ref) != 0)) { - - res = esock_atom_not_found; - - } else { - - res = esock_cancel_write_select(env, descP, opRef); - esock_requestor_release("esock_cancel_connect", - env, descP, &descP->connector); - descP->connectorP = NULL; - descP->writeState &= ~ESOCK_STATE_CONNECTING; - } - - SSDBG( descP, - ("SOCKET", - "esock_cancel_connect {%d,0x%X} ->" - "\r\n opRef: %T" - "\r\n res: %T" - "\r\n", - descP->sock, descP->writeState, - opRef, res) ); - - MUNLOCK(descP->writeMtx); - - return res; -} -#endif // #ifndef __WIN32__ - - -/* *** esock_cancel_accept *** - * - * We have two different cases: - * *) Its the current acceptor - * Cancel the select! - * We need to activate one of the waiting acceptors. - * *) Its one of the acceptors ("waiting") in the queue - * Simply remove the acceptor from the queue. - * - */ -#ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_accept(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef) -{ - ERL_NIF_TERM res; - - MLOCK(descP->readMtx); - - SSDBG( descP, - ("SOCKET", - "esock_cancel_accept(%T), {%d,0x%X} ->" - "\r\n opRef: %T" - "\r\n %s" - "\r\n", - sockRef, descP->sock, descP->readState, - opRef, - ((descP->currentAcceptorP == NULL) - ? "without acceptor" : "with acceptor")) ); - - if (! IS_OPEN(descP->readState)) { - - res = esock_make_error_closed(env); - - } else if (descP->currentAcceptorP == NULL) { - - res = esock_atom_not_found; - - } else { - ErlNifPid self; - - ESOCK_ASSERT( enif_self(env, &self) != NULL ); - - if (COMPARE_PIDS(&self, &descP->currentAcceptor.pid) == 0) { - if (COMPARE(opRef, descP->currentAcceptor.ref) == 0) - res = esock_cancel_accept_current(env, descP, sockRef); - else - res = esock_atom_not_found; - } else { - res = esock_cancel_accept_waiting(env, descP, opRef, &self); - } - } - - SSDBG( descP, - ("SOCKET", "esock_cancel_accept(%T) -> done with result:" - "\r\n %T" - "\r\n", sockRef, res) ); - - MUNLOCK(descP->readMtx); - - return res; -} -#endif // #ifndef __WIN32__ -/* The current acceptor process has an ongoing select we first must - * cancel. Then we must re-activate the "first" (the first - * in the acceptor queue). - */ #ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_accept_current(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef) -{ - ERL_NIF_TERM res; - - ESOCK_ASSERT( DEMONP("esock_cancel_accept_current -> current acceptor", - env, descP, &descP->currentAcceptor.mon) == 0); - MON_INIT(&descP->currentAcceptor.mon); - res = esock_cancel_read_select(env, descP, descP->currentAcceptor.ref); - - SSDBG( descP, - ("SOCKET", - "esock_cancel_accept_current(%T) {%d} -> cancel res: %T" - "\r\n", sockRef, descP->sock, res) ); - - if (!esock_activate_next_acceptor(env, descP, sockRef)) { - - SSDBG( descP, - ("SOCKET", - "esock_cancel_accept_current(%T) {%d} -> " - "no more acceptors\r\n", - sockRef, descP->sock) ); - - descP->readState &= ~ESOCK_STATE_ACCEPTING; - - descP->currentAcceptorP = NULL; - } - - return res; -} -#endif // #ifndef __WIN32__ - - -/* These processes have not performed a select, so we can simply - * remove them from the acceptor queue. - */ -#ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_accept_waiting(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef, - const ErlNifPid* selfP) -{ - /* unqueue request from (acceptor) queue */ - - if (esock_acceptor_unqueue(env, descP, &opRef, selfP)) { - return esock_atom_ok; - } else { - return esock_atom_not_found; - } -} -#endif // #ifndef __WIN32__ - - - -/* *** esock_cancel_send *** - * - * Cancel a send operation. - * Its either the current writer or one of the waiting writers. - */ -#ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_send(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef) -{ - ERL_NIF_TERM res; - - MLOCK(descP->writeMtx); - - SSDBG( descP, - ("SOCKET", - "esock_cancel_send(%T), {%d,0x%X} -> entry with" - "\r\n opRef: %T" - "\r\n %s" - "\r\n", - sockRef, descP->sock, descP->writeState, - opRef, - ((descP->currentWriterP == NULL) - ? "without writer" : "with writer")) ); - - if (! IS_OPEN(descP->writeState)) { - - res = esock_make_error_closed(env); - - } else if (descP->currentWriterP == NULL) { - - res = esock_atom_not_found; - - } else { - ErlNifPid self; - - ESOCK_ASSERT( enif_self(env, &self) != NULL ); - - if (COMPARE_PIDS(&self, &descP->currentWriter.pid) == 0) { - if (COMPARE(opRef, descP->currentWriter.ref) == 0) - res = esock_cancel_send_current(env, descP, sockRef); - else - res = esock_atom_not_found; - } else { - res = esock_cancel_send_waiting(env, descP, opRef, &self); - } - } - - SSDBG( descP, - ("SOCKET", "esock_cancel_send(%T) {%d} -> done with result:" - "\r\n %T" - "\r\n", sockRef, descP->sock, res) ); - - MUNLOCK(descP->writeMtx); - - return res; -} -#endif // #ifndef __WIN32__ - - - -/* The current writer process has an ongoing select we first must - * cancel. Then we must re-activate the "first" (the first - * in the writer queue). - */ -#ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_send_current(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef) -{ - ERL_NIF_TERM res; - - ESOCK_ASSERT( DEMONP("esock_cancel_send_current -> current writer", - env, descP, &descP->currentWriter.mon) == 0); - res = esock_cancel_write_select(env, descP, descP->currentWriter.ref); - - SSDBG( descP, - ("SOCKET", "esock_cancel_send_current(%T) {%d} -> cancel res: %T" - "\r\n", sockRef, descP->sock, res) ); - - if (!esock_activate_next_writer(env, descP, sockRef)) { - SSDBG( descP, - ("SOCKET", - "esock_cancel_send_current(%T) {%d} -> no more writers" - "\r\n", sockRef, descP->sock) ); - - descP->currentWriterP = NULL; - } - - return res; -} -#endif // #ifndef __WIN32__ - - -/* These processes have not performed a select, so we can simply - * remove them from the writer queue. - */ -#ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_send_waiting(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef, - const ErlNifPid* selfP) -{ - /* unqueue request from (writer) queue */ - - if (esock_writer_unqueue(env, descP, &opRef, selfP)) { - return esock_atom_ok; - } else { - return esock_atom_not_found; - } -} -#endif // #ifndef __WIN32__ - - - -/* *** esock_cancel_recv *** - * - * Cancel a read operation. - * Its either the current reader or one of the waiting readers. - */ -#ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_recv(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM opRef) -{ - ERL_NIF_TERM res; - - MLOCK(descP->readMtx); - - SSDBG( descP, - ("SOCKET", - "esock_cancel_recv(%T), {%d,0x%X} -> entry with" - "\r\n opRef: %T" - "\r\n %s" - "\r\n", - sockRef, descP->sock, descP->readState, - opRef, - ((descP->currentReaderP == NULL) - ? "without reader" : "with reader")) ); - - if (! IS_OPEN(descP->readState)) { - - res = esock_make_error_closed(env); - - } else if (descP->currentReaderP == NULL) { - - res = esock_atom_not_found; - - } else { - ErlNifPid self; - - ESOCK_ASSERT( enif_self(env, &self) != NULL ); - - if (COMPARE_PIDS(&self, &descP->currentReader.pid) == 0) { - if (COMPARE(opRef, descP->currentReader.ref) == 0) - res = esock_cancel_recv_current(env, descP, sockRef); - else - res = esock_atom_not_found; - } else { - res = esock_cancel_recv_waiting(env, descP, opRef, &self); - } - } - - SSDBG( descP, - ("SOCKET", "esock_cancel_recv(%T) {%d} -> done with result:" - "\r\n %T" - "\r\n", sockRef, descP->sock, res) ); - - MUNLOCK(descP->readMtx); - - return res; -} -#endif // #ifndef __WIN32__ - - -/* The current reader process has an ongoing select we first must - * cancel. Then we must re-activate the "first" (the first - * in the reader queue). - */ -#ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_recv_current(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef) -{ - ERL_NIF_TERM res; - - ESOCK_ASSERT( DEMONP("esock_cancel_recv_current -> current reader", - env, descP, &descP->currentReader.mon) == 0); - res = esock_cancel_read_select(env, descP, descP->currentReader.ref); - - SSDBG( descP, - ("SOCKET", "esock_cancel_recv_current(%T) {%d} -> cancel res: %T" - "\r\n", sockRef, descP->sock, res) ); - - if (!esock_activate_next_reader(env, descP, sockRef)) { - SSDBG( descP, - ("SOCKET", - "esock_cancel_recv_current(%T) {%d} -> no more readers" - "\r\n", sockRef, descP->sock) ); - - descP->currentReaderP = NULL; - } - - return res; -} -#endif // #ifndef __WIN32__ - - -/* These processes have not performed a select, so we can simply - * remove them from the reader queue. - */ -#ifndef __WIN32__ -static -ERL_NIF_TERM esock_cancel_recv_waiting(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef, - const ErlNifPid* selfP) -{ - /* unqueue request from (reader) queue */ - - if (esock_reader_unqueue(env, descP, &opRef, selfP)) { - return esock_atom_ok; - } else { - return esock_atom_not_found; - } -} -#endif // #ifndef __WIN32__ - - - -#ifndef __WIN32__ -static +extern ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM opRef) @@ -11809,10 +10098,10 @@ ERL_NIF_TERM esock_cancel_read_select(ErlNifEnv* env, #ifndef __WIN32__ -static +extern ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM opRef) + ESockDescriptor* descP, + ERL_NIF_TERM opRef) { return esock_cancel_mode_select(env, descP, opRef, ERL_NIF_SELECT_WRITE, @@ -11822,7 +10111,7 @@ ERL_NIF_TERM esock_cancel_write_select(ErlNifEnv* env, #ifndef __WIN32__ -static +extern ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM opRef, @@ -11840,7 +10129,7 @@ ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env, return esock_atom_ok; } else { /* Has already sent the message */ - return esock_atom_select_sent; + return esock_make_error(env, esock_atom_select_sent); } } else { /* Stopped? */ @@ -11849,7 +10138,7 @@ ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env, "esock_cancel_mode_select {%d} -> failed: %d (0x%lX)" "\r\n", descP->sock, selectRes, selectRes) ); - return esock_atom_not_found; + return esock_make_error(env, esock_atom_not_found); } } #endif // #ifndef __WIN32__ @@ -11861,7 +10150,6 @@ ERL_NIF_TERM esock_cancel_mode_select(ErlNifEnv* env, * ---------------------------------------------------------------------- */ -#ifndef __WIN32__ extern BOOLEAN_T esock_encode_cmsg(ErlNifEnv* env, int level, @@ -11899,11 +10187,10 @@ BOOLEAN_T esock_encode_cmsg(ErlNifEnv* env, * just return the type number as an erlang integer */ - *eType = MKI(env, type); + return FALSE; } -#endif /* +++ esock_encode_msg_flags +++ @@ -11912,7 +10199,6 @@ BOOLEAN_T esock_encode_cmsg(ErlNifEnv* env, * */ -#ifndef __WIN32__ extern void esock_encode_msg_flags(ErlNifEnv* env, ESockDescriptor* descP, @@ -11930,11 +10216,11 @@ void esock_encode_msg_flags(ErlNifEnv* env, size_t n; SocketTArray ta = TARRAY_CREATE(10); // Just to be on the safe side - for (n = 0; n < NUM(msg_flags); n++) { - int f = msg_flags[n].flag; + for (n = 0; n < esock_msg_flags_length; n++) { + int f = esock_msg_flags[n].flag; if ((f != 0) && ((msgFlags & f) == f)) { msgFlags &= ~f; - TARRAY_ADD(ta, *(msg_flags[n].name)); + TARRAY_ADD(ta, *(esock_msg_flags[n].name)); } if (msgFlags == 0) goto done; } @@ -11951,10 +10237,8 @@ void esock_encode_msg_flags(ErlNifEnv* env, TARRAY_TOLIST(ta, env, flags); } } -#endif -#ifndef __WIN32__ #ifdef SCM_TIMESTAMP static BOOLEAN_T esock_cmsg_encode_timeval(ErlNifEnv *env, @@ -11988,10 +10272,8 @@ static BOOLEAN_T esock_cmsg_decode_timeval(ErlNifEnv *env, return TRUE; } #endif -#endif -#ifndef __WIN32__ #if defined(IP_TOS) || defined(IP_RECVTOS) static BOOLEAN_T esock_cmsg_encode_ip_tos(ErlNifEnv *env, @@ -12028,9 +10310,8 @@ static BOOLEAN_T esock_cmsg_decode_ip_tos(ErlNifEnv *env, return TRUE; } #endif // #ifdef IP_TOS -#endif // #ifdef __WIN32__ -#ifndef __WIN32__ + #if defined(IP_TTL) || \ defined(IPV6_HOPLIMIT) || \ defined(IPV6_TCLASS) || defined(IPV6_RECVTCLASS) @@ -12061,18 +10342,16 @@ BOOLEAN_T esock_cmsg_decode_int(ErlNifEnv* env, if (! GET_INT(env, eValue, &value)) return FALSE; - if ((valueP = esock_init_cmsghdr(cmsgP, rem, - sizeof(*valueP), usedP)) == NULL) + valueP = esock_init_cmsghdr(cmsgP, rem, sizeof(*valueP), usedP); + if (valueP == NULL) return FALSE; *valueP = value; return TRUE; } #endif -#endif -#ifndef __WIN32__ extern BOOLEAN_T esock_cmsg_decode_bool(ErlNifEnv* env, ERL_NIF_TERM eValue, @@ -12093,10 +10372,8 @@ BOOLEAN_T esock_cmsg_decode_bool(ErlNifEnv* env, *valueP = v? 1 : 0; return TRUE; } -#endif -#ifndef __WIN32__ #ifdef IP_RECVTTL static BOOLEAN_T esock_cmsg_encode_uchar(ErlNifEnv *env, @@ -12113,15 +10390,15 @@ BOOLEAN_T esock_cmsg_encode_uchar(ErlNifEnv *env, return TRUE; } #endif -#endif -#ifndef __WIN32__ + #ifdef IP_PKTINFO static BOOLEAN_T esock_cmsg_encode_in_pktinfo(ErlNifEnv *env, unsigned char *data, size_t dataLen, - ERL_NIF_TERM *eResult) { + ERL_NIF_TERM *eResult) +{ struct in_pktinfo* pktInfoP = (struct in_pktinfo*) data; ERL_NIF_TERM ifIndex; ERL_NIF_TERM specDst, addr; @@ -12130,14 +10407,23 @@ BOOLEAN_T esock_cmsg_encode_in_pktinfo(ErlNifEnv *env, return FALSE; ifIndex = MKUI(env, pktInfoP->ipi_ifindex); +#ifndef __WIN32__ + /* On Windows, the field ipi_spec_dst does not exist */ esock_encode_in_addr(env, &pktInfoP->ipi_spec_dst, &specDst); +#endif esock_encode_in_addr(env, &pktInfoP->ipi_addr, &addr); { ERL_NIF_TERM keys[] = {esock_atom_ifindex, esock_atom_spec_dst, esock_atom_addr}; - ERL_NIF_TERM vals[] = {ifIndex, specDst, addr}; + ERL_NIF_TERM vals[] = {ifIndex, +#ifndef __WIN32__ + specDst, +#else + esock_atom_undefined, +#endif + addr}; unsigned int numKeys = NUM(keys); unsigned int numVals = NUM(vals); @@ -12147,7 +10433,7 @@ BOOLEAN_T esock_cmsg_encode_in_pktinfo(ErlNifEnv *env, return TRUE; } #endif -#endif + #ifndef __WIN32__ #ifdef IP_ORIGDSTADDR @@ -12170,6 +10456,7 @@ BOOLEAN_T esock_cmsg_encode_sockaddr(ErlNifEnv *env, #endif #endif + #ifndef __WIN32__ #ifdef HAVE_LINUX_ERRQUEUE_H #if defined(IP_RECVERR) || defined(IPV6_RECVERR) @@ -12433,7 +10720,6 @@ BOOLEAN_T esock_cmsg_encode_recverr(ErlNifEnv *env, #endif // #ifdef HAVE_LINUX_ERRQUEUE_H #endif // #ifndef __WIN32__ -#ifndef __WIN32__ #ifdef IPV6_PKTINFO static BOOLEAN_T esock_cmsg_encode_in6_pktinfo(ErlNifEnv *env, @@ -12459,12 +10745,9 @@ BOOLEAN_T esock_cmsg_encode_in6_pktinfo(ErlNifEnv *env, return TRUE; } #endif -#endif -#ifndef __WIN32__ - static int cmpESockCmsgSpec(const void *vpa, const void *vpb) { ESockCmsgSpec *a, *b; a = (ESockCmsgSpec *) vpa; @@ -12472,8 +10755,14 @@ static int cmpESockCmsgSpec(const void *vpa, const void *vpb) { return COMPARE(*(a->nameP), *(b->nameP)); } -static ESockCmsgSpec - cmsgLevelSocket[] = + +#if defined(SCM_CREDENTIALS) || defined(SCM_RIGHTS) || defined(SCM_TIMESTAMP) +#define HAVE_ESOCK_CMSG_SOCKET +#endif + + +#if defined(HAVE_ESOCK_CMSG_SOCKET) +static ESockCmsgSpec cmsgLevelSocket[] = { #if defined(SCM_CREDENTIALS) {SCM_CREDENTIALS, NULL, NULL, @@ -12493,9 +10782,10 @@ static ESockCmsgSpec &esock_cmsg_encode_timeval, esock_cmsg_decode_timeval, &esock_atom_timestamp}, #endif - }, + }; +#endif - cmsgLevelIP[] = +static ESockCmsgSpec cmsgLevelIP[] = { #if defined(IP_TOS) {IP_TOS, esock_cmsg_encode_ip_tos, esock_cmsg_decode_ip_tos, @@ -12575,11 +10865,16 @@ static ESockCmsgSpec cmsgLevelIPv6[] = }; #endif // #ifdef HAVE_IPV6 -static void initCmsgTables(void) { +static void initCmsgTables(void) +{ +#if defined(HAVE_ESOCK_CMSG_SOCKET) ESOCK_SORT_TABLE(cmsgLevelSocket, cmpESockCmsgSpec); - ESOCK_SORT_TABLE(cmsgLevelIP, cmpESockCmsgSpec); +#endif + + ESOCK_SORT_TABLE(cmsgLevelIP, cmpESockCmsgSpec); + #ifdef HAVE_IPV6 - ESOCK_SORT_TABLE(cmsgLevelIPv6, cmpESockCmsgSpec); + ESOCK_SORT_TABLE(cmsgLevelIPv6, cmpESockCmsgSpec); #endif } @@ -12588,24 +10883,34 @@ ESockCmsgSpec* esock_lookup_cmsg_table(int level, size_t *num) { switch (level) { +#if defined(HAVE_ESOCK_CMSG_SOCKET) case SOL_SOCKET: *num = NUM(cmsgLevelSocket); return cmsgLevelSocket; +#endif +#ifndef __WIN32__ #ifdef SOL_IP case SOL_IP: #else case IPPROTO_IP: #endif +#else + case IPPROTO_IP: +#endif *num = NUM(cmsgLevelIP); return cmsgLevelIP; #ifdef HAVE_IPV6 +#ifndef __WIN32__ #ifdef SOL_IPV6 case SOL_IPV6: #else case IPPROTO_IPV6: #endif +#else + case IPPROTO_IPV6: +#endif *num = NUM(cmsgLevelIPv6); return cmsgLevelIPv6; #endif @@ -12627,32 +10932,32 @@ ESockCmsgSpec* esock_lookup_cmsg_spec(ESockCmsgSpec* table, return bsearch(&key, table, num, sizeof(*table), cmpESockCmsgSpec); } -#endif // #ifdef __WIN32__ /* Clear the CMSG space and init the ->cmsg_len member, * return the position for the data, and the total used space */ -#ifndef __WIN32__ extern void* esock_init_cmsghdr(struct cmsghdr* cmsgP, size_t rem, // Remaining space size_t size, // Size of data size_t* usedP) { - size_t space = CMSG_SPACE(size); + size_t space = ESOCK_CMSG_SPACE(size); + void* dataP; if (rem < space) return NULL; // Not enough space sys_memzero(cmsgP, space); - cmsgP->cmsg_len = CMSG_LEN(size); + cmsgP->cmsg_len = ESOCK_CMSG_LEN(size); *usedP = space; - return CMSG_DATA(cmsgP); + dataP = ESOCK_CMSG_DATA(cmsgP); + + return dataP; } -#endif /* +++ decode the ip socket option TOS +++ @@ -12664,8 +10969,10 @@ void* esock_init_cmsghdr(struct cmsghdr* cmsgP, * * lowdelay | throughput | reliability | mincost * + * + * For Windows, the Microsoft recommendation is: *Do not use* */ -#ifndef __WIN32__ + #if defined(IP_TOS) static BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) @@ -12674,6 +10981,14 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) if (IS_ATOM(env, eVal)) { +#ifdef __WIN32__ + + /* See above */ + *val = -1; + result = FALSE; + +#else + if (COMPARE(eVal, esock_atom_lowdelay) == 0) { *val = IPTOS_LOWDELAY; result = TRUE; @@ -12688,11 +11003,14 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) *val = IPTOS_MINCOST; result = TRUE; #endif + } else { *val = -1; result = FALSE; } +#endif // ifdef __WIN32__ + } else if (IS_NUM(env, eVal)) { if (GET_INT(env, eVal, val)) { @@ -12710,7 +11028,7 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) return result; } #endif -#endif // #ifndef __WIN32__ + /* +++ decode the ip socket option MTU_DISCOVER +++ @@ -12722,20 +11040,23 @@ BOOLEAN_T decode_ip_tos(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) * * want | dont | do | probe * + * Note that on Windows, the 'want' value seems to not exist! */ -#ifndef __WIN32__ + #if defined(IP_MTU_DISCOVER) static BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) { if (IS_ATOM(env, eVal)) { - if (COMPARE(eVal, atom_want) == 0) { - *val = IP_PMTUDISC_WANT; - } else if (COMPARE(eVal, atom_dont) == 0) { + if (COMPARE(eVal, atom_dont) == 0) { *val = IP_PMTUDISC_DONT; } else if (COMPARE(eVal, atom_do) == 0) { *val = IP_PMTUDISC_DO; +#if defined(IP_PMTUDISC_WANT) + } else if (COMPARE(eVal, atom_want) == 0) { + *val = IP_PMTUDISC_WANT; +#endif #if defined(IP_PMTUDISC_PROBE) } else if (COMPARE(eVal, atom_probe) == 0) { *val = IP_PMTUDISC_PROBE; @@ -12751,10 +11072,8 @@ BOOLEAN_T decode_ip_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) return TRUE; } #endif -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ #if defined(IPV6_MULTICAST_HOPS) || defined(IPV6_UNICAST_HOPS) static BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val) { @@ -12774,7 +11093,6 @@ BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val) { return TRUE; } #endif -#endif // #ifndef __WIN32__ /* +++ decode the ipv6 socket option MTU_DISCOVER +++ @@ -12786,20 +11104,33 @@ BOOLEAN_T decode_hops(ErlNifEnv *env, ERL_NIF_TERM eVal, int *val) { * * want | dont | do | probe * + * Use same as IP on Windows!! */ -#ifndef __WIN32__ + #if defined(IPV6_MTU_DISCOVER) static BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) { if (IS_ATOM(env, eVal)) { - if (COMPARE(eVal, atom_want) == 0) { - *val = IPV6_PMTUDISC_WANT; - } else if (COMPARE(eVal, atom_dont) == 0) { +#ifdef __WIN32__ + /* On Windows, the IP-flags are used */ + if (COMPARE(eVal, atom_dont) == 0) { + *val = IP_PMTUDISC_DONT; + } else if (COMPARE(eVal, atom_do) == 0) { + *val = IP_PMTUDISC_DO; + } else if (COMPARE(eVal, atom_probe) == 0) { + *val = IP_PMTUDISC_PROBE; + } else { + return FALSE; + } +#else + if (COMPARE(eVal, atom_dont) == 0) { *val = IPV6_PMTUDISC_DONT; } else if (COMPARE(eVal, atom_do) == 0) { *val = IPV6_PMTUDISC_DO; + } else if (COMPARE(eVal, atom_want) == 0) { + *val = IPV6_PMTUDISC_WANT; #if defined(IPV6_PMTUDISC_PROBE) } else if (COMPARE(eVal, atom_probe) == 0) { *val = IPV6_PMTUDISC_PROBE; @@ -12807,6 +11138,7 @@ BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) } else { return FALSE; } +#endif } else if (! GET_INT(env, eVal, val)) { return FALSE; @@ -12815,7 +11147,6 @@ BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) return TRUE; } #endif -#endif // #ifndef __WIN32__ /* +++ encode the ip socket option MTU_DISCOVER +++ @@ -12828,15 +11159,17 @@ BOOLEAN_T decode_ipv6_pmtudisc(ErlNifEnv* env, ERL_NIF_TERM eVal, int* val) * want | dont | do | probe * */ -#ifndef __WIN32__ + #if defined(IP_MTU_DISCOVER) static void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) { switch (val) { +#if defined(IP_PMTUDISC_WANT) case IP_PMTUDISC_WANT: *eVal = atom_want; break; +#endif case IP_PMTUDISC_DONT: *eVal = atom_dont; @@ -12860,7 +11193,6 @@ void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) return; } #endif -#endif // #ifndef __WIN32__ /* +++ encode the ipv6 socket option MTU_DISCOVER +++ @@ -12872,30 +11204,47 @@ void encode_ip_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) * * want | dont | do | probe * + * Windows uses the IP-flags. */ -#ifndef __WIN32__ + #if defined(IPV6_MTU_DISCOVER) static void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) { switch (val) { +#if defined(IPV6_PMTUDISC_WANT) case IPV6_PMTUDISC_WANT: *eVal = atom_want; break; +#endif +#if defined(__WIN32__) + case IP_PMTUDISC_DONT: +#else case IPV6_PMTUDISC_DONT: +#endif *eVal = atom_dont; break; +#if defined(__WIN32__) + case IP_PMTUDISC_DO: +#else case IPV6_PMTUDISC_DO: +#endif *eVal = atom_do; break; +#if defined(__WIN32__) + case IP_PMTUDISC_PROBE: + *eVal = atom_probe; + break; +#else #if defined(IPV6_PMTUDISC_PROBE) case IPV6_PMTUDISC_PROBE: *eVal = atom_probe; break; #endif +#endif default: *eVal = MKI(env, val); @@ -12905,7 +11254,7 @@ void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) return; } #endif -#endif // #ifndef __WIN32__ + /* +++ encode the ip socket option tos +++ @@ -12914,24 +11263,30 @@ void encode_ipv6_pmtudisc(ErlNifEnv* env, int val, ERL_NIF_TERM* eVal) * lowdelay | throughput | reliability | mincost | integer() * */ -#ifndef __WIN32__ + static ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val) { ERL_NIF_TERM result; switch (IPTOS_TOS(val)) { +#if defined(IPTOS_LOWDELAY) case IPTOS_LOWDELAY: result = esock_atom_lowdelay; break; +#endif +#if defined(IPTOS_THROUGHPUT) case IPTOS_THROUGHPUT: result = esock_atom_throughput; break; +#endif +#if defined(IPTOS_RELIABILITY) case IPTOS_RELIABILITY: result = esock_atom_reliability; break; +#endif #if defined(IPTOS_MINCOST) case IPTOS_MINCOST: @@ -12946,10 +11301,9 @@ ERL_NIF_TERM encode_ip_tos(ErlNifEnv* env, int val) return result; } -#endif // #ifndef __WIN32__ -#ifndef __WIN32__ + #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO) static @@ -12993,7 +11347,7 @@ ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env, sctp_assoc_t val) } #endif // #if defined(SCTP_ASSOCINFO) || defined(SCTP_RTOINOFO) -#endif // #ifdef __WIN32__ + /* *** esock_alloc_descriptor *** @@ -13001,12 +11355,11 @@ ERL_NIF_TERM encode_sctp_assoc_t(ErlNifEnv* env, sctp_assoc_t val) * Allocate and perform basic initialization of a socket descriptor. * */ -#ifndef __WIN32__ extern -ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event) +ESockDescriptor* esock_alloc_descriptor(SOCKET sock) { ESockDescriptor* descP; - char buf[64]; /* Buffer used for building the mutex name */ + char buf[64]; /* Buffer used for building the mutex name(s) */ ESOCK_ASSERT( (descP = enif_alloc_resource(esocks, sizeof(ESockDescriptor))) @@ -13017,11 +11370,14 @@ ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event) esock_requestor_init(&descP->connector); descP->connectorP = NULL; - sprintf(buf, "esock.w[%d]", sock); + sprintf(buf, "esock.w[" SOCKET_FORMAT_STR "]", sock); descP->writeMtx = MCREATE(buf); descP->writeState = 0; +#ifndef __WIN32__ + /* Not used on Windows - see header for more info */ esock_requestor_init(&descP->currentWriter); descP->currentWriterP = NULL; // currentWriter not used +#endif descP->writersQ.first = NULL; descP->writersQ.last = NULL; @@ -13038,11 +11394,14 @@ ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event) descP->sendfileCountersP = NULL; #endif - sprintf(buf, "esock.r[%d]", sock); + sprintf(buf, "esock.r[" SOCKET_FORMAT_STR "]", sock); descP->readMtx = MCREATE(buf); descP->readState = 0; +#ifndef __WIN32__ + /* Not used on Windows - see header for more info */ esock_requestor_init(&descP->currentReader); descP->currentReaderP = NULL; // currentReader not used +#endif descP->readersQ.first = NULL; descP->readersQ.last = NULL; @@ -13054,9 +11413,12 @@ ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event) descP->readWaits = 0; descP->readFails = 0; - sprintf(buf, "esock.acc[%d]", sock); + sprintf(buf, "esock.acc[" SOCKET_FORMAT_STR "]", sock); +#ifndef __WIN32__ + /* Not used on Windows - see header for more info */ esock_requestor_init(&descP->currentAcceptor); descP->currentAcceptorP = NULL; // currentAcceptor not used +#endif descP->acceptorsQ.first = NULL; descP->acceptorsQ.last = NULL; descP->accSuccess = 0; @@ -13064,36 +11426,72 @@ ESockDescriptor* esock_alloc_descriptor(SOCKET sock, ErlNifEvent event) descP->accTries = 0; descP->accWaits = 0; - sprintf(buf, "esock.close[%d]", sock); + sprintf(buf, "esock.close[" SOCKET_FORMAT_STR "]", sock); descP->closeEnv = NULL; descP->closeRef = esock_atom_undefined; enif_set_pid_undefined(&descP->closerPid); MON_INIT(&descP->closerMon); - sprintf(buf, "esock.cfg[%d]", sock); + sprintf(buf, "esock.cfg[" SOCKET_FORMAT_STR "]", sock); descP->rBufSz = ESOCK_RECV_BUFFER_SIZE_DEFAULT; +#ifndef __WIN32__ descP->rNum = ESOCK_RECV_BUFFER_COUNT_DEFAULT; descP->rNumCnt = 0; +#endif descP->rCtrlSz = ESOCK_RECV_CTRL_BUFFER_SIZE_DEFAULT; descP->wCtrlSz = ESOCK_SEND_CTRL_BUFFER_SIZE_DEFAULT; descP->iow = FALSE; - descP->dbg = ESOCK_DEBUG_DEFAULT; // Overwritten by caller - descP->useReg = ESOCK_USE_SOCKET_REGISTRY; // Overwritten by caller + descP->dbg = ESOCK_DEBUG_DEFAULT; // Overwritten by caller + descP->useReg = ESOCK_USE_SOCKET_REGISTRY;// Overwritten by caller descP->meta.env = esock_alloc_env("esock_alloc_descriptor - " "meta-env"); descP->meta.ref = esock_atom_undefined; descP->sock = sock; - descP->event = event; descP->origFD = INVALID_SOCKET; descP->closeOnClose = TRUE; enif_set_pid_undefined(&descP->ctrlPid); MON_INIT(&descP->ctrlMon); +#if defined(ESOCK_DESCRIPTOR_FILLER) + sys_memzero(descP->filler, sizeof(descP->filler)); +#endif + return descP; } -#endif // #ifndef __WIN32__ + + +/* This function is *only* called during 'open' after an + * descriptor has been allocated but before it has been used. + */ +extern +void esock_dealloc_descriptor(ErlNifEnv* env, + ESockDescriptor* descP) +{ + if (descP->writeMtx != NULL) { + MDESTROY(descP->writeMtx); + descP->writeMtx = NULL; + } + + if (descP->readMtx != NULL) { + MDESTROY(descP->readMtx); + descP->readMtx = NULL; + } + + if (descP->closeEnv != NULL) { + esock_free_env("dealloc descriptor", descP->closeEnv); + descP->closeEnv = NULL; + } + + if (descP->meta.env != NULL) { + esock_free_env("dealloc descriptor", descP->meta.env); + descP->meta.env = NULL; + } + +} + + /* Decrement counters for when a socket is closed @@ -13196,7 +11594,6 @@ void esock_inc_socket(int domain, int type, int protocol) /* ehow2how - convert internal (erlang) "shutdown how" to * (proper) "shutdown how" */ -#ifndef __WIN32__ static BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how) { @@ -13204,22 +11601,33 @@ BOOLEAN_T ehow2how(ERL_NIF_TERM ehow, int* how) cmp = COMPARE(ehow, atom_read_write); if (cmp == 0) +#ifdef __WIN32__ + *how = SD_BOTH; +#else *how = SHUT_RDWR; +#endif else if (cmp < 0) { if (COMPARE(ehow, atom_read) == 0) +#ifdef __WIN32__ + *how = SD_RECEIVE; +#else *how = SHUT_RD; +#endif else return FALSE; } else { if (COMPARE(ehow, atom_write) == 0) +#ifdef __WIN32__ + *how = SD_SEND; +#else *how = SHUT_WR; +#endif else return FALSE; } return TRUE; } -#endif // #ifndef __WIN32__ @@ -13254,7 +11662,6 @@ size_t my_strnlen(const char *s, size_t maxlen) * terminate otherwise, so there is no need to test if * the sending fails. */ -#ifndef __WIN32__ extern void esock_send_reg_add_msg(ErlNifEnv* env, ESockDescriptor* descP, @@ -13271,7 +11678,6 @@ void esock_send_reg_add_msg(ErlNifEnv* env, sockRef, descP->sock, MKPID(env, &data.regPid)) ); } } -#endif // #ifndef __WIN32__ @@ -13280,7 +11686,6 @@ void esock_send_reg_add_msg(ErlNifEnv* env, * terminate otherwise, so there is no need to test if * the sending fails. */ -#ifndef __WIN32__ extern void esock_send_reg_del_msg(ErlNifEnv* env, ESockDescriptor* descP, @@ -13297,7 +11702,6 @@ void esock_send_reg_del_msg(ErlNifEnv* env, sockRef, descP->sock, MKPID(env, &data.regPid)) ); } } -#endif // #ifndef __WIN32__ /* =========================================================================== @@ -13314,7 +11718,6 @@ void esock_send_reg_del_msg(ErlNifEnv* env, * * This message will only be sent if the iow (Inform On Wrap) is TRUE. */ -#ifndef __WIN32__ extern void esock_send_wrap_msg(ErlNifEnv* env, ESockDescriptor* descP, @@ -13333,7 +11736,6 @@ void esock_send_wrap_msg(ErlNifEnv* env, sockRef, descP->sock, MKPID(env, &descP->ctrlPid), cnt) ); } } -#endif // #ifndef __WIN32__ /* Send an close message to the specified process: @@ -13345,7 +11747,6 @@ void esock_send_wrap_msg(ErlNifEnv* env, * erlang API (close-) function for the socket to be "closed" * (actually that the 'stop' callback function has been called). */ -#ifndef __WIN32__ extern void esock_send_close_msg(ErlNifEnv* env, ESockDescriptor* descP, @@ -13367,10 +11768,12 @@ void esock_send_close_msg(ErlNifEnv* env, } } + + #ifdef HAVE_SENDFILE -static void -esock_send_sendfile_deferred_close_msg(ErlNifEnv* env, - ESockDescriptor* descP) +extern +void esock_send_sendfile_deferred_close_msg(ErlNifEnv* env, + ESockDescriptor* descP) { ERL_NIF_TERM sockRef, msg; ErlNifPid *pid; @@ -13388,7 +11791,6 @@ esock_send_sendfile_deferred_close_msg(ErlNifEnv* env, ESOCK_ASSERT( esock_send_msg(env, pid, msg, NULL) ); } #endif // #ifdef HAVE_SENDFILE -#endif // #ifndef __WIN32__ /* Send an abort message to the specified process: @@ -13399,7 +11801,6 @@ esock_send_sendfile_deferred_close_msg(ErlNifEnv* env, * This message is for processes that is waiting in the * erlang API functions for a select message. */ -#ifndef __WIN32__ extern void esock_send_abort_msg(ErlNifEnv* env, ESockDescriptor* descP, @@ -13413,10 +11814,11 @@ void esock_send_abort_msg(ErlNifEnv* env, { ERL_NIF_TERM msg; - msg = - mk_abort_msg(reqP->env, - /* sockRef not in env so copy */ - CP_TERM(reqP->env, sockRef), reqP->ref, reason); + msg = mk_abort_msg(reqP->env, + /* sockRef not in env so copy */ + CP_TERM(reqP->env, sockRef), + reqP->ref, + CP_TERM(reqP->env, reason)); if (! esock_send_msg(env, &reqP->pid, msg, reqP->env)) { SSDBG( descP, @@ -13428,13 +11830,38 @@ void esock_send_abort_msg(ErlNifEnv* env, } reqP->env = NULL; } -#endif // #ifndef __WIN32__ + + +/* Send an *simple* abort message to the specified process: + * A message in the form: + * + * {'$socket', Socket, abort, Info} + * + */ +extern +void esock_send_simple_abort_msg(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid* pid, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM reason) +{ + ERL_NIF_TERM msg = mk_simple_abort_msg(env, sockRef, reason); + + if (! esock_send_msg(env, pid, msg, NULL)) { + + SSDBG( descP, + ("SOCKET", + "esock_send_simple_abort_msg(%T) {%d} failed ->" + "\r\n pid: %T" + "\r\n", + sockRef, descP->sock, MKPID(env, pid)) ); + } +} /* Send a message to the specified process. */ -#ifndef __WIN32__ -static +extern BOOLEAN_T esock_send_msg(ErlNifEnv* env, ErlNifPid* pid, ERL_NIF_TERM msg, @@ -13445,7 +11872,6 @@ BOOLEAN_T esock_send_msg(ErlNifEnv* env, return !!res; } -#endif // #ifndef __WIN32__ @@ -13456,14 +11882,12 @@ BOOLEAN_T esock_send_msg(ErlNifEnv* env, * {'$socket', add, Socket} * */ -#ifndef __WIN32__ static ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef) { return mk_reg_msg(env, atom_add, sockRef); } -#endif // #ifndef __WIN32__ /* *** mk_reg_del_msg *** @@ -13473,14 +11897,12 @@ ERL_NIF_TERM mk_reg_add_msg(ErlNifEnv* env, * {'$socket', del, Socket} * */ -#ifndef __WIN32__ static ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef) { return mk_reg_msg(env, atom_del, sockRef); } -#endif // #ifndef __WIN32__ /* *** mk_reg_msg *** @@ -13491,17 +11913,31 @@ ERL_NIF_TERM mk_reg_del_msg(ErlNifEnv* env, * {'$socket', Tag, Socket} * */ -#ifndef __WIN32__ static ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env, ERL_NIF_TERM tag, ERL_NIF_TERM sockRef) { - ERL_NIF_TERM socket = mk_socket(env, sockRef); + ERL_NIF_TERM socket = esock_mk_socket(env, sockRef); return MKT3(env, esock_atom_socket_tag, tag, socket); } -#endif // #ifndef __WIN32__ + + +/* *** mk_simple_abort_msg *** + * + * Create the simple abort message, which has the following form: + * + * {'$socket', Socket, abort, Info} + * + */ +static +ERL_NIF_TERM mk_simple_abort_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM reason) +{ + return esock_mk_socket_msg(env, sockRef, esock_atom_abort, reason); +} /* *** mk_abort_msg *** @@ -13513,7 +11949,6 @@ ERL_NIF_TERM mk_reg_msg(ErlNifEnv* env, * This message is for processes that are waiting in the * erlang API functions for a select (or this) message. */ -#ifndef __WIN32__ static ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef, @@ -13522,9 +11957,8 @@ ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env, { ERL_NIF_TERM info = MKT2(env, opRef, reason); - return mk_socket_msg(env, sockRef, esock_atom_abort, info); + return esock_mk_socket_msg(env, sockRef, esock_atom_abort, info); } -#endif // #ifndef __WIN32__ /* *** mk_wrap_msg *** @@ -13534,15 +11968,13 @@ ERL_NIF_TERM mk_abort_msg(ErlNifEnv* env, * {'$socket', Socket, counter_wrap, Counter} * */ -#ifndef __WIN32__ static ERL_NIF_TERM mk_wrap_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef, ERL_NIF_TERM cnt) { - return mk_socket_msg(env, sockRef, atom_counter_wrap, cnt); + return esock_mk_socket_msg(env, sockRef, atom_counter_wrap, cnt); } -#endif // #ifndef __WIN32__ /* *** mk_close_msg *** @@ -13552,15 +11984,14 @@ ERL_NIF_TERM mk_wrap_msg(ErlNifEnv* env, * {'$socket', Socket, close, closeRef} * */ -#ifndef __WIN32__ static ERL_NIF_TERM mk_close_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef, ERL_NIF_TERM closeRef) { - return mk_socket_msg(env, sockRef, esock_atom_close, closeRef); + return esock_mk_socket_msg(env, sockRef, esock_atom_close, closeRef); } -#endif // #ifndef __WIN32__ + /* *** mk_select_msg *** @@ -13576,12 +12007,12 @@ ERL_NIF_TERM mk_select_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef, ERL_NIF_TERM selectRef) { - return mk_socket_msg(env, sockRef, esock_atom_select, selectRef); + return esock_mk_socket_msg(env, sockRef, esock_atom_select, selectRef); } #endif // #ifndef __WIN32__ -/* *** mk_socket_msg *** +/* *** esock_mk_socket_msg *** * * Construct the socket message: * @@ -13592,18 +12023,16 @@ ERL_NIF_TERM mk_select_msg(ErlNifEnv* env, * Info :: term() * */ -#ifndef __WIN32__ -static -ERL_NIF_TERM mk_socket_msg(ErlNifEnv* env, - ERL_NIF_TERM sockRef, - ERL_NIF_TERM tag, - ERL_NIF_TERM info) +extern +ERL_NIF_TERM esock_mk_socket_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM tag, + ERL_NIF_TERM info) { - ERL_NIF_TERM socket = mk_socket(env, sockRef); + ERL_NIF_TERM socket = esock_mk_socket(env, sockRef); return MKT4(env, esock_atom_socket_tag, socket, tag, info); } -#endif // #ifndef __WIN32__ /* *** mk_socket *** @@ -13612,14 +12041,12 @@ ERL_NIF_TERM mk_socket_msg(ErlNifEnv* env, * * socket:socket() :: {'$socket', SockRef :: reference()} */ -#ifndef __WIN32__ -static -ERL_NIF_TERM mk_socket(ErlNifEnv* env, - ERL_NIF_TERM sockRef) +extern +ERL_NIF_TERM esock_mk_socket(ErlNifEnv* env, + ERL_NIF_TERM sockRef) { return MKT2(env, esock_atom_socket_tag, sockRef); } -#endif // #ifndef __WIN32__ /* ---------------------------------------------------------------------- @@ -13823,7 +12250,18 @@ ACTIVATE_NEXT_FUNCS * we make use of set of declaration macros. */ -#ifndef __WIN32__ + +extern +void esock_free_request_queue(ESockRequestQueue* q) +{ + while (q->first) { + ESockRequestQueueElement* free_me = q->first; + q->first = free_me->nextP; + esock_free_env("dtor", free_me->data.env); + FREE(free_me); + } +} + /* *** esock_acceptor_search4pid *** * *** esock_writer_search4pid *** @@ -13849,7 +12287,6 @@ ACTIVATE_NEXT_FUNCS REQ_SEARCH4PID_FUNCS #undef REQ_SEARCH4PID_FUNC_DECL -#endif // #ifndef __WIN32__ @@ -13862,8 +12299,6 @@ REQ_SEARCH4PID_FUNCS * */ -#ifndef __WIN32__ - #define REQ_PUSH_FUNCS \ REQ_PUSH_FUNC_DECL(acceptor, acceptorsQ) \ REQ_PUSH_FUNC_DECL(writer, writersQ) \ @@ -13872,9 +12307,10 @@ REQ_SEARCH4PID_FUNCS #define REQ_PUSH_FUNC_DECL(F, Q) \ extern \ void esock_##F##_push(ErlNifEnv* env, \ - ESockDescriptor* descP, \ - ErlNifPid pid, /* self() */ \ - ERL_NIF_TERM ref) \ + ESockDescriptor* descP, \ + ErlNifPid pid, /* self() */ \ + ERL_NIF_TERM ref, \ + void* dataP) \ { \ ESockRequestQueueElement *e; \ ESockRequestor *reqP; \ @@ -13882,20 +12318,18 @@ REQ_SEARCH4PID_FUNCS ESOCK_ASSERT( (e = MALLOC(sizeof(ESockRequestQueueElement))) \ != NULL ); \ reqP = &e->data; \ - reqP->pid = pid; \ + reqP->dataP = dataP; \ + reqP->pid = pid; \ ESOCK_ASSERT( MONP("esock_" #F "_push -> " #F " request", \ env, descP, &pid, &reqP->mon) == 0 ); \ - reqP->env = esock_alloc_env("esock_" #F "_push"); \ - reqP->ref = CP_TERM(reqP->env, ref); \ + reqP->env = esock_alloc_env("esock_" #F "_push"); \ + reqP->ref = CP_TERM(reqP->env, ref); \ \ qpush(&descP->Q, e); \ } REQ_PUSH_FUNCS #undef REQ_PUSH_FUNC_DECL -#endif // #ifndef __WIN32__ - - /* *** esock_acceptor_pop *** * *** esock_writer_pop *** @@ -13927,6 +12361,50 @@ REQ_POP_FUNCS +/* *** esock_acceptor_get *** + * *** esock_writer_get *** + * *** esock_reader_get *** + * + * Remove a requestor (acceptor, writer, or reader) from its queue. + * + */ + +#ifdef __WIN32__ + +#define REQ_GET_FUNCS \ + REQ_GET_FUNC_DECL(acceptor, acceptorsQ) \ + REQ_GET_FUNC_DECL(writer, writersQ) \ + REQ_GET_FUNC_DECL(reader, readersQ) + +#define REQ_GET_FUNC_DECL(F, Q) \ + extern \ + BOOLEAN_T esock_##F##_get(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM* refP, \ + const ErlNifPid* pidP, \ + ESockRequestor* reqP) \ + { \ + ESockRequestQueueElement* elemP; \ + \ + elemP = qget(env, descP, "esock_" #F "_get ", \ + &descP->Q, refP, pidP); \ + if (elemP != NULL) { \ + reqP->pid = elemP->data.pid; \ + reqP->mon = elemP->data.mon; \ + reqP->env = elemP->data.env; \ + reqP->ref = elemP->data.ref; \ + reqP->dataP = elemP->data.dataP; \ + return TRUE; \ + } \ + return FALSE; \ + } +REQ_GET_FUNCS +#undef REQ_GET_FUNC_DECL + +#endif // #ifndef __WIN32__ + + + /* *** esock_acceptor_unqueue *** * *** esock_writer_unqueue *** * *** esock_reader_unqueue *** @@ -13935,8 +12413,6 @@ REQ_POP_FUNCS * */ -#ifndef __WIN32__ - #define REQ_UNQUEUE_FUNCS \ REQ_UNQUEUE_FUNC_DECL(acceptor, acceptorsQ) \ REQ_UNQUEUE_FUNC_DECL(writer, writersQ) \ @@ -13955,17 +12431,12 @@ REQ_POP_FUNCS REQ_UNQUEUE_FUNCS #undef REQ_UNQUEUE_FUNC_DECL -#endif // #ifndef __WIN32__ - - /* *** requestor pop *** * * Pop an requestor from its queue. */ -#ifndef __WIN32__ - extern BOOLEAN_T esock_requestor_pop(ESockRequestQueue* q, ESockRequestor* reqP) @@ -13975,10 +12446,11 @@ BOOLEAN_T esock_requestor_pop(ESockRequestQueue* q, esock_free_env("requestor_pop", reqP->env); if (e != NULL) { - reqP->pid = e->data.pid; - reqP->mon = e->data.mon; - reqP->env = e->data.env; - reqP->ref = e->data.ref; + reqP->pid = e->data.pid; + reqP->mon = e->data.mon; + reqP->env = e->data.env; + reqP->ref = e->data.ref; + reqP->dataP = e->data.dataP; FREE(e); return TRUE; } else { @@ -13994,8 +12466,9 @@ void esock_requestor_init(ESockRequestor* reqP) { enif_set_pid_undefined(&reqP->pid); MON_INIT(&reqP->mon); - reqP->env = NULL; - reqP->ref = esock_atom_undefined; + reqP->env = NULL; + reqP->ref = esock_atom_undefined; + reqP->dataP = NULL; } extern @@ -14004,17 +12477,16 @@ void esock_requestor_release(const char* slogan, ESockDescriptor* descP, ESockRequestor* reqP) { + reqP->dataP = NULL; enif_set_pid_undefined(&reqP->pid); (void) DEMONP(slogan, env, descP, &reqP->mon); + esock_clear_env(slogan, reqP->env); esock_free_env(slogan, reqP->env); reqP->env = NULL; reqP->ref = esock_atom_undefined; } -#endif // #ifndef __WIN32__ - -#ifndef __WIN32__ static BOOLEAN_T qsearch4pid(ErlNifEnv* env, @@ -14034,6 +12506,22 @@ BOOLEAN_T qsearch4pid(ErlNifEnv* env, } static +unsigned int qlength(ESockRequestQueue* q) +{ + ESockRequestQueueElement* tmp; + unsigned int cnt = 0; + + tmp = q->first; + while (tmp != NULL) { + cnt++; + tmp = tmp->nextP; + } + + return cnt; +} + + +static void qpush(ESockRequestQueue* q, ESockRequestQueueElement* e) { @@ -14048,6 +12536,7 @@ void qpush(ESockRequestQueue* q, } } + static ESockRequestQueueElement* qpop(ESockRequestQueue* q) { @@ -14066,11 +12555,8 @@ ESockRequestQueueElement* qpop(ESockRequestQueue* q) return e; } -#endif // #ifndef __WIN32__ - -#ifndef __WIN32__ static BOOLEAN_T qunqueue(ErlNifEnv* env, ESockDescriptor* descP, @@ -14079,19 +12565,40 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, ERL_NIF_TERM* refP, const ErlNifPid* pidP) { + ESockRequestQueueElement* e = qget(env, descP, slogan, q, refP, pidP); + + if (e != NULL) { + (void) DEMONP(slogan, env, descP, &e->data.mon); + esock_clear_env(slogan, e->data.env); + esock_free_env(slogan, e->data.env); + FREE(e); + + return TRUE; + } else { + return FALSE; + } +} + + +static +ESockRequestQueueElement* qget(ErlNifEnv* env, + ESockDescriptor* descP, + const char* slogan, + ESockRequestQueue* q, + ERL_NIF_TERM* refP, + const ErlNifPid* pidP) +{ ESockRequestQueueElement* e = q->first; ESockRequestQueueElement* p = NULL; - /* Check if it was one of the waiting acceptor processes */ + /* Check if it was one of the waiting requestor processes */ while (e != NULL) { if (COMPARE_PIDS(&e->data.pid, pidP) == 0) { if ((refP != NULL) && (COMPARE(e->data.ref, *refP) != 0)) - return FALSE; + return NULL; /* We have a match */ - (void) DEMONP(slogan, env, descP, &e->data.mon); - if (p != NULL) { /* Not the first, but could be the last */ if (q->last == e) { @@ -14111,10 +12618,7 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, } } - esock_free_env("qunqueue", e->data.env); - FREE(e); - - return TRUE; + return e; } /* Try next */ @@ -14122,9 +12626,8 @@ BOOLEAN_T qunqueue(ErlNifEnv* env, e = e->nextP; } - return FALSE; + return NULL; } -#endif // #ifndef __WIN32__ @@ -14171,8 +12674,6 @@ void esock_cnt_dec(ESockCounter* cnt, ESockCounter dec) * ---------------------------------------------------------------------- */ -#ifndef __WIN32__ - extern int esock_monitor(const char* slogan, ErlNifEnv* env, @@ -14261,8 +12762,6 @@ BOOLEAN_T esock_monitor_eq(const ESockMonitor* monP, return FALSE; } -#endif // #ifndef __WIN32__ - /* ---------------------------------------------------------------------- @@ -14271,18 +12770,6 @@ BOOLEAN_T esock_monitor_eq(const ESockMonitor* monP, */ -#ifndef __WIN32__ -static void free_request_queue(ESockRequestQueue* q) -{ - while (q->first) { - ESockRequestQueueElement* free_me = q->first; - q->first = free_me->nextP; - esock_free_env("dtor", free_me->data.env); - FREE(free_me); - } -} -#endif // #ifndef __WIN32__ - /* ========================================================================= * esock_dtor - Callback function for resource destructor * @@ -14290,79 +12777,30 @@ static void free_request_queue(ESockRequestQueue* q) static void esock_dtor(ErlNifEnv* env, void* obj) { -#ifndef __WIN32__ ESockDescriptor* descP = (ESockDescriptor*) obj; MLOCK(descP->readMtx); MLOCK(descP->writeMtx); - SGDBG( ("SOCKET", "dtor {%d,0x%X}\r\n", + SGDBG( ("SOCKET", "esock_dtor {%d,0x%X}\r\n", descP->sock, descP->readState | descP->writeState) ); - if (IS_SELECTED(descP)) { - /* We have used the socket in the select machinery, - * so we must have closed it properly to get here - */ - ESOCK_ASSERT( IS_CLOSED(descP->readState) ); - ESOCK_ASSERT( IS_CLOSED(descP->writeState) ); - ESOCK_ASSERT( descP->sock == INVALID_SOCKET ); - } else { - /* The socket is only opened, should be safe to close nonblocking */ - (void) sock_close(descP->sock); - descP->sock = INVALID_SOCKET; - } - - SGDBG( ("SOCKET", "dtor -> set state and pattern\r\n") ); - descP->readState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED); - descP->writeState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED); - descP->pattern = (ESOCK_DESC_PATTERN_DTOR | ESOCK_STATE_CLOSED); - - esock_free_env("dtor reader", descP->currentReader.env); - descP->currentReader.env = NULL; - - esock_free_env("dtor writer", descP->currentWriter.env); - descP->currentWriter.env = NULL; - - esock_free_env("dtor acceptor", descP->currentAcceptor.env); - descP->currentAcceptor.env = NULL; - - SGDBG( ("SOCKET", "dtor -> try free readers request queue\r\n") ); - free_request_queue(&descP->readersQ); - - SGDBG( ("SOCKET", "dtor -> try free writers request queue\r\n") ); - free_request_queue(&descP->writersQ); - - SGDBG( ("SOCKET", "dtor -> try free acceptors request queue\r\n") ); - free_request_queue(&descP->acceptorsQ); - -#ifdef HAVE_SENDFILE - ESOCK_ASSERT( descP->sendfileHandle == INVALID_HANDLE ); - if (descP->sendfileCountersP != NULL) { - FREE(descP->sendfileCountersP); - descP->sendfileCountersP = NULL; - } -#endif - - esock_free_env("dtor close env", descP->closeEnv); - descP->closeEnv = NULL; - - esock_free_env("dtor meta env", descP->meta.env); - descP->meta.env = NULL; + ESOCK_IO_DTOR(env, descP); MUNLOCK(descP->writeMtx); MUNLOCK(descP->readMtx); - SGDBG( ("SOCKET", "dtor -> try destroy read mutex\r\n") ); + SGDBG( ("SOCKET", "esock_dtor -> try destroy read mutex\r\n") ); MDESTROY(descP->readMtx); descP->readMtx = NULL; - SGDBG( ("SOCKET", "dtor -> try destroy write mutex\r\n") ); + SGDBG( ("SOCKET", "esock_dtor -> try destroy write mutex\r\n") ); MDESTROY(descP->writeMtx); descP->writeMtx = NULL; - SGDBG( ("SOCKET", "dtor -> done\r\n") ); -#endif // #ifndef __WIN32__ + SGDBG( ("SOCKET", "esock_dtor -> done\r\n") ); } + /* ========================================================================= * esock_stop - Callback function for resource stop * @@ -14381,7 +12819,6 @@ void esock_dtor(ErlNifEnv* env, void* obj) static void esock_stop(ErlNifEnv* env, void* obj, ErlNifEvent fd, int is_direct_call) { -#ifndef __WIN32__ ESockDescriptor* descP = (ESockDescriptor*) obj; if (is_direct_call) { @@ -14434,96 +12871,26 @@ void esock_stop(ErlNifEnv* env, void* obj, ErlNifEvent fd, int is_direct_call) (unsigned long) descP->accWaits, (unsigned long) descP->accFails) ); -#ifdef HAVE_SENDFILE - if (descP->sendfileCountersP != NULL) { - ESockSendfileCounters *cP = descP->sendfileCountersP; - - SSDBG( descP, ("SOCKET", "esock_stop {%d/%d} ->" - "\r\nsendfileCounters:" - "\r\n cnt: %lu" - "\r\n byteCnt: %lu" - "\r\n fails: %lu" - "\r\n max: %lu" - "\r\n pkg: %lu" - "\r\n pkgMax %lu" - "\r\n tries: %lu" - "\r\n waits: %lu" - "\r\n", - descP->sock, fd, - (unsigned long) cP->cnt, - (unsigned long) cP->byteCnt, - (unsigned long) cP->fails, - (unsigned long) cP->max, - (unsigned long) cP->pkg, - (unsigned long) cP->pkgMax, - (unsigned long) cP->tries, - (unsigned long) cP->waits) ); - } -#endif - - /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - * - * Inform waiting Closer, or close socket - * - * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - */ - - if (! enif_is_pid_undefined(&descP->closerPid)) { - /* We have a waiting closer process after nif_close() - * - send message to trigger nif_finalize_close() - */ - - SSDBG( descP, - ("SOCKET", - "esock_stop {%d/%d} -> send close msg to %T\r\n", - descP->sock, fd, MKPID(env, &descP->closerPid)) ); - - esock_send_close_msg(env, descP, &descP->closerPid); - /* Message send frees closeEnv */ - descP->closeEnv = NULL; - descP->closeRef = esock_atom_undefined; - } else { - int err; - - /* We do not have a closer process - * - have to do an unclean (non blocking) close */ - -#ifdef HAVE_SENDFILE - if (descP->sendfileHandle != INVALID_HANDLE) - esock_send_sendfile_deferred_close_msg(env, descP); -#endif - - err = esock_close_socket(env, descP, FALSE); + ESOCK_IO_STOP(env, descP); - if (err != 0) - esock_warning_msg("Failed closing socket without " - "closer process: " - "\r\n Controlling Process: %T" - "\r\n Descriptor: %d" - "\r\n Errno: %d (%T)" - "\r\n", - descP->ctrlPid, descP->sock, - err, MKA(env, erl_errno_id(err))); - } + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); SSDBG( descP, ("SOCKET", "esock_stop {%d/%d} -> done\r\n", descP->sock, fd) ); - MUNLOCK(descP->writeMtx); - MUNLOCK(descP->readMtx); -#endif // #ifndef __WIN32__ } -/* *** esock_stop_handle_current *** +/* *** esock_stop_handle_currentalloc_desc *** * * Handle current requestor (reader, writer or acceptor) during * socket stop. */ -static +extern void esock_stop_handle_current(ErlNifEnv* env, const char* role, ESockDescriptor* descP, @@ -14549,14 +12916,13 @@ void esock_stop_handle_current(ErlNifEnv* env, * nif_abort message with the specified reason to each member, * and empty the queue. */ -#ifndef __WIN32__ -static -void inform_waiting_procs(ErlNifEnv* env, - const char* role, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - ESockRequestQueue* q, - ERL_NIF_TERM reason) +extern +void esock_inform_waiting_procs(ErlNifEnv* env, + const char* role, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ESockRequestQueue* q, + ERL_NIF_TERM reason) { ESockRequestQueueElement* currentP = q->first; ESockRequestQueueElement* nextP; @@ -14596,7 +12962,6 @@ void inform_waiting_procs(ErlNifEnv* env, q->first = NULL; q->last = NULL; } -#endif // #ifndef __WIN32__ /* ========================================================================= @@ -14609,7 +12974,6 @@ void esock_down(ErlNifEnv* env, const ErlNifPid* pidP, const ErlNifMonitor* monP) { -#ifndef __WIN32__ ESockDescriptor* descP = (ESockDescriptor*) obj; MLOCK(descP->readMtx); @@ -14623,362 +12987,17 @@ void esock_down(ErlNifEnv* env, B2S(IS_CLOSED(descP->readState)), B2S(IS_CLOSING(descP->readState))) ); - if (COMPARE_PIDS(&descP->closerPid, pidP) == 0) { - - /* The closer process went down - * - it will not call nif_finalize_close - */ - - enif_set_pid_undefined(&descP->closerPid); - - if (MON_EQ(&descP->closerMon, monP)) { - MON_INIT(&descP->closerMon); - - SSDBG( descP, - ("SOCKET", - "esock_down {%d} -> closer process exit\r\n", - descP->sock) ); - - } else { - // The owner is the closer so we used its monitor - - ESOCK_ASSERT( MON_EQ(&descP->ctrlMon, monP) ); - MON_INIT(&descP->ctrlMon); - enif_set_pid_undefined(&descP->ctrlPid); - - SSDBG( descP, - ("SOCKET", - "esock_down {%d} -> closer controlling process exit\r\n", - descP->sock) ); - } - - /* Since the closer went down there was one, - * hence esock_close() must have run or scheduled esock_stop(), - * or the socket has never been selected upon - */ - - if (descP->closeEnv == NULL) { - int err; - - /* Since there is no closeEnv, - * esock_close() did not schedule esock_stop() - * and is about to call esock_finalize_close() but died, - * or esock_stop() has run, sent close_msg to the closer - * and cleared ->closeEnv but the closer died - * - we have to do an unclean (non blocking) socket close here - */ - -#ifdef HAVE_SENDFILE - if (descP->sendfileHandle != INVALID_HANDLE) - esock_send_sendfile_deferred_close_msg(env, descP); -#endif - - err = esock_close_socket(env, descP, FALSE); - if (err != 0) - esock_warning_msg("Failed closing socket for terminating " - "closer process: " - "\r\n Closer Process: %T" - "\r\n Descriptor: %d" - "\r\n Errno: %d (%T)" - "\r\n", - MKPID(env, pidP), descP->sock, - err, MKA(env, erl_errno_id(err))); - } else { - /* Since there is a closeEnv esock_stop() has not run yet - * - when it finds that there is no closer process - * it will close the socket and ignore the close_msg - */ - esock_free_env("esock_down - close-env", descP->closeEnv); - descP->closeEnv = NULL; - descP->closeRef = esock_atom_undefined; - } - - } else if (MON_EQ(&descP->ctrlMon, monP)) { - MON_INIT(&descP->ctrlMon); - /* The owner went down */ - enif_set_pid_undefined(&descP->ctrlPid); - - if (IS_OPEN(descP->readState)) { - SSDBG( descP, - ("SOCKET", - "esock_down {%d} -> controller process exit" - "\r\n initiate close\r\n", - descP->sock) ); - - esock_down_ctrl(env, descP, pidP); - - descP->readState |= ESOCK_STATE_CLOSING; - descP->writeState |= ESOCK_STATE_CLOSING; - } else { - SSDBG( descP, - ("SOCKET", - "esock_down {%d} -> controller process exit" - "\r\n already closed or closing\r\n", - descP->sock) ); - } - - } else if (descP->connectorP != NULL && - MON_EQ(&descP->connector.mon, monP)) { - MON_INIT(&descP->connector.mon); - - SSDBG( descP, - ("SOCKET", - "esock_down {%d} -> connector process exit\r\n", - descP->sock) ); - - /* connectorP is only set during connection. - * Forget all about the ongoing connection. - * We might end up connected, but the process that initiated - * the connection has died and will never know - */ - - esock_requestor_release("esock_down->connector", - env, descP, &descP->connector); - descP->connectorP = NULL; - descP->writeState &= ~ESOCK_STATE_CONNECTING; - - } else { - ERL_NIF_TERM sockRef; - - /* check all operation queue(s): acceptor, writer and reader. - * - * Is it really any point in doing this if the socket is closed? - * - */ - - sockRef = enif_make_resource(env, descP); - - if (IS_CLOSED(descP->readState)) { - SSDBG( descP, - ("SOCKET", - "esock_down(%T) {%d} -> stray down: %T\r\n", - sockRef, descP->sock, pidP) ); - } else { - - SSDBG( descP, - ("SOCKET", - "esock_down(%T) {%d} -> other process term\r\n", - sockRef, descP->sock) ); - - if (descP->currentReaderP != NULL) - esock_down_reader(env, descP, sockRef, pidP, monP); - if (descP->currentAcceptorP != NULL) - esock_down_acceptor(env, descP, sockRef, pidP, monP); - if (descP->currentWriterP != NULL) - esock_down_writer(env, descP, sockRef, pidP, monP); - } - } + ESOCK_IO_DOWN(env, descP, pidP, monP); MUNLOCK(descP->writeMtx); MUNLOCK(descP->readMtx); SSDBG( descP, ("SOCKET", "esock_down -> done\r\n") ); -#endif // #ifndef __WIN32__ -} - - - -/* *** esock_down_ctrl *** - * - * Stop after a downed controller - * - */ -#ifndef __WIN32__ -static -void esock_down_ctrl(ErlNifEnv* env, - ESockDescriptor* descP, - const ErlNifPid* pidP) -{ - SSDBG( descP, - ("SOCKET", "esock_down_ctrl {%d} ->" - "\r\n Pid: %T" - "\r\n", descP->sock, MKPID(env, pidP)) ); - - if (esock_do_stop(env, descP)) { - /* esock_stop() is scheduled - * - it has to close the socket - */ - SSDBG( descP, - ("SOCKET", "esock_down_ctrl {%d} -> stop was scheduled\r\n", - descP->sock) ); - } else { - int err; - - /* Socket is not in the select machinery - * so esock_stop() will not be called - * - we have to do an unclean (non blocking) socket close here - */ - -#ifdef HAVE_SENDFILE - if (descP->sendfileHandle != INVALID_HANDLE) - esock_send_sendfile_deferred_close_msg(env, descP); -#endif - - err = esock_close_socket(env, descP, FALSE); - if (err != 0) - esock_warning_msg("Failed closing socket for terminating " - "owner process: " - "\r\n Owner Process: %T" - "\r\n Descriptor: %d" - "\r\n Errno: %d (%T)" - "\r\n", - MKPID(env, pidP), descP->sock, - err, MKA(env, erl_errno_id(err))); - } } -#endif // #ifndef __WIN32__ -/* *** esock_down_acceptor *** - * - * Check and then handle a downed acceptor process. - * - */ -#ifndef __WIN32__ -static -void esock_down_acceptor(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pidP, - const ErlNifMonitor* monP) -{ - if (MON_EQ(&descP->currentAcceptor.mon, monP)) { - MON_INIT(&descP->currentAcceptor.mon); - - SSDBG( descP, - ("SOCKET", - "esock_down_acceptor(%T) {%d} -> " - "current acceptor - try activate next\r\n", - sockRef, descP->sock) ); - - if (!esock_activate_next_acceptor(env, descP, sockRef)) { - - SSDBG( descP, - ("SOCKET", - "esock_down_acceptor(%T) {%d} -> no more writers\r\n", - sockRef, descP->sock) ); - - descP->readState &= ~ESOCK_STATE_ACCEPTING; - - descP->currentAcceptorP = NULL; - } - - } else { - - /* Maybe unqueue one of the waiting acceptors */ - - SSDBG( descP, - ("SOCKET", - "esock_down_acceptor(%T) {%d} -> " - "not current acceptor - maybe a waiting acceptor\r\n", - sockRef, descP->sock) ); - - esock_acceptor_unqueue(env, descP, NULL, pidP); - } -} -#endif // #ifndef __WIN32__ - - -/* *** esock_down_writer *** - * - * Check and then handle a downed writer process. - * - */ -#ifndef __WIN32__ -static -void esock_down_writer(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pidP, - const ErlNifMonitor* monP) -{ - if (MON_EQ(&descP->currentWriter.mon, monP)) { - MON_INIT(&descP->currentWriter.mon); - - SSDBG( descP, - ("SOCKET", - "esock_down_writer(%T) {%d} -> " - "current writer - try activate next\r\n", - sockRef, descP->sock) ); - - if (!esock_activate_next_writer(env, descP, sockRef)) { - - SSDBG( descP, - ("SOCKET", - "esock_down_writer(%T) {%d} -> no active writer\r\n", - sockRef, descP->sock) ); - - descP->currentWriterP = NULL; - } - - } else { - - /* Maybe unqueue one of the waiting writer(s) */ - - SSDBG( descP, - ("SOCKET", - "esock_down_writer(%T) {%d} -> " - "not current writer - maybe a waiting writer\r\n", - sockRef, descP->sock) ); - - esock_writer_unqueue(env, descP, NULL, pidP); - } -} -#endif // #ifndef __WIN32__ - - - - -/* *** esock_down_reader *** - * - * Check and then handle a downed reader process. - * - */ -#ifndef __WIN32__ -static -void esock_down_reader(ErlNifEnv* env, - ESockDescriptor* descP, - ERL_NIF_TERM sockRef, - const ErlNifPid* pidP, - const ErlNifMonitor* monP) -{ - if (MON_EQ(&descP->currentReader.mon, monP)) { - MON_INIT(&descP->currentReader.mon); - - SSDBG( descP, - ("SOCKET", - "esock_down_reader(%T) {%d} -> " - "current reader - try activate next\r\n", - sockRef, descP->sock) ); - - if (! esock_activate_next_reader(env, descP, sockRef)) { - - SSDBG( descP, - ("SOCKET", - "esock_down_reader(%T) {%d} -> no more readers\r\n", - sockRef, descP->sock) ); - - descP->currentReaderP = NULL; - } - - } else { - - /* Maybe unqueue one of the waiting reader(s) */ - - SSDBG( descP, - ("SOCKET", - "esock_down_reader(%T) {%d} -> " - "not current reader - maybe a waiting reader\r\n", - sockRef, descP->sock) ); - - esock_reader_unqueue(env, descP, NULL, pidP); - } -} -#endif // #ifndef __WIN32__ - - /* * The idea with this function is that it should call esock_io_finish * and release anything allocated by the I/O backend (just to be a @@ -14990,11 +13009,14 @@ void esock_down_reader(ErlNifEnv* env, static void esock_on_halt(void* priv_data) { + // We do not *currently* use this (priv_data), so ignore #ifndef __WIN32__ VOID(priv_data); #else VOIDP(priv_data); #endif + + ESOCK_IO_FIN(); } @@ -15052,7 +13074,6 @@ ErlNifFunc esock_funcs[] = }; -#ifndef __WIN32__ static char* extract_debug_filename(ErlNifEnv* env, ERL_NIF_TERM map) @@ -15074,7 +13095,6 @@ char* extract_debug_filename(ErlNifEnv* env, filename[bin.size] = '\0'; return filename; } -#endif // #ifndef __WIN32__ @@ -15085,6 +13105,9 @@ char* extract_debug_filename(ErlNifEnv* env, static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { + ErlNifSysInfo sysInfo; + unsigned int ioNumThreads, ioNumThreadsDef; + /* +++ Local atoms and error reason atoms +++ */ #define LOCAL_ATOM_DECL(A) atom_##A = MKA(env, #A) LOCAL_ATOMS; @@ -15099,86 +13122,6 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) esock_atom_socket_tag = MKA(env, "$socket"); - /* This is (currently) intended for Windows use */ - { - ErlNifSysInfo sysInfo; - unsigned int ioNumThreadsDef; // Used as "default" value - - enif_system_info(&sysInfo, sizeof(ErlNifSysInfo)); - - ioNumThreadsDef = - (unsigned int) (sysInfo.scheduler_threads > 0) ? - 2*sysInfo.scheduler_threads : 1; // ESOCK_IO_NUM_THREADS); - - data.ioNumThreads = - esock_get_uint_from_map(env, load_info, - atom_io_num_threads, - ioNumThreadsDef); - } - - -#ifdef __WIN32__ - - io_backend.init = NULL; - io_backend.finish = NULL; - io_backend.open_with_fd = NULL; - io_backend.open_plain = NULL; - io_backend.bind = NULL; - io_backend.connect = NULL; - io_backend.listen = NULL; - io_backend.accept = NULL; - io_backend.send = NULL; - io_backend.sendto = NULL; - io_backend.sendmsg = NULL; - io_backend.sendfile_start = NULL; - io_backend.sendfile_cont = NULL; - io_backend.sendfile_dc = NULL; - io_backend.recv = NULL; - io_backend.recvfrom = NULL; - io_backend.recvmsg = NULL; - io_backend.close = NULL; - io_backend.fin_close = NULL; - io_backend.shutdown = NULL; - io_backend.sockname = NULL; - io_backend.peername = NULL; - -#else - - io_backend.init = essio_init; - io_backend.finish = essio_finish; - io_backend.open_with_fd = essio_open_with_fd; - io_backend.open_plain = essio_open_plain; - io_backend.bind = essio_bind; - io_backend.connect = essio_connect; - io_backend.listen = essio_listen; - io_backend.accept = essio_accept; - io_backend.send = essio_send; - io_backend.sendto = essio_sendto; - io_backend.sendmsg = essio_sendmsg; - io_backend.sendfile_start = essio_sendfile_start; - io_backend.sendfile_cont = essio_sendfile_cont; - io_backend.sendfile_dc = essio_sendfile_deferred_close; - io_backend.recv = essio_recv; - io_backend.recvfrom = essio_recvfrom; - io_backend.recvmsg = essio_recvmsg; - io_backend.close = essio_close; - io_backend.fin_close = essio_fin_close; - io_backend.shutdown = essio_shutdown; - io_backend.sockname = essio_sockname; - io_backend.peername = essio_peername; - -#endif - -#ifndef __WIN32__ - // This ifndef is only temporary!! - if (ESOCK_IO_INIT(data.ioNumThreads) != ESOCK_IO_OK) { - esock_error_msg("Failed initiating I/O backend"); - return 1; // Failure - } -#endif - -#ifndef __WIN32__ - if (! esock_extract_pid_from_map(env, load_info, atom_registry, &data.regPid)) { @@ -15186,16 +13129,19 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) return 1; // Failure - no registry pid } + /* --esock-disable-registry */ data.useReg = esock_get_bool_from_map(env, load_info, esock_atom_use_registry, ESOCK_USE_SOCKET_REGISTRY); + /* --esock-enable-iow */ data.iow = esock_get_bool_from_map(env, load_info, atom_iow, ESOCK_NIF_IOW_DEFAULT); + /* --esock-debug-file=<filename> */ { char *debug_filename; @@ -15234,9 +13180,37 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) data.numProtoUDP = 0; data.numProtoSCTP = 0; + initOpts(); initCmsgTables(); + + // #define ESOCK_DISPLAY_OPTION_TABLES 1 +#if defined(ESOCK_DISPLAY_OPTION_TABLES) + { + /* Display option table(s) after init */ + ESOCK_EPRINTF("\r\n[ESOCK] Option tables after init:\r\n"); + + for (int levelIdx = 0; levelIdx < NUM(optLevels); levelIdx++) { + int numOpts = optLevels[levelIdx].num; + int level = optLevels[levelIdx].level; + ERL_NIF_TERM lname = *optLevels[levelIdx].nameP; + struct ESockOpt* opts = optLevels[levelIdx].opts; + + ESOCK_EPRINTF("[ESOCK] [%d] Option table for level %T (%d) (%d options):\r\n", + levelIdx, lname, level, numOpts); + + for (int optIdx = 0; optIdx < numOpts; optIdx++) { + ESOCK_EPRINTF("[ESOCK] %T[%d]: %T -> %d\r\n", + lname, optIdx, lname, opts[optIdx].opt); + + } + ESOCK_EPRINTF("\r\n"); + } + } +#endif + + data.iov_max = #if defined(NO_SYSCONF) || (! defined(_SC_IOV_MAX)) # ifdef IOV_MAX @@ -15250,7 +13224,129 @@ int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) ; ESOCK_ASSERT( data.iov_max > 0 ); -#endif // #ifndef __WIN32__ + + /* This is (currently) intended for Windows use */ + enif_system_info(&sysInfo, sizeof(ErlNifSysInfo)); + + /* We should have a config options for this: + * --esock-num-io-threads=16 + * + * ESOCK_IO_NUM_THREADS + */ + ioNumThreadsDef = + (unsigned int) (sysInfo.scheduler_threads > 0) ? + 2*sysInfo.scheduler_threads : 2; + + ioNumThreads = esock_get_uint_from_map(env, load_info, + atom_io_num_threads, + ioNumThreadsDef); + +#ifdef __WIN32__ + + io_backend.init = esaio_init; + io_backend.finish = esaio_finish; + + io_backend.info = esaio_info; + io_backend.cmd = esock_command; + io_backend.supports_0 = esock_supports_0; + io_backend.supports_1 = esock_supports_1; + + io_backend.open_with_fd = NULL; + io_backend.open_plain = esaio_open_plain; + io_backend.bind = esaio_bind; + io_backend.connect = esaio_connect; + io_backend.listen = esock_listen; + io_backend.accept = esaio_accept; + io_backend.send = esaio_send; + io_backend.sendto = esaio_sendto; + io_backend.sendmsg = esaio_sendmsg; + io_backend.sendfile_start = NULL; + io_backend.sendfile_cont = NULL; + io_backend.sendfile_dc = NULL; + io_backend.recv = esaio_recv; + io_backend.recvfrom = esaio_recvfrom; + io_backend.recvmsg = esaio_recvmsg; + io_backend.close = esaio_close; + io_backend.fin_close = esaio_fin_close; + io_backend.shutdown = esock_shutdown; + io_backend.sockname = esock_sockname; + io_backend.peername = esock_peername; + io_backend.cancel_connect = esaio_cancel_connect; + io_backend.cancel_accept = esaio_cancel_accept; + io_backend.cancel_send = esaio_cancel_send; + io_backend.cancel_recv = esaio_cancel_recv; + + io_backend.setopt = esock_setopt; + io_backend.setopt_native = esock_setopt_native; + io_backend.setopt_otp = esock_setopt_otp; + io_backend.getopt = esock_getopt; + io_backend.getopt_native = esock_getopt_native; + io_backend.getopt_otp = esock_getopt_otp; + + io_backend.ioctl_2 = NULL; + io_backend.ioctl_3 = NULL; + io_backend.ioctl_4 = NULL; + + io_backend.dtor = esaio_dtor; + io_backend.stop = NULL; // esaio_stop; + io_backend.down = esaio_down; + +#else + + io_backend.init = essio_init; + io_backend.finish = essio_finish; + + io_backend.info = essio_info; + io_backend.cmd = esock_command; + io_backend.supports_0 = esock_supports_0; + io_backend.supports_1 = esock_supports_1; + + io_backend.open_with_fd = essio_open_with_fd; + io_backend.open_plain = essio_open_plain; + io_backend.bind = essio_bind; + io_backend.connect = essio_connect; + io_backend.listen = esock_listen; + io_backend.accept = essio_accept; + io_backend.send = essio_send; + io_backend.sendto = essio_sendto; + io_backend.sendmsg = essio_sendmsg; + io_backend.sendfile_start = essio_sendfile_start; + io_backend.sendfile_cont = essio_sendfile_cont; + io_backend.sendfile_dc = essio_sendfile_deferred_close; + io_backend.recv = essio_recv; + io_backend.recvfrom = essio_recvfrom; + io_backend.recvmsg = essio_recvmsg; + io_backend.close = essio_close; + io_backend.fin_close = essio_fin_close; + io_backend.shutdown = esock_shutdown; + io_backend.sockname = esock_sockname; + io_backend.peername = esock_peername; + io_backend.cancel_connect = essio_cancel_connect; + io_backend.cancel_accept = essio_cancel_accept; + io_backend.cancel_send = essio_cancel_send; + io_backend.cancel_recv = essio_cancel_recv; + + io_backend.setopt = esock_setopt; + io_backend.setopt_native = esock_setopt_native; + io_backend.setopt_otp = esock_setopt_otp; + io_backend.getopt = esock_getopt; + io_backend.getopt_native = esock_getopt_native; + io_backend.getopt_otp = esock_getopt_otp; + + io_backend.ioctl_2 = essio_ioctl2; + io_backend.ioctl_3 = essio_ioctl3; + io_backend.ioctl_4 = essio_ioctl4; + + io_backend.dtor = essio_dtor; + io_backend.stop = essio_stop; + io_backend.down = essio_down; + +#endif + + if (ESOCK_IO_INIT(ioNumThreads) != ESOCK_IO_OK) { + esock_error_msg("Failed initiating I/O backend"); + return 1; // Failure + } esocks = enif_open_resource_type_x(env, "sockets", diff --git a/erts/emulator/nifs/common/socket_asyncio.h b/erts/emulator/nifs/common/socket_asyncio.h new file mode 100644 index 0000000000..059ee2bf08 --- /dev/null +++ b/erts/emulator/nifs/common/socket_asyncio.h @@ -0,0 +1,172 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2023-2023. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Asynchronous I/O functions. + * ---------------------------------------------------------------------- + * + * essio = ESock Asynchronous I/O + * + */ + +#ifndef SOCKET_ASYNCIO_H__ +#define SOCKET_ASYNCIO_H__ + +#include "socket_io.h" + +extern int esaio_init(unsigned int numThreads, + const ESockData* dataP); +extern void esaio_finish(void); +extern ERL_NIF_TERM esaio_info(ErlNifEnv* env); + +/* +extern ERL_NIF_TERM esaio_open_with_fd(ErlNifEnv* env, + int fd, + ERL_NIF_TERM eopts, + const ESockData* dataP); + */ +extern ERL_NIF_TERM esaio_open_plain(ErlNifEnv* env, + int domain, + int type, + int protocol, + ERL_NIF_TERM eopts, + const ESockData* dataP); +extern ERL_NIF_TERM esaio_bind(ErlNifEnv* env, + ESockDescriptor* descP, + ESockAddress* sockAddrP, + SOCKLEN_T addrLen); +extern ERL_NIF_TERM esaio_connect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM connRef, + ESockAddress* addrP, + SOCKLEN_T addrLen); +/* +extern ERL_NIF_TERM esaio_listen(ErlNifEnv* env, + ESockDescriptor* descP, + int backlog); +*/ +extern ERL_NIF_TERM esaio_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef); +extern ERL_NIF_TERM esaio_send(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* sndDataP, + int flags); +extern ERL_NIF_TERM esaio_sendto(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* dataP, + int flags, + ESockAddress* toAddrP, + SOCKLEN_T toAddrLen); +extern ERL_NIF_TERM esaio_sendmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ERL_NIF_TERM eMsg, + int flags, + ERL_NIF_TERM eIOV, + const ESockData* dataP); +/* +extern +ERL_NIF_TERM esaio_sendfile_start(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + off_t offset, + size_t count, + ERL_NIF_TERM fRef); +extern +ERL_NIF_TERM esaio_sendfile_cont(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + off_t offset, + size_t count); +extern +ERL_NIF_TERM esaio_sendfile_deferred_close(ErlNifEnv* env, + ESockDescriptor* descP); +*/ + +extern ERL_NIF_TERM esaio_recv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + ssize_t len, + int flags); +extern ERL_NIF_TERM esaio_recvfrom(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + ssize_t len, + int flags); +extern ERL_NIF_TERM esaio_recvmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + ssize_t bufLen, + ssize_t ctrlLen, + int flags); +extern ERL_NIF_TERM esaio_close(ErlNifEnv* env, + ESockDescriptor* descP); +extern ERL_NIF_TERM esaio_fin_close(ErlNifEnv* env, + ESockDescriptor* descP); +extern ERL_NIF_TERM esaio_shutdown(ErlNifEnv* env, + ESockDescriptor* descP, + int how); +extern ERL_NIF_TERM esaio_sockname(ErlNifEnv* env, + ESockDescriptor* descP); +extern ERL_NIF_TERM esaio_peername(ErlNifEnv* env, + ESockDescriptor* descP); +extern ERL_NIF_TERM esaio_cancel_connect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +extern ERL_NIF_TERM esaio_cancel_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +extern ERL_NIF_TERM esaio_cancel_send(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +extern ERL_NIF_TERM esaio_cancel_recv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); + +extern void esaio_dtor(ErlNifEnv* env, + ESockDescriptor* descP); +extern void esaio_stop(ErlNifEnv* env, + ESockDescriptor* descP); +extern void esaio_down(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); + +/* Temporary (I hope) workaround */ +extern void esaio_down_ctrl(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP); + +#endif // SOCKET_ASYNCIO_H__ diff --git a/erts/emulator/nifs/common/socket_int.h b/erts/emulator/nifs/common/socket_int.h index ec76f661f4..2613fe5a1e 100644 --- a/erts/emulator/nifs/common/socket_int.h +++ b/erts/emulator/nifs/common/socket_int.h @@ -33,7 +33,9 @@ #ifdef __WIN32__ /* All this just to replace sys/socket.h, netinet/in.h and sys/un.h??? */ +#if !defined(INCL_WINSOCK_API_TYPEDEFS) #define INCL_WINSOCK_API_TYPEDEFS 1 +#endif #ifndef WINDOWS_H_INCLUDES_WINSOCK2_H #include <winsock2.h> #endif @@ -156,6 +158,13 @@ typedef int BOOLEAN_T; #define B2S(__B__) ((__B__) ? "true" : "false") +#define TYPE2STR(T) (((T) == SOCK_STREAM) ? "stream" : \ + (((T) == SOCK_DGRAM) ? "dgram" : \ + (((T) == SOCK_RAW) ? "raw" : \ + (((T) == SOCK_RDM) ? "rdm" : \ + (((T) == SOCK_SEQPACKET) ? "seqpacket" : \ + "undefined"))))) + #define NUM(Array) (sizeof(Array) / sizeof(*(Array))) @@ -219,10 +228,12 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(autoclose); \ GLOBAL_ATOM_DEF(automedia); \ GLOBAL_ATOM_DEF(bad_data); \ + GLOBAL_ATOM_DEF(base_addr); \ GLOBAL_ATOM_DEF(bindtodevice); \ GLOBAL_ATOM_DEF(block_source); \ GLOBAL_ATOM_DEF(broadcast); \ GLOBAL_ATOM_DEF(busy_poll); \ + GLOBAL_ATOM_DEF(cancel); \ GLOBAL_ATOM_DEF(cantconfig); \ GLOBAL_ATOM_DEF(chaos); \ GLOBAL_ATOM_DEF(checksum); \ @@ -230,6 +241,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(closed); \ GLOBAL_ATOM_DEF(cmsg_cloexec); \ GLOBAL_ATOM_DEF(command); \ + GLOBAL_ATOM_DEF(completion); \ GLOBAL_ATOM_DEF(confirm); \ GLOBAL_ATOM_DEF(congestion); \ GLOBAL_ATOM_DEF(connect); \ @@ -237,6 +249,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(connecting); \ GLOBAL_ATOM_DEF(context); \ GLOBAL_ATOM_DEF(cork); \ + GLOBAL_ATOM_DEF(counters); \ GLOBAL_ATOM_DEF(credentials); \ GLOBAL_ATOM_DEF(ctrl); \ GLOBAL_ATOM_DEF(ctrunc); \ @@ -249,6 +262,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(dgram); \ GLOBAL_ATOM_DEF(disable_fragments); \ GLOBAL_ATOM_DEF(dlci); \ + GLOBAL_ATOM_DEF(dma); \ GLOBAL_ATOM_DEF(domain); \ GLOBAL_ATOM_DEF(dontfrag); \ GLOBAL_ATOM_DEF(dontroute); \ @@ -282,6 +296,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(fragment_interleave); \ GLOBAL_ATOM_DEF(freebind); \ GLOBAL_ATOM_DEF(frelay); \ + GLOBAL_ATOM_DEF(get_overlapped_result); \ GLOBAL_ATOM_DEF(get_peer_addr_info); \ GLOBAL_ATOM_DEF(hatype); \ GLOBAL_ATOM_DEF(hdrincl); \ @@ -309,6 +324,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(ipcomp_level); \ GLOBAL_ATOM_DEF(ipip); \ GLOBAL_ATOM_DEF(ipv6); \ + GLOBAL_ATOM_DEF(irq); \ GLOBAL_ATOM_DEF(i_want_mapped_v4_addr); \ GLOBAL_ATOM_DEF(join_group); \ GLOBAL_ATOM_DEF(keepalive); \ @@ -321,9 +337,9 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(level); \ GLOBAL_ATOM_DEF(linger); \ GLOBAL_ATOM_DEF(link); \ - GLOBAL_ATOM_DEF(link0); \ - GLOBAL_ATOM_DEF(link1); \ - GLOBAL_ATOM_DEF(link2); \ + GLOBAL_ATOM_DEF(link0); \ + GLOBAL_ATOM_DEF(link1); \ + GLOBAL_ATOM_DEF(link2); \ GLOBAL_ATOM_DEF(local); \ GLOBAL_ATOM_DEF(localtlk); \ GLOBAL_ATOM_DEF(local_auth_chunks); \ @@ -335,6 +351,8 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(maxburst); \ GLOBAL_ATOM_DEF(maxseg); \ GLOBAL_ATOM_DEF(md5sig); \ + GLOBAL_ATOM_DEF(mem_end); \ + GLOBAL_ATOM_DEF(mem_start); \ GLOBAL_ATOM_DEF(metricom); \ GLOBAL_ATOM_DEF(mincost); \ GLOBAL_ATOM_DEF(minttl); \ @@ -362,8 +380,16 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(nopush); \ GLOBAL_ATOM_DEF(nosignal); \ GLOBAL_ATOM_DEF(notrailers); \ + GLOBAL_ATOM_DEF(not_bound); \ GLOBAL_ATOM_DEF(not_found); \ + GLOBAL_ATOM_DEF(num_general_errors); \ GLOBAL_ATOM_DEF(not_owner); \ + GLOBAL_ATOM_DEF(num_threads); \ + GLOBAL_ATOM_DEF(num_unexpected_accepts); \ + GLOBAL_ATOM_DEF(num_unexpected_connects); \ + GLOBAL_ATOM_DEF(num_unexpected_reads); \ + GLOBAL_ATOM_DEF(num_unexpected_writes); \ + GLOBAL_ATOM_DEF(num_unknown_cmds); \ GLOBAL_ATOM_DEF(oactive); \ GLOBAL_ATOM_DEF(ok); \ GLOBAL_ATOM_DEF(oob); \ @@ -406,6 +432,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(read_fails); \ GLOBAL_ATOM_DEF(read_pkg); \ GLOBAL_ATOM_DEF(read_tries); \ + GLOBAL_ATOM_DEF(read_waits); \ GLOBAL_ATOM_DEF(recv); \ GLOBAL_ATOM_DEF(recvdstaddr); \ GLOBAL_ATOM_DEF(recverr); \ @@ -460,6 +487,7 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(sndbufforce); \ GLOBAL_ATOM_DEF(sndlowat); \ GLOBAL_ATOM_DEF(sndtimeo); \ + GLOBAL_ATOM_DEF(sockaddr); \ GLOBAL_ATOM_DEF(socket); \ GLOBAL_ATOM_DEF(socket_tag); \ GLOBAL_ATOM_DEF(spec_dst); \ @@ -480,11 +508,13 @@ typedef long ssize_t; GLOBAL_ATOM_DEF(ttl); \ GLOBAL_ATOM_DEF(tunnel); \ GLOBAL_ATOM_DEF(tunnel6); \ + GLOBAL_ATOM_DEF(txqlen); \ GLOBAL_ATOM_DEF(type); \ GLOBAL_ATOM_DEF(udp); \ GLOBAL_ATOM_DEF(unblock_source); \ GLOBAL_ATOM_DEF(undefined); \ GLOBAL_ATOM_DEF(unicast_hops); \ + GLOBAL_ATOM_DEF(unknown); \ GLOBAL_ATOM_DEF(unspec); \ GLOBAL_ATOM_DEF(up); \ GLOBAL_ATOM_DEF(usec); \ @@ -508,11 +538,15 @@ typedef long ssize_t; * Error reason atoms */ -#define GLOBAL_ERROR_REASON_ATOM_DEFS \ - GLOBAL_ATOM_DEF(eagain); \ - GLOBAL_ATOM_DEF(einval); \ - GLOBAL_ATOM_DEF(select_read); \ - GLOBAL_ATOM_DEF(select_write) +#define GLOBAL_ERROR_REASON_ATOM_DEFS \ + GLOBAL_ATOM_DEF(add_socket); \ + GLOBAL_ATOM_DEF(create_accept_socket); \ + GLOBAL_ATOM_DEF(eagain); \ + GLOBAL_ATOM_DEF(einval); \ + GLOBAL_ATOM_DEF(select_read); \ + GLOBAL_ATOM_DEF(select_write); \ + GLOBAL_ATOM_DEF(update_accept_context); \ + GLOBAL_ATOM_DEF(update_connect_context) #define GLOBAL_ATOM_DEF(A) extern ERL_NIF_TERM esock_atom_##A @@ -537,6 +571,7 @@ GLOBAL_ERROR_REASON_ATOM_DEFS; #define MKL1(E,T) enif_make_list1((E), (T)) #define MKEL(E) enif_make_list((E), 0) #define MKC(E,H,T) enif_make_list_cell((E), (H), (T)) +#define MKEMA(E) enif_make_new_map((E)) #define MKMA(E,KA,VA,L,M) enif_make_map_from_arrays((E), (KA), (VA), (L), (M)) #define MKPID(E, P) enif_make_pid((E), (P)) #define MKREF(E) enif_make_ref((E)) @@ -568,6 +603,7 @@ GLOBAL_ERROR_REASON_ATOM_DEFS; #define COMPARE(A, B) enif_compare((A), (B)) #define COMPARE_PIDS(P1, P2) enif_compare_pids((P1), (P2)) +#define IS_ZERO(R) (COMPARE((R), esock_atom_zero) == 0) #define IS_ATOM(E, TE) enif_is_atom((E), (TE)) #define IS_BIN(E, TE) enif_is_binary((E), (TE)) @@ -577,6 +613,9 @@ GLOBAL_ERROR_REASON_ATOM_DEFS; #define IS_TUPLE(E, TE) enif_is_tuple((E), (TE)) #define IS_INTEGER(E, TE) esock_is_integer((E), (TE)) +#define IS_PID_UNDEF(P) enif_is_pid_undefined((P)) +#define SET_PID_UNDEF(P) enif_set_pid_undefined((P)) + #define GET_ATOM_LEN(E, TE, LP) \ enif_get_atom_length((E), (TE), (LP), ERL_NIF_LATIN1) #define GET_ATOM(E, TE, BP, MAX) \ @@ -600,7 +639,22 @@ GLOBAL_ERROR_REASON_ATOM_DEFS; #define REALLOC_BIN(SZ, BP) enif_realloc_binary((SZ), (BP)) #define FREE_BIN(BP) enif_release_binary((BP)) +#define FREE_IOVEC(IV) enif_free_iovec((IV)) + /* Copy term T into environment E */ -#define CP_TERM(E, T) enif_make_copy((E), (T)) +#define CP_TERM(E, T) enif_make_copy((E), (T)) + +#define CLEAR_ENV(E) esock_clear_env(__FUNCTION__, (E)) +#define FREE_ENV(E) esock_free_env(__FUNCTION__, (E)) + +#define TCREATE(NAME, TID, FUNC, ARGS, OPTS) \ + enif_thread_create((NAME), (TID), (FUNC), (ARGS), (OPTS)) +#define TEXIT(EVAL) enif_thread_exit((EVAL)) +#define TJOIN(TID, EV) enif_thread_join((TID), (EV)) +#define TOCREATE(NAME) enif_thread_opts_create((NAME)) +#define TODESTROY(OPTS) enif_thread_opts_destroy((OPTS)) + +#define ESOCK_PRINTF(...) enif_fprintf(stdout, __VA_ARGS__) +#define ESOCK_EPRINTF(...) enif_fprintf(stderr, __VA_ARGS__) #endif // SOCKET_INT_H__ diff --git a/erts/emulator/nifs/common/socket_io.h b/erts/emulator/nifs/common/socket_io.h index f5b2b2a875..100ca372be 100644 --- a/erts/emulator/nifs/common/socket_io.h +++ b/erts/emulator/nifs/common/socket_io.h @@ -33,11 +33,21 @@ #define ESOCK_IO_OK 0 #define ESOCK_IO_ERR_UNSUPPORTED -1 -typedef int (*ESockIOInit)(unsigned int numThreads); +typedef int (*ESockIOInit)(unsigned int numThreads, + const ESockData* dataP); typedef void (*ESockIOFinish)(void); +typedef ERL_NIF_TERM (*ESockIOInfo)(ErlNifEnv* env); +typedef ERL_NIF_TERM (*ESockIOCommand)(ErlNifEnv* env, + ERL_NIF_TERM command, + ERL_NIF_TERM cdata); +typedef ERL_NIF_TERM (*ESockIOSupports0)(ErlNifEnv* env); +typedef ERL_NIF_TERM (*ESockIOSupports1)(ErlNifEnv* env, + ERL_NIF_TERM key); + + typedef ERL_NIF_TERM (*ESockIOOpenWithFd)(ErlNifEnv* env, int fd, ERL_NIF_TERM eopts, @@ -150,5 +160,73 @@ typedef ERL_NIF_TERM (*ESockIOSockName)(ErlNifEnv* env, typedef ERL_NIF_TERM (*ESockIOPeerName)(ErlNifEnv* env, ESockDescriptor* descP); +typedef ERL_NIF_TERM (*ESockIOCancelConnect)(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); + +typedef ERL_NIF_TERM (*ESockIOCancelAccept)(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); + +typedef ERL_NIF_TERM (*ESockIOCancelSend)(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); + +typedef ERL_NIF_TERM (*ESockIOCancelRecv)(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); + +typedef ERL_NIF_TERM (*ESockIOSetopt)(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); +typedef ERL_NIF_TERM (*ESockIOSetoptNative)(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM eVal); +typedef ERL_NIF_TERM (*ESockIOSetoptOtp)(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt, + ERL_NIF_TERM eVal); + +typedef ERL_NIF_TERM (*ESockIOGetopt)(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt); +typedef ERL_NIF_TERM (*ESockIOGetoptNative)(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + int opt, + ERL_NIF_TERM valueSpec); +typedef ERL_NIF_TERM (*ESockIOGetoptOtp)(ErlNifEnv* env, + ESockDescriptor* descP, + int eOpt); + +typedef ERL_NIF_TERM (*ESockIOIoctl_2)(ErlNifEnv* env, + ESockDescriptor* descP, + unsigned long req); +typedef ERL_NIF_TERM (*ESockIOIoctl_3)(ErlNifEnv* env, + ESockDescriptor* descP, + unsigned long req, + ERL_NIF_TERM arg); +typedef ERL_NIF_TERM (*ESockIOIoctl_4)(ErlNifEnv* env, + ESockDescriptor* descP, + unsigned long req, + ERL_NIF_TERM arg1, + ERL_NIF_TERM arg2); + +typedef void (*ESockIODTor)(ErlNifEnv* env, + ESockDescriptor* descP); +typedef void (*ESockIOStop)(ErlNifEnv* env, + ESockDescriptor* descP); +typedef void (*ESockIODown)(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); #endif // SOCKET_IO_H__ diff --git a/erts/emulator/nifs/common/socket_syncio.h b/erts/emulator/nifs/common/socket_syncio.h index 15dac1af3e..2d548b8645 100644 --- a/erts/emulator/nifs/common/socket_syncio.h +++ b/erts/emulator/nifs/common/socket_syncio.h @@ -30,8 +30,10 @@ #include "socket_io.h" -extern int essio_init(unsigned int numThreads); +extern int essio_init(unsigned int numThreads, + const ESockData* dataP); extern void essio_finish(void); +extern ERL_NIF_TERM essio_info(ErlNifEnv* env); extern ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env, int fd, @@ -53,9 +55,11 @@ extern ERL_NIF_TERM essio_connect(ErlNifEnv* env, ERL_NIF_TERM connRef, ESockAddress* addrP, SOCKLEN_T addrLen); +/* extern ERL_NIF_TERM essio_listen(ErlNifEnv* env, ESockDescriptor* descP, int backlog); +*/ extern ERL_NIF_TERM essio_accept(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM sockRef, @@ -124,12 +128,47 @@ extern ERL_NIF_TERM essio_close(ErlNifEnv* env, ESockDescriptor* descP); extern ERL_NIF_TERM essio_fin_close(ErlNifEnv* env, ESockDescriptor* descP); -extern ERL_NIF_TERM essio_shutdown(ErlNifEnv* env, - ESockDescriptor* descP, - int how); -extern ERL_NIF_TERM essio_sockname(ErlNifEnv* env, - ESockDescriptor* descP); -extern ERL_NIF_TERM essio_peername(ErlNifEnv* env, - ESockDescriptor* descP); +extern ERL_NIF_TERM essio_cancel_connect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef); +extern ERL_NIF_TERM essio_cancel_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +extern ERL_NIF_TERM essio_cancel_send(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); +extern ERL_NIF_TERM essio_cancel_recv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef); + +extern ERL_NIF_TERM essio_ioctl2(ErlNifEnv* env, + ESockDescriptor* descP, + unsigned long req); +extern ERL_NIF_TERM essio_ioctl3(ErlNifEnv* env, + ESockDescriptor* descP, + unsigned long req, + ERL_NIF_TERM arg); +extern ERL_NIF_TERM essio_ioctl4(ErlNifEnv* env, + ESockDescriptor* descP, + unsigned long req, + ERL_NIF_TERM ename, + ERL_NIF_TERM eval); + +extern void essio_dtor(ErlNifEnv* env, + ESockDescriptor* descP); +extern void essio_stop(ErlNifEnv* env, + ESockDescriptor* descP); +extern void essio_down(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); + +/* Temporary (I hope) workaround */ +extern void essio_down_ctrl(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP); #endif // SOCKET_SYNCIO_H__ diff --git a/erts/emulator/nifs/common/socket_util.c b/erts/emulator/nifs/common/socket_util.c index efeb055240..8eeb98dba7 100644 --- a/erts/emulator/nifs/common/socket_util.c +++ b/erts/emulator/nifs/common/socket_util.c @@ -2286,15 +2286,15 @@ ERL_NIF_TERM esock_encode_bool(BOOLEAN_T val) /* *** esock_decode_level *** * - * Decode option or cmsg level - 'socket' or protocol number. + * Decode option or cmsg level - 'socket' or level number. * */ extern -BOOLEAN_T esock_decode_level(ErlNifEnv* env, ERL_NIF_TERM eVal, int *val) +BOOLEAN_T esock_decode_level(ErlNifEnv* env, ERL_NIF_TERM elevel, int *level) { - if (COMPARE(esock_atom_socket, eVal) == 0) - *val = SOL_SOCKET; - else if (! GET_INT(env, eVal, val)) + if (COMPARE(esock_atom_socket, elevel) == 0) + *level = SOL_SOCKET; + else if (! GET_INT(env, elevel, level)) return FALSE; return TRUE; @@ -2329,6 +2329,101 @@ ERL_NIF_TERM esock_make_ok2(ErlNifEnv* env, ERL_NIF_TERM any) } +/* Takes an 'errno' value and converts it to a term. + * + * If the errno can be translated using erl_errno_id, + * then we use that value otherwise we use the errno + * integer value converted to a term. + * Unless there is a specific error code that can be + * handled specially. + */ +extern +ERL_NIF_TERM esock_errno_to_term(ErlNifEnv* env, int err) +{ + switch (err) { +#if defined(NO_ERROR) + case NO_ERROR: + return MKA(env, "no_error"); + break; +#endif + +#if defined(WSA_IO_PENDING) + case WSA_IO_PENDING: + return MKA(env, "io_pending"); + break; +#endif + +#if defined(WSA_IO_INCOMPLETE) + case WSA_IO_INCOMPLETE: + return MKA(env, "io_incomplete"); + break; +#endif + +#if defined(WSA_OPERATION_ABORTED) + case WSA_OPERATION_ABORTED: + return MKA(env, "operation_aborted"); + break; +#endif + +#if defined(WSA_INVALID_PARAMETER) + case WSA_INVALID_PARAMETER: + return MKA(env, "invalid_parameter"); + break; +#endif + + default: + { + char* str = erl_errno_id(err); + if ( strcmp(str, "unknown") == 0 ) + return MKI(env, err); + else + return MKA(env, str); + } + break; + } + + /* This is just in case of programming error. + * We should not get this far! + */ + return MKI(env, err); +} + + + +/* *** esock_make_extra_error_info_term *** + * This is used primarily for debugging. + * Is supposed to be called via the 'MKEEI' macro. + */ +extern +ERL_NIF_TERM esock_make_extra_error_info_term(ErlNifEnv* env, + const char* file, + const char* function, + const int line, + ERL_NIF_TERM rawinfo, + ERL_NIF_TERM info) +{ + ERL_NIF_TERM keys[] = {MKA(env, "file"), + MKA(env, "function"), + MKA(env, "line"), + MKA(env, "raw_info"), + MKA(env, "info")}; + ERL_NIF_TERM vals[] = {MKS(env, file), + MKS(env, function), + MKI(env, line), + rawinfo, + info}; + unsigned int numKeys = NUM(keys); + unsigned int numVals = NUM(vals); + ERL_NIF_TERM map; + + ESOCK_ASSERT( numKeys == numVals ); + ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &map) ); + + return map; +} + + + /* Create an error two (2) tuple in the form: * * {error, Reason} @@ -2387,6 +2482,23 @@ ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err) /* Create an error two (2) tuple in the form: * + * {error, {Tag, Reason}} + * + * Both 'Tag' and 'Reason' are already in the form of an + * ERL_NIF_TERM so all we have to do is create "the" tuple. + */ +extern +ERL_NIF_TERM esock_make_error_t2r(ErlNifEnv* env, + ERL_NIF_TERM tag, + ERL_NIF_TERM reason) +{ + return MKT2(env, esock_atom_error, MKT2(env, tag, reason)); +} + + + +/* Create an error two (2) tuple in the form: + * * {error, {invalid, What}}} */ extern diff --git a/erts/emulator/nifs/common/socket_util.h b/erts/emulator/nifs/common/socket_util.h index 7f77630845..04f0cc3d94 100644 --- a/erts/emulator/nifs/common/socket_util.h +++ b/erts/emulator/nifs/common/socket_util.h @@ -46,6 +46,31 @@ #define ESOCK_ABORT(E) esock_abort(E, __func__, __FILE__, __LINE__) #define ESOCK_ASSERT(e) ((void) ((e) ? 1 : (ESOCK_ABORT(#e), 0))) +#define MKEEI(E, RI, I) \ + esock_make_extra_error_info_term((E), \ + __FILE__, \ + __FUNCTION__, \ + __LINE__, \ + (RI), (I)) + +#define ESOCK_VERBOSE_ERRNO 1 +#if defined(ESOCK_VERBOSE_ERRNO) +#define ENO2T(E, ENO) MKEEI((E), \ + MKI((E), (ENO)), \ + esock_errno_to_term((E), (ENO))) +#else +#define ENO2T(E, ENO) esock_errno_to_term((E), (ENO)) +#endif + + +extern +ERL_NIF_TERM esock_make_extra_error_info_term(ErlNifEnv* env, + const char* file, + const char* function, + const int line, + ERL_NIF_TERM rawinfo, + ERL_NIF_TERM info); + extern unsigned int esock_get_uint_from_map(ErlNifEnv* env, ERL_NIF_TERM map, @@ -247,6 +272,8 @@ ERL_NIF_TERM esock_self(ErlNifEnv* env); extern ERL_NIF_TERM esock_make_ok2(ErlNifEnv* env, ERL_NIF_TERM any); extern +ERL_NIF_TERM esock_errno_to_term(ErlNifEnv* env, int err); +extern ERL_NIF_TERM esock_make_error(ErlNifEnv* env, ERL_NIF_TERM reason); extern ERL_NIF_TERM esock_make_error_closed(ErlNifEnv* env); @@ -255,6 +282,9 @@ ERL_NIF_TERM esock_make_error_str(ErlNifEnv* env, char* reason); extern ERL_NIF_TERM esock_make_error_errno(ErlNifEnv* env, int err); extern +ERL_NIF_TERM esock_make_error_t2r(ErlNifEnv* env, + ERL_NIF_TERM tag, ERL_NIF_TERM reason); +extern ERL_NIF_TERM esock_make_error_invalid(ErlNifEnv* env, ERL_NIF_TERM what); extern ERL_NIF_TERM esock_make_error_integer_range(ErlNifEnv* env, ERL_NIF_TERM i); diff --git a/erts/emulator/nifs/unix/unix_socket_syncio.c b/erts/emulator/nifs/unix/unix_socket_syncio.c index 88f45a7b51..3e3cfcaeef 100644 --- a/erts/emulator/nifs/unix/unix_socket_syncio.c +++ b/erts/emulator/nifs/unix/unix_socket_syncio.c @@ -45,6 +45,16 @@ #endif #include "sys.h" +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include <sys/ioctl.h> +#endif + +#include <net/if.h> + #include "prim_socket_int.h" #include "socket_util.h" #include "socket_io.h" @@ -66,12 +76,12 @@ #define sock_accept(s, addr, len) accept((s), (addr), (len)) #endif #define sock_bind(s, addr, len) bind((s), (addr), (len)) -// #define sock_close(s) close((s)) +#define sock_close(s) close((s)) // #define sock_close_event(e) /* do nothing */ #define sock_connect(s, addr, len) connect((s), (addr), (len)) #define sock_errno() errno -#define sock_listen(s, b) listen((s), (b)) -#define sock_name(s, addr, len) getsockname((s), (addr), (len)) +// #define sock_listen(s, b) listen((s), (b)) +// #define sock_name(s, addr, len) getsockname((s), (addr), (len)) #define sock_open(domain, type, proto) socket((domain), (type), (proto)) #define sock_peer(s, addr, len) getpeername((s), (addr), (len)) #define sock_recv(s,buf,len,flag) recv((s),(buf),(len),(flag)) @@ -85,21 +95,38 @@ #define sock_shutdown(s, how) shutdown((s), (how)) +/* =================================================================== * + * * + * Various esaio macros * + * * + * =================================================================== */ + +/* Global socket debug */ +#define SGDBG( proto ) ESOCK_DBG_PRINTF( ctrl.dbg , proto ) + + +/* =================================================================== * + * * + * Local types * + * * + * =================================================================== */ + +typedef struct { + /* Misc stuff */ + BOOLEAN_T dbg; + BOOLEAN_T sockDbg; +} ESSIOControl; + + + /* ======================================================================== * * Function Forwards * * ======================================================================== * */ -static BOOLEAN_T open_is_debug(ErlNifEnv* env, - ERL_NIF_TERM eopts, - BOOLEAN_T def); -static BOOLEAN_T open_use_registry(ErlNifEnv* env, - ERL_NIF_TERM eopts, - BOOLEAN_T def); static BOOLEAN_T open_todup(ErlNifEnv* env, ERL_NIF_TERM eopts); static BOOLEAN_T open_which_domain(SOCKET sock, int* domain); static BOOLEAN_T open_which_type(SOCKET sock, int* type); -static BOOLEAN_T open_which_protocol(SOCKET sock, int* proto); static BOOLEAN_T open_get_domain(ErlNifEnv* env, ERL_NIF_TERM eopts, int* domain); @@ -122,6 +149,28 @@ static BOOLEAN_T restore_network_namespace(BOOLEAN_T dbg, static BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err); +static ERL_NIF_TERM essio_cancel_accept_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM essio_cancel_accept_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef, + const ErlNifPid* selfP); +static ERL_NIF_TERM essio_cancel_send_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM essio_cancel_send_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef, + const ErlNifPid* selfP); +static ERL_NIF_TERM essio_cancel_recv_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM essio_cancel_recv_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef, + const ErlNifPid* selfP); + static ERL_NIF_TERM essio_accept_listening_error(ErlNifEnv* env, ESockDescriptor* descP, ERL_NIF_TERM sockRef, @@ -228,7 +277,7 @@ static void encode_msg(ErlNifEnv* env, struct msghdr* msgHdrP, ErlNifBinary* dataBufP, ErlNifBinary* ctrlBufP, - ERL_NIF_TERM* eSockAddr); + ERL_NIF_TERM* eMsg); static void encode_cmsgs(ErlNifEnv* env, ESockDescriptor* descP, ErlNifBinary* cmsgBinP, @@ -366,6 +415,266 @@ static ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env, ERL_NIF_TERM sockRef); +static ERL_NIF_TERM essio_ioctl_gifconf(ErlNifEnv* env, + ESockDescriptor* descP); +#if defined(SIOCGIFNAME) +static ERL_NIF_TERM essio_ioctl_gifname(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eidx); +#endif + +/* esock_ioctl_gifindex */ +#if defined(SIOCGIFINDEX) +#define IOCTL_GIFINDEX_FUNC_DEF IOCTL_GET_FUNC_DEF(gifindex) +#else +#define IOCTL_GIFINDEX_FUNC_DEF +#endif + +/* esock_ioctl_gifflags */ +#if defined(SIOCGIFFLAGS) +#define IOCTL_GIFFLAGS_FUNC_DEF IOCTL_GET_FUNC_DEF(gifflags) +#else +#define IOCTL_GIFFLAGS_FUNC_DEF +#endif + +/* esock_ioctl_gifaddr */ +#if defined(SIOCGIFADDR) +#define IOCTL_GIFADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifaddr) +#else +#define IOCTL_GIFADDR_FUNC_DEF +#endif + +/* esock_ioctl_gifdstaddr */ +#if defined(SIOCGIFDSTADDR) +#define IOCTL_GIFDSTADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifdstaddr) +#else +#define IOCTL_GIFDSTADDR_FUNC_DEF +#endif + +/* esock_ioctl_gifbrdaddr */ +#if defined(SIOCGIFBRDADDR) +#define IOCTL_GIFBRDADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifbrdaddr) +#else +#define IOCTL_GIFBRDADDR_FUNC_DEF +#endif + +/* esock_ioctl_gifnetmask */ +#if defined(SIOCGIFNETMASK) +#define IOCTL_GIFNETMASK_FUNC_DEF IOCTL_GET_FUNC_DEF(gifnetmask) +#else +#define IOCTL_GIFNETMASK_FUNC_DEF +#endif + +/* esock_ioctl_gifmtu */ +#if defined(SIOCGIFMTU) +#define IOCTL_GIFMTU_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmtu) +#else +#define IOCTL_GIFMTU_FUNC_DEF +#endif + +/* esock_ioctl_gifhwaddr */ +#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) +#define IOCTL_GIFHWADDR_FUNC_DEF IOCTL_GET_FUNC_DEF(gifhwaddr) +#else +#define IOCTL_GIFHWADDR_FUNC_DEF +#endif + +/* esock_ioctl_gifmap */ +#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) +#define IOCTL_GIFMAP_FUNC_DEF IOCTL_GET_FUNC_DEF(gifmap) +#else +#define IOCTL_GIFMAP_FUNC_DEF +#endif + +/* esock_ioctl_giftxqlen */ +#if defined(SIOCGIFTXQLEN) +#define IOCTL_GIFTXQLEN_FUNC_DEF IOCTL_GET_FUNC_DEF(giftxqlen) +#else +#define IOCTL_GIFTXQLEN_FUNC_DEF +#endif + +#define IOCTL_GET_FUNCS_DEF \ + IOCTL_GIFINDEX_FUNC_DEF; \ + IOCTL_GIFFLAGS_FUNC_DEF; \ + IOCTL_GIFADDR_FUNC_DEF; \ + IOCTL_GIFDSTADDR_FUNC_DEF; \ + IOCTL_GIFBRDADDR_FUNC_DEF; \ + IOCTL_GIFNETMASK_FUNC_DEF; \ + IOCTL_GIFMTU_FUNC_DEF; \ + IOCTL_GIFHWADDR_FUNC_DEF; \ + IOCTL_GIFMAP_FUNC_DEF; \ + IOCTL_GIFTXQLEN_FUNC_DEF; +#define IOCTL_GET_FUNC_DEF(F) \ + static ERL_NIF_TERM essio_ioctl_##F(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM ename) +IOCTL_GET_FUNCS_DEF +#undef IOCTL_GET_FUNC_DEF + +/* esock_ioctl_sifflags */ +#if defined(SIOCSIFFLAGS) +#define IOCTL_SIFFLAGS_FUNC_DEF IOCTL_SET_FUNC_DEF(sifflags) +#else +#define IOCTL_SIFFLAGS_FUNC_DEF +#endif + +/* esock_ioctl_sifaddr */ +#if defined(SIOCSIFADDR) +#define IOCTL_SIFADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifaddr) +#else +#define IOCTL_SIFADDR_FUNC_DEF +#endif + +/* esock_ioctl_sifdstaddr */ +#if defined(SIOCSIFDSTADDR) +#define IOCTL_SIFDSTADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifdstaddr) +#else +#define IOCTL_SIFDSTADDR_FUNC_DEF +#endif + +/* esock_ioctl_sifbrdaddr */ +#if defined(SIOCSIFBRDADDR) +#define IOCTL_SIFBRDADDR_FUNC_DEF IOCTL_SET_FUNC_DEF(sifbrdaddr) +#else +#define IOCTL_SIFBRDADDR_FUNC_DEF +#endif + +/* esock_ioctl_sifnetmask */ +#if defined(SIOCSIFNETMASK) +#define IOCTL_SIFNETMASK_FUNC_DEF IOCTL_SET_FUNC_DEF(sifnetmask) +#else +#define IOCTL_SIFNETMASK_FUNC_DEF +#endif + +/* esock_ioctl_sifmtu */ +#if defined(SIOCSIFMTU) +#define IOCTL_SIFMTU_FUNC_DEF IOCTL_SET_FUNC_DEF(sifmtu) +#else +#define IOCTL_SIFMTU_FUNC_DEF +#endif + +/* esock_ioctl_siftxqlen */ +#if defined(SIOCSIFTXQLEN) +#define IOCTL_SIFTXQLEN_FUNC_DEF IOCTL_SET_FUNC_DEF(siftxqlen) +#else +#define IOCTL_SIFTXQLEN_FUNC_DEF +#endif + +#define IOCTL_SET_FUNCS_DEF \ + IOCTL_SIFFLAGS_FUNC_DEF; \ + IOCTL_SIFADDR_FUNC_DEF; \ + IOCTL_SIFDSTADDR_FUNC_DEF; \ + IOCTL_SIFBRDADDR_FUNC_DEF; \ + IOCTL_SIFNETMASK_FUNC_DEF; \ + IOCTL_SIFMTU_FUNC_DEF; \ + IOCTL_SIFTXQLEN_FUNC_DEF; +#define IOCTL_SET_FUNC_DEF(F) \ + static ERL_NIF_TERM essio_ioctl_##F(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM ename, \ + ERL_NIF_TERM evalue) +IOCTL_SET_FUNCS_DEF +#undef IOCTL_SET_FUNC_DEF + + +static ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env, + ESockDescriptor* descP, + struct ifconf* ifcP); +static ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env, + ESockDescriptor* descP, + struct ifreq* ifrP); +static ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env, + char* name); +static ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env, + struct sockaddr* sa); +static ERL_NIF_TERM make_ifreq(ErlNifEnv* env, + ERL_NIF_TERM name, + ERL_NIF_TERM key2, + ERL_NIF_TERM val2); +#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) +static ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env, + ESockDescriptor* descP, + struct ifmap* mapP); +#endif +#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) +static ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env, + ESockDescriptor* descP, + struct sockaddr* addrP); +#endif +static ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env, + ESockDescriptor* descP, + struct sockaddr* addrP); +static ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env, + ESockDescriptor* descP, + short flags); +#if defined(SIOCSIFFLAGS) +static BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eflags, + short* flags); +#endif +static BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eaddr, + ESockAddress* addr); +#if defined(SIOCSIFMTU) +static BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM emtu, + int* mtu); +#endif +#if defined(SIOCSIFTXQLEN) +static BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM etxqlen, + int* txqlen); +#endif +#if defined(SIOCSIFTXQLEN) +static BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eivalue, + int* ivalue); +#endif +static ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env, + ESockDescriptor* descP, + int ivalue); + + +/* +static void essio_down_ctrl(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP); +*/ +static void essio_down_acceptor(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); +static void essio_down_writer(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); +static void essio_down_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); + +static BOOLEAN_T do_stop(ErlNifEnv* env, + ESockDescriptor* descP); + + +/* =================================================================== * + * * + * Local (global) variables * + * * + * =================================================================== */ + +static ESSIOControl ctrl = {0}; + + + /* ======================================================================== * * ESSIO Functions * * ======================================================================== * @@ -376,10 +685,14 @@ static ERL_NIF_TERM recvmsg_check_msg(ErlNifEnv* env, * this is just a dummy function. */ extern -int essio_init(unsigned int numThreads) +int essio_init(unsigned int numThreads, + const ESockData* dataP) { VOID(numThreads); + ctrl.dbg = dataP->dbg; + ctrl.sockDbg = dataP->sockDbg; + return ESOCK_IO_OK; } @@ -395,6 +708,28 @@ void essio_finish(void) } + +/* ******************************************************************* + * essio_info - Return info "about" this I/O backend. + */ + +extern +ERL_NIF_TERM essio_info(ErlNifEnv* env) +{ + ERL_NIF_TERM info; + ERL_NIF_TERM keys[] = {esock_atom_name}; + ERL_NIF_TERM vals[] = {MKA(env, "unix_essio")}; + unsigned int numKeys = NUM(keys); + unsigned int numVals = NUM(vals); + + ESOCK_ASSERT( numKeys == numVals ); + ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &info) ); + + return info; +} + + + /* ======================================================================== * essio_open - create an endpoint (from an existing fd) for communication * @@ -410,15 +745,14 @@ ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env, ERL_NIF_TERM eopts, const ESockData* dataP) { - BOOLEAN_T dbg = open_is_debug(env, eopts, dataP->sockDbg); - BOOLEAN_T useReg = open_use_registry(env, eopts, dataP->useReg); + BOOLEAN_T dbg = esock_open_is_debug(env, eopts, dataP->sockDbg); + BOOLEAN_T useReg = esock_open_use_registry(env, eopts, dataP->useReg); ESockDescriptor* descP; ERL_NIF_TERM sockRef; int domain, type, protocol; int save_errno = 0; BOOLEAN_T closeOnClose; SOCKET sock; - ErlNifEvent event; ErlNifPid self; /* Keep track of the creator @@ -464,7 +798,7 @@ ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env, return esock_make_invalid(env, esock_atom_type); } - if (! open_which_protocol(fd, &protocol)) { + if (! esock_open_which_protocol(fd, &protocol)) { SSDBG2( dbg, ("UNIX-ESSIO", "essio_open2 -> failed get protocol from system\r\n") ); @@ -506,12 +840,11 @@ ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env, closeOnClose = FALSE; } - event = sock; SET_NONBLOCKING(sock); /* Create and initiate the socket "descriptor" */ - descP = esock_alloc_descriptor(sock, event); + descP = esock_alloc_descriptor(sock); descP->ctrlPid = self; descP->domain = domain; descP->type = type; @@ -560,23 +893,6 @@ ERL_NIF_TERM essio_open_with_fd(ErlNifEnv* env, static -BOOLEAN_T open_is_debug(ErlNifEnv* env, - ERL_NIF_TERM eopts, - BOOLEAN_T def) -{ - return esock_get_bool_from_map(env, eopts, esock_atom_debug, def); -} - -static -BOOLEAN_T open_use_registry(ErlNifEnv* env, - ERL_NIF_TERM eopts, - BOOLEAN_T def) -{ - return esock_get_bool_from_map(env, eopts, esock_atom_use_registry, def); -} - - -static BOOLEAN_T open_which_domain(SOCKET sock, int* domain) { #if defined(SO_DOMAIN) @@ -633,16 +949,6 @@ BOOLEAN_T open_get_type(ErlNifEnv* env, return TRUE; } -static -BOOLEAN_T open_which_protocol(SOCKET sock, int* proto) -{ -#if defined(SO_PROTOCOL) - if (esock_getopt_int(sock, SOL_SOCKET, SO_PROTOCOL, proto)) - return TRUE; -#endif - return FALSE; -} - /* The eopts contains an integer 'type' key. */ static @@ -674,8 +980,8 @@ ERL_NIF_TERM essio_open_plain(ErlNifEnv* env, ERL_NIF_TERM eopts, const ESockData* dataP) { - BOOLEAN_T dbg = open_is_debug(env, eopts, dataP->sockDbg); - BOOLEAN_T useReg = open_use_registry(env, eopts, dataP->useReg); + BOOLEAN_T dbg = esock_open_is_debug(env, eopts, dataP->sockDbg); + BOOLEAN_T useReg = esock_open_use_registry(env, eopts, dataP->useReg); ESockDescriptor* descP; ERL_NIF_TERM sockRef; int proto = protocol; @@ -735,7 +1041,7 @@ ERL_NIF_TERM essio_open_plain(ErlNifEnv* env, */ if (proto == 0) - (void) open_which_protocol(sock, &proto); + (void) esock_open_which_protocol(sock, &proto); #ifdef HAVE_SETNS if (netns != NULL) { @@ -750,7 +1056,7 @@ ERL_NIF_TERM essio_open_plain(ErlNifEnv* env, /* Create and initiate the socket "descriptor" */ - descP = esock_alloc_descriptor(sock, sock); + descP = esock_alloc_descriptor(sock); descP->ctrlPid = self; descP->domain = domain; descP->type = type; @@ -766,7 +1072,7 @@ ERL_NIF_TERM essio_open_plain(ErlNifEnv* env, descP->dbg = dbg; descP->useReg = useReg; - esock_inc_socket(domain, type, protocol); + esock_inc_socket(domain, type, proto); /* And finally (maybe) update the registry */ if (descP->useReg) esock_send_reg_add_msg(env, descP, sockRef); @@ -1008,7 +1314,7 @@ ERL_NIF_TERM essio_connect(ErlNifEnv* env, MKI(env, sres))); /* Initiate connector */ descP->connector.pid = self; - ESOCK_ASSERT( MONP("esock_connect -> conn", + ESOCK_ASSERT( MONP("essio_connect -> conn", env, descP, &self, &descP->connector.mon) == 0 ); descP->connector.env = esock_alloc_env("connector"); @@ -1084,33 +1390,7 @@ BOOLEAN_T verify_is_connected(ESockDescriptor* descP, int* err) -/* ======================================================================== - */ -extern -ERL_NIF_TERM essio_listen(ErlNifEnv* env, - ESockDescriptor* descP, - int backlog) -{ - - /* - * Verify that we are in the proper state - */ - - if (! IS_OPEN(descP->readState)) - return esock_make_error_closed(env); - - /* - * And attempt to make socket listening - */ - - if ((sock_listen(descP->sock, backlog)) < 0) - return esock_make_error_errno(env, sock_errno()); - - descP->readState |= ESOCK_STATE_LISTENING; - - return esock_atom_ok; - -} +/* *** essio_listen *** */ /* ======================================================================== @@ -1121,7 +1401,7 @@ ERL_NIF_TERM essio_accept(ErlNifEnv* env, ERL_NIF_TERM sockRef, ERL_NIF_TERM accRef) { - ErlNifPid caller; + ErlNifPid caller; ESOCK_ASSERT( enif_self(env, &caller) != NULL ); @@ -1135,7 +1415,7 @@ ERL_NIF_TERM essio_accept(ErlNifEnv* env, return esock_make_error_invalid(env, esock_atom_state); if (descP->currentAcceptorP == NULL) { - SOCKET accSock; + SOCKET accSock; /* We have no active acceptor (and therefore no acceptors in queue) */ @@ -1454,7 +1734,7 @@ ERL_NIF_TERM essio_accept_accepting_other(ErlNifEnv* env, ErlNifPid caller) { if (! esock_acceptor_search4pid(env, descP, &caller)) { - esock_acceptor_push(env, descP, caller, ref); + esock_acceptor_push(env, descP, caller, ref, NULL); return esock_atom_select; } else { /* Acceptor already in queue */ @@ -1536,7 +1816,7 @@ BOOLEAN_T essio_accept_accepted(ErlNifEnv* env, ESOCK_CNT_INC(env, descP, sockRef, esock_atom_acc_success, &descP->accSuccess, 1); - accDescP = esock_alloc_descriptor(accSock, accSock); + accDescP = esock_alloc_descriptor(accSock); accDescP->domain = descP->domain; accDescP->type = descP->type; accDescP->protocol = descP->protocol; @@ -1567,7 +1847,7 @@ BOOLEAN_T essio_accept_accepted(ErlNifEnv* env, SET_NONBLOCKING(accDescP->sock); - descP->writeState |= ESOCK_STATE_CONNECTED; + accDescP->writeState |= ESOCK_STATE_CONNECTED; MUNLOCK(descP->writeMtx); @@ -1843,7 +2123,7 @@ ERL_NIF_TERM essio_sendmsg(ErlNifEnv* env, } SSDBG( descP, ("UNIX-ESSIO", - "essio_sendmsg {%d} ->" + "essio_sendmsg {%d} -> iovec size verified" "\r\n iov length: %lu" "\r\n data size: %u" "\r\n", @@ -1899,13 +2179,14 @@ ERL_NIF_TERM essio_sendmsg(ErlNifEnv* env, sockRef, sendRef); done_free_iovec: - enif_free_iovec(iovec); + FREE_IOVEC( iovec ); if (ctrlBuf != NULL) FREE(ctrlBuf); SSDBG( descP, - ("UNIX-ESSIO", "essio_sendmsg {%d} ->" + ("UNIX-ESSIO", "essio_sendmsg {%d} -> done" "\r\n %T" "\r\n", descP->sock, res) ); + return res; } @@ -2764,7 +3045,7 @@ ERL_NIF_TERM essio_close(ErlNifEnv* env, /* Prepare for closing the socket */ descP->readState |= ESOCK_STATE_CLOSING; descP->writeState |= ESOCK_STATE_CLOSING; - if (esock_do_stop(env, descP)) { + if (do_stop(env, descP)) { // stop() has been scheduled - wait for it SSDBG( descP, ("UNIX-ESSIO", "essio_close {%d} -> stop was scheduled\r\n", @@ -2787,6 +3068,177 @@ ERL_NIF_TERM essio_close(ErlNifEnv* env, } + +/* Prepare for close - return whether stop is scheduled or not + */ +static +BOOLEAN_T do_stop(ErlNifEnv* env, + ESockDescriptor* descP) +{ + BOOLEAN_T ret; + int sres; + ERL_NIF_TERM sockRef; + + sockRef = enif_make_resource(env, descP); + + if (IS_SELECTED(descP)) { + ESOCK_ASSERT( (sres = esock_select_stop(env, + (ErlNifEvent) descP->sock, + descP)) + >= 0 ); + if ((sres & ERL_NIF_SELECT_STOP_CALLED) != 0) { + /* The socket is no longer known by the select machinery + * - it may be closed + */ + ret = FALSE; + } else { + ESOCK_ASSERT( (sres & ERL_NIF_SELECT_STOP_SCHEDULED) != 0 ); + /* esock_stop() is scheduled + * - socket may be removed by esock_stop() or later + */ + ret = TRUE; + } + } else { + sres = 0; + /* The socket has never been used in the select machinery + * - it may be closed + */ + ret = FALSE; + } + + /* +++++++ Current and waiting Writers +++++++ */ + + if (descP->currentWriterP != NULL) { + + /* We have a current Writer; was it deselected? + */ + + if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) { + + /* The current Writer will not get a select message + * - send it an abort message + */ + + esock_stop_handle_current(env, + "writer", + descP, sockRef, &descP->currentWriter); + } + + /* Inform the waiting Writers (in the same way) */ + + SSDBG( descP, + ("UNIX-ESSIO", + "do_stop {%d} -> handle waiting writer(s)\r\n", + descP->sock) ); + + esock_inform_waiting_procs(env, "writer", + descP, sockRef, &descP->writersQ, + esock_atom_closed); + + descP->currentWriterP = NULL; + } + + /* +++++++ Connector +++++++ + * Note that there should not be Writers and a Connector + * at the same time so the check for if the + * current Writer/Connecter was deselected is only correct + * under that assumption + */ + + if (descP->connectorP != NULL) { + + /* We have a Connector; was it deselected? + */ + + if (sres & ERL_NIF_SELECT_WRITE_CANCELLED) { + + /* The Connector will not get a select message + * - send it an abort message + */ + + esock_stop_handle_current(env, + "connector", + descP, sockRef, &descP->connector); + } + + descP->connectorP = NULL; + } + + /* +++++++ Current and waiting Readers +++++++ */ + + if (descP->currentReaderP != NULL) { + + /* We have a current Reader; was it deselected? + */ + + if (sres & ERL_NIF_SELECT_READ_CANCELLED) { + + /* The current Reader will not get a select message + * - send it an abort message + */ + + esock_stop_handle_current(env, + "reader", + descP, sockRef, &descP->currentReader); + } + + /* Inform the Readers (in the same way) */ + + SSDBG( descP, + ("UNIX-ESSIO", + "do_stop {%d} -> handle waiting reader(s)\r\n", + descP->sock) ); + + esock_inform_waiting_procs(env, "writer", + descP, sockRef, &descP->readersQ, + esock_atom_closed); + + descP->currentReaderP = NULL; + } + + /* +++++++ Current and waiting Acceptors +++++++ + * + * Note that there should not be Readers and Acceptors + * at the same time so the check for if the + * current Reader/Acceptor was deselected is only correct + * under that assumption + */ + + if (descP->currentAcceptorP != NULL) { + + /* We have a current Acceptor; was it deselected? + */ + + if (sres & ERL_NIF_SELECT_READ_CANCELLED) { + + /* The current Acceptor will not get a select message + * - send it an abort message + */ + + esock_stop_handle_current(env, + "acceptor", + descP, sockRef, &descP->currentAcceptor); + } + + /* Inform the waiting Acceptor (in the same way) */ + + SSDBG( descP, + ("UNIX-ESSIO", + "do_stop {%d} -> handle waiting acceptors(s)\r\n", + descP->sock) ); + + esock_inform_waiting_procs(env, "acceptor", + descP, sockRef, &descP->acceptorsQ, + esock_atom_closed); + + descP->currentAcceptorP = NULL; + } + + return ret; +} + + + /* ======================================================================== * Perform the final step in the socket close. */ @@ -2876,102 +3328,1560 @@ ERL_NIF_TERM essio_fin_close(ErlNifEnv* env, /* ======================================================================== + * *** essio_shutdown should go here - if we need one *** + */ + + +/* ======================================================================== + * *** essio_sockname should go here - if we need one *** */ + + +/* ======================================================================== + * *** essio_peername should go here - if we need one *** + */ + + +/* ======================================================================== + * Cancel a connect request. + */ + extern -ERL_NIF_TERM essio_shutdown(ErlNifEnv* env, - ESockDescriptor* descP, - int how) +ERL_NIF_TERM essio_cancel_connect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef) { - if (! IS_OPEN(descP->readState)) - return esock_make_error_closed(env); + ERL_NIF_TERM res; + ErlNifPid self; + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_cancel_connect {%d} -> entry with" + "\r\n writeState: 0x%X" + "\r\n opRef: %T" + "\r\n", + descP->sock, descP->writeState, opRef) ); + + ESOCK_ASSERT( enif_self(env, &self) != NULL ); + + if (! IS_OPEN(descP->writeState)) { + + res = esock_make_error_closed(env); + + } else if ((descP->connectorP == NULL) || + (COMPARE_PIDS(&self, &descP->connector.pid) != 0) || + (COMPARE(opRef, descP->connector.ref) != 0)) { + + res = esock_make_error(env, esock_atom_not_found); + + } else { + + res = esock_cancel_write_select(env, descP, opRef); + esock_requestor_release("esock_cancel_connect", + env, descP, &descP->connector); + descP->connectorP = NULL; + descP->writeState &= ~ESOCK_STATE_CONNECTING; + } + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_cancel_connect {%d} -> done when" + "\r\n res: %T" + "\r\n", + descP->sock, descP->writeState, + opRef, res) ); + + return res; +} + + + +/* ======================================================================== + * Cancel accept request + * + * We have two different cases: + * *) Its the current acceptor + * Cancel the select! + * We need to activate one of the waiting acceptors. + * *) Its one of the acceptors ("waiting") in the queue + * Simply remove the acceptor from the queue. + * + */ +extern +ERL_NIF_TERM essio_cancel_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) +{ + ERL_NIF_TERM res; + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_cancel_accept(%T), {%d,0x%X} ->" + "\r\n opRef: %T" + "\r\n %s" + "\r\n", + sockRef, descP->sock, descP->readState, + opRef, + ((descP->currentAcceptorP == NULL) + ? "without acceptor" : "with acceptor")) ); + + if (! IS_OPEN(descP->readState)) { + + res = esock_make_error_closed(env); + + } else if (descP->currentAcceptorP == NULL) { + + res = esock_atom_not_found; + + } else { + ErlNifPid self; + + ESOCK_ASSERT( enif_self(env, &self) != NULL ); + + if (COMPARE_PIDS(&self, &descP->currentAcceptor.pid) == 0) { + if (COMPARE(opRef, descP->currentAcceptor.ref) == 0) + res = essio_cancel_accept_current(env, descP, sockRef); + else + res = esock_atom_not_found; + } else { + res = essio_cancel_accept_waiting(env, descP, opRef, &self); + } + } + + SSDBG( descP, + ("UNIX-ESSIO", "essio_cancel_accept(%T) -> done with result:" + "\r\n %T" + "\r\n", sockRef, res) ); + + return res; +} + + +/* The current acceptor process has an ongoing select we first must + * cancel. Then we must re-activate the "first" (the first + * in the acceptor queue). + */ +static +ERL_NIF_TERM essio_cancel_accept_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM res; + + ESOCK_ASSERT( DEMONP("essio_cancel_accept_current -> current acceptor", + env, descP, &descP->currentAcceptor.mon) == 0); + MON_INIT(&descP->currentAcceptor.mon); + res = esock_cancel_read_select(env, descP, descP->currentAcceptor.ref); - if (sock_shutdown(descP->sock, how) == 0) + SSDBG( descP, + ("UNIX-ESSIO", + "essio_cancel_accept_current(%T) {%d} -> cancel res: %T" + "\r\n", sockRef, descP->sock, res) ); + + if (!esock_activate_next_acceptor(env, descP, sockRef)) { + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_cancel_accept_current(%T) {%d} -> " + "no more acceptors\r\n", + sockRef, descP->sock) ); + + descP->readState &= ~ESOCK_STATE_ACCEPTING; + + descP->currentAcceptorP = NULL; + } + + return res; +} + + +/* These processes have not performed a select, so we can simply + * remove them from the acceptor queue. + */ +static +ERL_NIF_TERM essio_cancel_accept_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef, + const ErlNifPid* selfP) +{ + /* unqueue request from (acceptor) queue */ + + if (esock_acceptor_unqueue(env, descP, &opRef, selfP)) { return esock_atom_ok; - else - return esock_make_error_errno(env, sock_errno()); + } else { + return esock_atom_not_found; + } } + /* ======================================================================== + * Cancel send request + * + * Cancel a send operation. + * Its either the current writer or one of the waiting writers. */ + extern -ERL_NIF_TERM essio_sockname(ErlNifEnv* env, - ESockDescriptor* descP) +ERL_NIF_TERM essio_cancel_send(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) { - ESockAddress sa; - ESockAddress* saP = &sa; - SOCKLEN_T sz = sizeof(ESockAddress); + ERL_NIF_TERM res; - if (! IS_OPEN(descP->readState)) - return esock_make_error_closed(env); - SSDBG( descP, - ("UNIX-ESSIO", "essio_sockname {%d} -> open - try get sockname\r\n", - descP->sock) ); + ("UNIX-ESSIO", + "essio_cancel_send(%T), {%d,0x%X} -> entry with" + "\r\n opRef: %T" + "\r\n %s" + "\r\n", + sockRef, descP->sock, descP->writeState, + opRef, + ((descP->currentWriterP == NULL) + ? "without writer" : "with writer")) ); + + if (! IS_OPEN(descP->writeState)) { + + res = esock_make_error_closed(env); + + } else if (descP->currentWriterP == NULL) { + + res = esock_atom_not_found; - sys_memzero((char*) saP, sz); - if (sock_name(descP->sock, (struct sockaddr*) saP, &sz) < 0) { - return esock_make_error_errno(env, sock_errno()); } else { - ERL_NIF_TERM esa; + ErlNifPid self; + + ESOCK_ASSERT( enif_self(env, &self) != NULL ); + if (COMPARE_PIDS(&self, &descP->currentWriter.pid) == 0) { + if (COMPARE(opRef, descP->currentWriter.ref) == 0) + res = essio_cancel_send_current(env, descP, sockRef); + else + res = esock_atom_not_found; + } else { + res = essio_cancel_send_waiting(env, descP, opRef, &self); + } + } + + SSDBG( descP, + ("UNIX-ESSIO", "essio_cancel_send(%T) {%d} -> done with result:" + "\r\n %T" + "\r\n", sockRef, descP->sock, res) ); + + return res; +} + + + +/* The current writer process has an ongoing select we first must + * cancel. Then we must re-activate the "first" (the first + * in the writer queue). + */ +static +ERL_NIF_TERM essio_cancel_send_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM res; + + ESOCK_ASSERT( DEMONP("essio_cancel_send_current -> current writer", + env, descP, &descP->currentWriter.mon) == 0); + res = esock_cancel_write_select(env, descP, descP->currentWriter.ref); + + SSDBG( descP, + ("UNIX-ESSIO", "essio_cancel_send_current(%T) {%d} -> cancel res: %T" + "\r\n", sockRef, descP->sock, res) ); + + if (!esock_activate_next_writer(env, descP, sockRef)) { SSDBG( descP, - ("UNIX-ESSIO", "essio_sockname {%d} -> " - "got sockname - try decode\r\n", - descP->sock) ); + ("UNIX-ESSIO", + "essio_cancel_send_current(%T) {%d} -> no more writers" + "\r\n", sockRef, descP->sock) ); + + descP->currentWriterP = NULL; + } + + return res; +} + - esock_encode_sockaddr(env, saP, sz, &esa); +/* These processes have not performed a select, so we can simply + * remove them from the writer queue. + */ +static +ERL_NIF_TERM essio_cancel_send_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef, + const ErlNifPid* selfP) +{ + /* unqueue request from (writer) queue */ + + if (esock_writer_unqueue(env, descP, &opRef, selfP)) { + return esock_atom_ok; + } else { + return esock_atom_not_found; + } +} + + + +/* ======================================================================== + * Cancel receive request + * + * Cancel a read operation. + * Its either the current reader or one of the waiting readers. + */ +extern +ERL_NIF_TERM essio_cancel_recv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) +{ + ERL_NIF_TERM res; + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_cancel_recv(%T), {%d,0x%X} -> entry with" + "\r\n opRef: %T" + "\r\n %s" + "\r\n", + sockRef, descP->sock, descP->readState, + opRef, + ((descP->currentReaderP == NULL) + ? "without reader" : "with reader")) ); + + if (! IS_OPEN(descP->readState)) { + + res = esock_make_error_closed(env); + + } else if (descP->currentReaderP == NULL) { + + res = esock_atom_not_found; + + } else { + ErlNifPid self; + + ESOCK_ASSERT( enif_self(env, &self) != NULL ); + + if (COMPARE_PIDS(&self, &descP->currentReader.pid) == 0) { + if (COMPARE(opRef, descP->currentReader.ref) == 0) + res = essio_cancel_recv_current(env, descP, sockRef); + else + res = esock_atom_not_found; + } else { + res = essio_cancel_recv_waiting(env, descP, opRef, &self); + } + } + + SSDBG( descP, + ("UNIX-ESSIO", "essio_cancel_recv(%T) {%d} -> done with result:" + "\r\n %T" + "\r\n", sockRef, descP->sock, res) ); + + + return res; + +} + + +/* The current reader process has an ongoing select we first must + * cancel. Then we must re-activate the "first" (the first + * in the reader queue). + */ +static +ERL_NIF_TERM essio_cancel_recv_current(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM res; + + ESOCK_ASSERT( DEMONP("essio_cancel_recv_current -> current reader", + env, descP, &descP->currentReader.mon) == 0); + res = esock_cancel_read_select(env, descP, descP->currentReader.ref); + + SSDBG( descP, + ("UNIX-ESSIO", "essio_cancel_recv_current(%T) {%d} -> cancel res: %T" + "\r\n", sockRef, descP->sock, res) ); + + if (!esock_activate_next_reader(env, descP, sockRef)) { SSDBG( descP, - ("UNIX-ESSIO", "essio_sockname {%d} -> decoded: " - "\r\n %T\r\n", - descP->sock, esa) ); + ("UNIX-ESSIO", + "essio_cancel_recv_current(%T) {%d} -> no more readers" + "\r\n", sockRef, descP->sock) ); - return esock_make_ok2(env, esa); + descP->currentReaderP = NULL; + } + + return res; +} + + +/* These processes have not performed a select, so we can simply + * remove them from the reader queue. + */ +static +ERL_NIF_TERM essio_cancel_recv_waiting(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef, + const ErlNifPid* selfP) +{ + /* unqueue request from (reader) queue */ + + if (esock_reader_unqueue(env, descP, &opRef, selfP)) { + return esock_atom_ok; + } else { + return esock_atom_not_found; } } + +/* ======================================================================== + * IOCTL with two args (socket and request "key") + * + */ +extern +ERL_NIF_TERM essio_ioctl2(ErlNifEnv* env, + ESockDescriptor* descP, + unsigned long req) +{ + switch (req) { + +#if defined(SIOCGIFCONF) + case SIOCGIFCONF: + return essio_ioctl_gifconf(env, descP); + break; +#endif + + default: + return esock_make_error(env, esock_atom_enotsup); + break; + } + +} + + + +/* ======================================================================== + * IOCTL with three args (socket, request "key" and one argument) + * + * The type and value of 'arg' depend on the request, + * which we have not yet "analyzed". + * + * Request arg arg type + * ------- ------- -------- + * gifname ifindex integer + * gifindex name string + * gifflags name string + * gifaddr name string + * gifdstaddr name string + * gifbdraddr name string + * gifnetmask name string + * gifmtu name string + * gifhwaddr name string + * gifmap name string + * giftxqlen name string + */ +extern +ERL_NIF_TERM essio_ioctl3(ErlNifEnv* env, + ESockDescriptor* descP, + unsigned long req, + ERL_NIF_TERM arg) +{ + /* This for *get* requests */ + + switch (req) { + +#if defined(SIOCGIFNAME) + case SIOCGIFNAME: + return essio_ioctl_gifname(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFINDEX) + case SIOCGIFINDEX: + return essio_ioctl_gifindex(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFFLAGS) + case SIOCGIFFLAGS: + return essio_ioctl_gifflags(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFADDR) + case SIOCGIFADDR: + return essio_ioctl_gifaddr(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFDSTADDR) + case SIOCGIFDSTADDR: + return essio_ioctl_gifdstaddr(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFBRDADDR) + case SIOCGIFBRDADDR: + return essio_ioctl_gifbrdaddr(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFNETMASK) + case SIOCGIFNETMASK: + return essio_ioctl_gifnetmask(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFMTU) + case SIOCGIFMTU: + return essio_ioctl_gifmtu(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) + case SIOCGIFHWADDR: + return essio_ioctl_gifhwaddr(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) + case SIOCGIFMAP: + return essio_ioctl_gifmap(env, descP, arg); + break; +#endif + +#if defined(SIOCGIFTXQLEN) + case SIOCGIFTXQLEN: + return essio_ioctl_giftxqlen(env, descP, arg); + break; +#endif + + default: + return esock_make_error(env, esock_atom_enotsup); + break; + } + +} + + + /* ======================================================================== + * IOCTL with four args (socket, request "key" and two arguments) + * + * The type and value of arg(s) depend on the request, + * which we have not yet "analyzed". + * + * Request arg1 arg1 type arg2 arg2 type + * ------- ------- --------- ------ --------- + * sifflags name string Flags #{IntFlag := boolean()} + * IntFlag is the native flag + * sifaddr name string Addr sockaddr() + * sifdstaddr name string DstAddr sockaddr() + * sifbrdaddr name string BrdAddr sockaddr() + * sifnetmask name string NetMask sockaddr() + * gifmtu name string MTU integer() + * sifhwaddr name string HwAddr sockaddr() + * giftxqlen name string Len integer() */ extern -ERL_NIF_TERM essio_peername(ErlNifEnv* env, - ESockDescriptor* descP) +ERL_NIF_TERM essio_ioctl4(ErlNifEnv* env, + ESockDescriptor* descP, + unsigned long req, + ERL_NIF_TERM ename, + ERL_NIF_TERM eval) +{ + + switch (req) { + +#if defined(SIOCSIFFLAGS) + case SIOCSIFFLAGS: + return essio_ioctl_sifflags(env, descP, ename, eval); + break; +#endif + +#if defined(SIOCSIFADDR) + case SIOCSIFADDR: + return essio_ioctl_sifaddr(env, descP, ename, eval); + break; +#endif + +#if defined(SIOCSIFDSTADDR) + case SIOCSIFDSTADDR: + return essio_ioctl_sifdstaddr(env, descP, ename, eval); + break; +#endif + +#if defined(SIOCSIFBRDADDR) + case SIOCSIFBRDADDR: + return essio_ioctl_sifbrdaddr(env, descP, ename, eval); + break; +#endif + +#if defined(SIOCSIFNETMASK) + case SIOCSIFNETMASK: + return essio_ioctl_sifnetmask(env, descP, ename, eval); + break; +#endif + +#if defined(SIOCSIFMTU) + case SIOCSIFMTU: + return essio_ioctl_sifmtu(env, descP, ename, eval); + break; +#endif + +#if defined(SIOCSIFTXQLEN) + case SIOCSIFTXQLEN: + return essio_ioctl_siftxqlen(env, descP, ename, eval); + break; +#endif + + default: + return esock_make_error(env, esock_atom_enotsup); + break; + } + +} + + + +/* =========================================================================== + * The implemented (ioctl) get requests falls into three grops: + * + * 1) gifconf - Takes no argument other then the request + * 2) gifname - Takes the interface index (integer) as an argument + * 3) other - All other (get) requests takes the interface name (string) + * as the argument. + * + * The functions defined using the macros below are all in the third (3) + * group. + * + */ + +/* *** essio_ioctl_gifindex *** */ +#if defined(SIOCGIFINDEX) +#if defined(ESOCK_USE_IFINDEX) +#define IOCTL_GIFINDEX_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_ifindex) +#elif defined(ESOCK_USE_INDEX) +#define IOCTL_GIFINDEX_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifindex, SIOCGIFINDEX, ivalue, ifreq.ifr_index) +#else +#define IOCTL_GIFINDEX_FUNC_DECL +#endif +#else +#define IOCTL_GIFINDEX_FUNC_DECL +#endif + +/* *** essio_ioctl_gifflags *** */ +#if defined(SIOCGIFFLAGS) +#define IOCTL_GIFFLAGS_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifflags, SIOCGIFFLAGS, flags, ifreq.ifr_flags) +#else +#define IOCTL_GIFFLAGS_FUNC_DECL +#endif + +/* *** essio_ioctl_gifaddr *** */ +#if defined(SIOCGIFADDR) +#define IOCTL_GIFADDR_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifaddr, SIOCGIFADDR, ifraddr, &ifreq.ifr_addr) +#else +#define IOCTL_GIFADDR_FUNC_DECL +#endif + +/* *** essio_ioctl_gifdstaddr *** */ +#if defined(SIOCGIFDSTADDR) +#define IOCTL_GIFDSTADDR_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifdstaddr, SIOCGIFDSTADDR, ifraddr, &ifreq.ifr_dstaddr) +#else +#define IOCTL_GIFDSTADDR_FUNC_DECL +#endif + +/* *** essio_ioctl_gifbrdaddr *** */ +#if defined(SIOCGIFBRDADDR) +#define IOCTL_GIFBRDADDR_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifbrdaddr, SIOCGIFBRDADDR, ifraddr, &ifreq.ifr_broadaddr) +#else +#define IOCTL_GIFBRDADDR_FUNC_DECL +#endif + +/* *** essio_ioctl_gifnetmask *** */ +#if defined(SIOCGIFNETMASK) +#ifdef __linux__ +#define IOCTL_GIFNETMASK_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_netmask) +#else +#define IOCTL_GIFNETMASK_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifnetmask, SIOCGIFNETMASK, ifraddr, &ifreq.ifr_addr) +#endif +#else +#define IOCTL_GIFNETMASK_FUNC_DECL +#endif + +/* *** essio_ioctl_gifmtu *** */ +#if defined(SIOCGIFMTU) +#define IOCTL_GIFMTU_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifmtu, SIOCGIFMTU, ivalue, ifreq.ifr_mtu) +#else +#define IOCTL_GIFMTU_FUNC_DECL +#endif + +/* *** essio_ioctl_gifhwaddr *** */ +#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) +#define IOCTL_GIFHWADDR_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifhwaddr, SIOCGIFHWADDR, hwaddr, &ifreq.ifr_hwaddr) +#else +#define IOCTL_GIFHWADDR_FUNC_DECL +#endif + +/* *** essio_ioctl_gifmap *** */ +#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) +#define IOCTL_GIFMAP_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(gifmap, SIOCGIFMAP, ifrmap, &ifreq.ifr_map) +#else +#define IOCTL_GIFMAP_FUNC_DECL +#endif + +/* *** essio_ioctl_giftxqlen *** */ +#if defined(SIOCGIFTXQLEN) +#define IOCTL_GIFTXQLEN_FUNC_DECL \ + IOCTL_GET_REQUEST_DECL(giftxqlen, SIOCGIFTXQLEN, ivalue, ifreq.ifr_qlen) +#else +#define IOCTL_GIFTXQLEN_FUNC_DECL +#endif + +#define IOCTL_GET_FUNCS \ + IOCTL_GIFINDEX_FUNC_DECL \ + IOCTL_GIFFLAGS_FUNC_DECL \ + IOCTL_GIFADDR_FUNC_DECL \ + IOCTL_GIFDSTADDR_FUNC_DECL \ + IOCTL_GIFBRDADDR_FUNC_DECL \ + IOCTL_GIFNETMASK_FUNC_DECL \ + IOCTL_GIFMTU_FUNC_DECL \ + IOCTL_GIFHWADDR_FUNC_DECL \ + IOCTL_GIFMAP_FUNC_DECL \ + IOCTL_GIFTXQLEN_FUNC_DECL + +#define IOCTL_GET_REQUEST_DECL(OR, R, EF, UV) \ + static \ + ERL_NIF_TERM essio_ioctl_##OR(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM ename) \ + { \ + ERL_NIF_TERM result; \ + struct ifreq ifreq; \ + char* ifn = NULL; \ + int nlen; \ + \ + SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> entry with" \ + "\r\n (e)Name: %T" \ + "\r\n", descP->sock, ename) ); \ + \ + if (!esock_decode_string(env, ename, &ifn)) \ + return enif_make_badarg(env); \ + \ + nlen = esock_strnlen(ifn, IFNAMSIZ); \ + \ + sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \ + sys_memcpy(ifreq.ifr_name, ifn, \ + (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \ + \ + SSDBG( descP, \ + ("UNIX-ESSIO", \ + "essio_ioctl_" #OR " {%d} -> try ioctl\r\n", \ + descP->sock) ); \ + \ + if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \ + int saveErrno = sock_errno(); \ + ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \ + \ + SSDBG( descP, \ + ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failure: " \ + "\r\n reason: %T (%d)" \ + "\r\n", descP->sock, reason, saveErrno) ); \ + \ + result = esock_make_error(env, reason); \ + \ + } else { \ + SSDBG( descP, \ + ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> encode value\r\n", \ + descP->sock) ); \ + result = encode_ioctl_##EF(env, descP, UV); \ + } \ + \ + FREE(ifn); \ + \ + return result; \ + \ + } +IOCTL_GET_FUNCS +#undef IOCTL_GET_FUNCS + + +/* =========================================================================== + * The "rest" of the implemented (ioctl) get requests + * + * These (get) requests could not be 'generated' by the macros above. + */ + +static +ERL_NIF_TERM essio_ioctl_gifconf(ErlNifEnv* env, + ESockDescriptor* descP) +{ + struct ifconf ifc; + int ifc_len = 0; + int buflen = 100 * sizeof(struct ifreq); + char *buf = MALLOC(buflen); + ERL_NIF_TERM result; + + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_gifconf {%d} -> entry\r\n", descP->sock) ); + + for (;;) { + ifc.ifc_len = buflen; + ifc.ifc_buf = buf; + if (ioctl(descP->sock, SIOCGIFCONF, (char *) &ifc) < 0) { + int saveErrno = sock_errno(); + + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_gifconf {%d} -> failure: " + "\r\n errno: %d (%s)" + "\r\n", descP->sock, saveErrno, erl_errno_id(saveErrno)) ); + + if (saveErrno != EINVAL || ifc_len) { + ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); + FREE(buf); + return esock_make_error(env, reason); + } + } else { + if (ifc.ifc_len == ifc_len) break; /* buf large enough */ + ifc_len = ifc.ifc_len; + } + buflen += 10 * sizeof(struct ifreq); + buf = (char *) REALLOC(buf, buflen); + } + + result = encode_ioctl_ifconf(env, descP, &ifc); + + FREE(ifc.ifc_buf); + + return result; +} + + +#if defined(SIOCGIFNAME) +static +ERL_NIF_TERM essio_ioctl_gifname(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eidx) { - ESockAddress sa; - ESockAddress* saP = &sa; - SOCKLEN_T sz = sizeof(ESockAddress); + ERL_NIF_TERM result; + struct ifreq ifreq; + int index; + + SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> entry with" + "\r\n (e)Index: %T" + "\r\n", descP->sock, eidx) ); - if (! IS_OPEN(descP->readState)) - return esock_make_error_closed(env); + if (!GET_INT(env, eidx, &index)) + return enif_make_badarg(env); + + ifreq.ifr_ifindex = index; SSDBG( descP, - ("UNIX-ESSIO", "essio_peername {%d} -> open - try get peername\r\n", - descP->sock) ); + ("UNIX-ESSIO", + "essio_ioctl_gifname {%d} -> try ioctl\r\n", descP->sock) ); + + if (ioctl(descP->sock, SIOCGIFNAME, (char *) &ifreq) < 0) { + int saveErrno = sock_errno(); + ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); + + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> failure: " + "\r\n reason: %T (%d)" + "\r\n", descP->sock, reason, saveErrno) ); + + result = esock_make_error(env, reason); - sys_memzero((char*) saP, sz); - if (sock_peer(descP->sock, (struct sockaddr*) saP, &sz) < 0) { - return esock_make_error_errno(env, sock_errno()); } else { - ERL_NIF_TERM esa; + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> encode name\r\n", + descP->sock) ); + + result = esock_make_ok2(env, encode_ioctl_ifreq_name(env, ifreq.ifr_name)); + } + + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_gifname {%d} -> done with" + "\r\n result: %T" + "\r\n", + descP->sock, result) ); + + return result; + +} +#endif + + + + +/* =========================================================================== + * The implemented (ioctl) set requests: + * + */ + +/* *** essio_ioctl_sifaddr *** */ +#if defined(SIOCSIFADDR) +#define IOCTL_SIFADDR_FUNC_DECL \ + IOCTL_SET_REQUEST_DECL(sifaddr, SIOCSIFADDR, sockaddr, \ + ((ESockAddress*) &ifreq.ifr_addr)) +#else +#define IOCTL_SIFADDR_FUNC_DECL +#endif + +/* *** essio_ioctl_sifdstaddr *** */ +#if defined(SIOCSIFDSTADDR) +#define IOCTL_SIFDSTADDR_FUNC_DECL \ + IOCTL_SET_REQUEST_DECL(sifdstaddr, SIOCSIFDSTADDR, sockaddr, \ + ((ESockAddress*) &ifreq.ifr_dstaddr)) +#else +#define IOCTL_SIFDSTADDR_FUNC_DECL +#endif + +/* *** essio_ioctl_sifbrdaddr *** */ +#if defined(SIOCSIFBRDADDR) +#define IOCTL_SIFBRDADDR_FUNC_DECL \ + IOCTL_SET_REQUEST_DECL(sifbrdaddr, SIOCSIFBRDADDR, sockaddr, \ + ((ESockAddress*) &ifreq.ifr_broadaddr)) +#else +#define IOCTL_SIFBRDADDR_FUNC_DECL +#endif + +/* *** essio_ioctl_sifnetmask *** */ +#if defined(SIOCSIFNETMASK) +#ifdef __linux__ +#define IOCTL_SIFNETMASK_FUNC_DECL \ + IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \ + ((ESockAddress*) &ifreq.ifr_netmask)) +#else +#define IOCTL_SIFNETMASK_FUNC_DECL \ + IOCTL_SET_REQUEST_DECL(sifnetmask, SIOCSIFNETMASK, sockaddr, \ + ((ESockAddress*) &ifreq.ifr_addr)) +#endif +#else +#define IOCTL_SIFNETMASK_FUNC_DECL +#endif + +/* *** essio_ioctl_sifmtu *** + * On some platforms, MTU is an unsigned int + */ +#if defined(SIOCSIFMTU) +#define IOCTL_SIFMTU_FUNC_DECL \ + IOCTL_SET_REQUEST_DECL(sifmtu, SIOCSIFMTU, mtu, (int*) &ifreq.ifr_mtu) +#else +#define IOCTL_SIFMTU_FUNC_DECL +#endif + +/* *** essio_ioctl_siftxqlen *** */ +#if defined(SIOCSIFTXQLEN) +#define IOCTL_SIFTXQLEN_FUNC_DECL \ + IOCTL_SET_REQUEST_DECL(siftxqlen, SIOCSIFTXQLEN, txqlen, &ifreq.ifr_qlen) +#else +#define IOCTL_SIFTXQLEN_FUNC_DECL +#endif + +#define IOCTL_SET_FUNCS \ + IOCTL_SIFADDR_FUNC_DECL \ + IOCTL_SIFDSTADDR_FUNC_DECL \ + IOCTL_SIFBRDADDR_FUNC_DECL \ + IOCTL_SIFNETMASK_FUNC_DECL \ + IOCTL_SIFMTU_FUNC_DECL \ + IOCTL_SIFTXQLEN_FUNC_DECL + +#define IOCTL_SET_REQUEST_DECL(OR, R, DF, UVP) \ + static \ + ERL_NIF_TERM essio_ioctl_##OR(ErlNifEnv* env, \ + ESockDescriptor* descP, \ + ERL_NIF_TERM ename, \ + ERL_NIF_TERM evalue) \ + { \ + ERL_NIF_TERM result; \ + struct ifreq ifreq; \ + char* ifn = NULL; \ + int nlen; \ + \ + SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> entry with" \ + "\r\n (e)Name: %T" \ + "\r\n (e)Value: %T" \ + "\r\n", descP->sock, ename, evalue) ); \ + \ + if (!esock_decode_string(env, ename, &ifn)) { \ + \ + SSDBG( descP, \ + ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failed decode name" \ + "\r\n", descP->sock) ); \ + \ + return enif_make_badarg(env); \ + } \ + \ + if (! decode_ioctl_##DF(env, descP, evalue, UVP)) { \ + \ + SSDBG( descP, \ + ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failed decode addr" \ + "\r\n", descP->sock) ); \ + \ + return esock_make_invalid(env, esock_atom_##DF); \ + } \ + \ + nlen = esock_strnlen(ifn, IFNAMSIZ); \ + \ + sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); \ + sys_memcpy(ifreq.ifr_name, ifn, \ + (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); \ + \ + SSDBG( descP, \ + ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> try ioctl\r\n", \ + descP->sock) ); \ + \ + if (ioctl(descP->sock, R, (char *) &ifreq) < 0) { \ + int saveErrno = sock_errno(); \ + ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); \ + \ + SSDBG( descP, \ + ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> failure: " \ + "\r\n reason: %T (%d)" \ + "\r\n", descP->sock, reason, saveErrno) ); \ + \ + result = esock_make_error(env, reason); \ + \ + } else { \ + SSDBG( descP, \ + ("UNIX-ESSIO", "essio_ioctl_" #OR " {%d} -> " \ + "addr successfully set\r\n", \ + descP->sock) ); \ + result = esock_atom_ok; \ + } \ + \ + FREE(ifn); \ + \ + return result; \ + \ + } + +IOCTL_SET_FUNCS +#undef IOCTL_SET_FUNCS + + +/* =========================================================================== + * The "rest" of the implemented (ioctl) set requests + * + * These (set) requests could not be 'generated' by the macros above. + */ + +#if defined(SIOCSIFFLAGS) +static +ERL_NIF_TERM essio_ioctl_sifflags(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM ename, + ERL_NIF_TERM eflags) +{ + ERL_NIF_TERM result; + struct ifreq ifreq; + char* ifn = NULL; + int nlen; + + SSDBG( descP, ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> entry with" + "\r\n (e)Name: %T" + "\r\n (e)Flags: %T" + "\r\n", descP->sock, ename, eflags) ); + + if (!esock_decode_string(env, ename, &ifn)) { + + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> failed decode name" + "\r\n", descP->sock) ); + + return enif_make_badarg(env); + } + + // Make sure the length of the string is valid! + nlen = esock_strnlen(ifn, IFNAMSIZ); + + sys_memset(ifreq.ifr_name, '\0', IFNAMSIZ); // Just in case + sys_memcpy(ifreq.ifr_name, ifn, + (nlen >= IFNAMSIZ) ? IFNAMSIZ-1 : nlen); + + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> try (get) ioctl\r\n", + descP->sock) ); + + if (ioctl(descP->sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) { + int saveErrno = sock_errno(); + ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); + + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> " + "failure: failed reading *current* flags" + "\r\n reason: %T (%d)" + "\r\n", descP->sock, reason, saveErrno) ); + + result = esock_make_error(env, reason); + + } else { + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_ioctl_sifflags {%d} -> (local) update flags\r\n", + descP->sock) ); + + if (decode_ioctl_flags(env, descP, eflags, &ifreq.ifr_flags)) { SSDBG( descP, - ("UNIX-ESSIO", "essio_peername {%d} -> " - "got peername - try decode\r\n", - descP->sock) ); + ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> try (set) ioctl\r\n", + descP->sock) ); + + if (ioctl(descP->sock, SIOCSIFFLAGS, (char *) &ifreq) < 0) { + int saveErrno = sock_errno(); + ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); - esock_encode_sockaddr(env, saP, sz, &esa); + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> failure: " + "\r\n reason: %T (%d)" + "\r\n", descP->sock, reason, saveErrno) ); + result = esock_make_error(env, reason); + + } else { + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> " + "updated flags successfully set\r\n", + descP->sock) ); + result = esock_atom_ok; + } + + /* We know that if esock_decode_string is successful, + * we have "some" form of string, and therefor memory + * has been allocated (and need to be freed)... */ + FREE(ifn); + + } else { + result = enif_make_badarg(env); + } + } + + SSDBG( descP, + ("UNIX-ESSIO", "essio_ioctl_sifflags {%d} -> done with result: " + "\r\n %T" + "\r\n", + descP->sock, result) ); + + return result; + +} +#endif + + + +/* =========================================================================== + * ioctl utility functions + * + */ + +static +ERL_NIF_TERM encode_ioctl_ifconf(ErlNifEnv* env, + ESockDescriptor* descP, + struct ifconf* ifcP) +{ + ERL_NIF_TERM result; + unsigned int len = ((ifcP == NULL) ? 0 : + (ifcP->ifc_len / sizeof(struct ifreq))); + + SSDBG( descP, + ("UNIX-ESSIO", + "encode_ioctl_ifconf -> entry (when len = %d)\r\n", len) ); + + if (len > 0) { + ERL_NIF_TERM* array = MALLOC(len * sizeof(ERL_NIF_TERM)); + unsigned int i = 0; + struct ifreq* p = ifcP->ifc_req; + + for (i = 0 ; i < len ; i++) { + SSDBG( descP, + ("UNIX-ESSIO", + "encode_ioctl_ifconf -> encode ifreq entry %d\r\n", i) ); + array[i] = encode_ioctl_ifconf_ifreq(env, descP, &p[i]); + } + + SSDBG( descP, + ("UNIX-ESSIO", "encode_ioctl_ifconf -> all entries encoded\r\n", i) ); + + result = esock_make_ok2(env, MKLA(env, array, len)); + FREE(array); + + } else { + + result = esock_make_ok2(env, MKEL(env)); + + } + + return result; +} + + +#if defined(SIOCGIFMAP) && defined(ESOCK_USE_IFMAP) +static +ERL_NIF_TERM encode_ioctl_ifrmap(ErlNifEnv* env, + ESockDescriptor* descP, + struct ifmap* mapP) +{ + ERL_NIF_TERM mapKeys[] = {esock_atom_mem_start, + esock_atom_mem_end, + esock_atom_base_addr, + esock_atom_irq, + esock_atom_dma, + esock_atom_port}; + ERL_NIF_TERM mapVals[] = {MKUL(env, mapP->mem_start), + MKUL(env, mapP->mem_end), + MKUI(env, mapP->base_addr), + MKUI(env, mapP->irq), + MKUI(env, mapP->dma), + MKUI(env, mapP->port)}; + unsigned int numMapKeys = NUM(mapKeys); + unsigned int numMapVals = NUM(mapVals); + ERL_NIF_TERM emap; + + ESOCK_ASSERT( numMapVals == numMapKeys ); + ESOCK_ASSERT( MKMA(env, mapKeys, mapVals, numMapKeys, &emap) ); + + SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ifrmap -> done with" + "\r\n Map: %T" + "\r\n", emap) ); + + return esock_make_ok2(env, emap);; +} +#endif + + +#if defined(SIOCGIFHWADDR) && defined(ESOCK_USE_HWADDR) +static +ERL_NIF_TERM encode_ioctl_hwaddr(ErlNifEnv* env, + ESockDescriptor* descP, + struct sockaddr* addrP) +{ + ERL_NIF_TERM eaddr; + SOCKLEN_T sz = sizeof(struct sockaddr); + + esock_encode_hwsockaddr(env, addrP, sz, &eaddr); + + SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ifraddr -> done with" + "\r\n Sock Addr: %T" + "\r\n", eaddr) ); + + return esock_make_ok2(env, eaddr);; +} +#endif + + +static +ERL_NIF_TERM encode_ioctl_ifraddr(ErlNifEnv* env, + ESockDescriptor* descP, + struct sockaddr* addrP) +{ + ERL_NIF_TERM eaddr; + + esock_encode_sockaddr(env, (ESockAddress*) addrP, -1, &eaddr); + + SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ifraddr -> done with" + "\r\n Sock Addr: %T" + "\r\n", eaddr) ); + + return esock_make_ok2(env, eaddr);; +} + + +static +ERL_NIF_TERM encode_ioctl_flags(ErlNifEnv* env, + ESockDescriptor* descP, + short flags) +{ + int i, flag, num = esock_ioctl_flags_length; // NUM(ioctl_flags); + ERL_NIF_TERM eflags, eflag; + SocketTArray ta = TARRAY_CREATE(20); // Just to be on the safe side + + if (flags == 0) { + eflags = MKEL(env); + } else { + for (i = 0; (i < num) && (flags != 0); i++) { + flag = esock_ioctl_flags[i].flag; + if ((flag != 0) && ((flags & flag) == flag)) { + eflag = *(esock_ioctl_flags[i].name); + flags &= ~flag; + + SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_flags {%d} -> " + "\r\n i: %d" + "\r\n found flag: %T (%d)" + "\r\n remaining flags: %d" + "\r\n", descP->sock, i, eflag, flag, flags) ); + + TARRAY_ADD(ta, eflag); + } + } + if (flags != 0) { + + SSDBG( descP, + ("UNIX-ESSIO", "encode_ioctl_flags {%d} -> unknown flag(s): %d" + "\r\n", descP->sock, flags) ); + + TARRAY_ADD(ta, MKI(env, flags)); + } + + TARRAY_TOLIST(ta, env, &eflags); + } + + + SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_flags -> done with" + "\r\n Flags: %T (%d)" + "\r\n", eflags, flags) ); + + return esock_make_ok2(env, eflags); +} + + +static +BOOLEAN_T decode_ioctl_sockaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eaddr, + ESockAddress* addr) +{ + SOCKLEN_T addrLen; + BOOLEAN_T result; + + result = esock_decode_sockaddr(env, eaddr, (ESockAddress*) addr, &addrLen); + + VOID(addrLen); + + SSDBG( descP, + ("UNIX-ESSIO", "decode_ioctl_sockaddr {%d} -> decode result: %s" + "\r\n", descP->sock, B2S(result)) ); + + return result; +} + + +static +BOOLEAN_T decode_ioctl_mtu(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM emtu, + int* mtu) +{ + BOOLEAN_T result; + + if (! GET_INT(env, emtu, mtu)) { + result = FALSE; + } else { + result = TRUE; + } + + SSDBG( descP, + ("UNIX-ESSIO", "decode_ioctl_mtu {%d} -> decode result: %s" + "\r\n", descP->sock, B2S(result)) ); + + return result; +} + + +#if defined(SIOCSIFTXQLEN) +static +BOOLEAN_T decode_ioctl_txqlen(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM etxqlen, + int* txqlen) +{ + return decode_ioctl_ivalue(env, descP, etxqlen, txqlen); +} +#endif + +/* All uses of the function should be added. For instance: + * #if defined(SIOCGIFTXQLEN) || defined(FOOBAR) || defined(YXA) + */ +#if defined(SIOCGIFTXQLEN) +static +BOOLEAN_T decode_ioctl_ivalue(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eivalue, + int* ivalue) +{ + BOOLEAN_T result; + + if (! GET_INT(env, eivalue, ivalue)) { + result = FALSE; + } else { + result = TRUE; + } + + SSDBG( descP, + ("UNIX-ESSIO", "decode_ioctl_ivalue {%d} -> decode result: %s" + "\r\n", descP->sock, B2S(result)) ); + + return result; +} +#endif + + +static +BOOLEAN_T decode_ioctl_flags(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eflags, + short* flags) +{ + ERL_NIF_TERM key, value; + ErlNifMapIterator iter; + int tmpFlags = (int) *flags; // Current value + int flag; + + SSDBG( descP, + ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> entry with" + "\r\n flags: %d" + "\r\n", + descP->sock, tmpFlags) ); + + enif_map_iterator_create(env, eflags, &iter, ERL_NIF_MAP_ITERATOR_FIRST); + + while (enif_map_iterator_get_pair(env, &iter, &key, &value)) { + + /* Convert key (eflag) to int */ + if (! GET_INT(env, key, &flag)) { + enif_map_iterator_destroy(env, &iter); + return FALSE; + } + + // Update flag + if (COMPARE(value, esock_atom_true) == 0) { SSDBG( descP, - ("UNIX-ESSIO", "esock_peername {%d} -> decoded: " - "\r\n %T\r\n", - descP->sock, esa) ); + ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> set %d\r\n", + descP->sock, flag) ); + tmpFlags |= flag; + } else { + SSDBG( descP, + ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> reset %d\r\n", + descP->sock, flag) ); + tmpFlags &= ~flag; + } + + enif_map_iterator_next(env, &iter); + } + + enif_map_iterator_destroy(env, &iter); + + SSDBG( descP, + ("UNIX-ESSIO", "decode_ioctl_flags {%d} -> done with" + "\r\n (new) flags: %d" + "\r\n", + descP->sock, tmpFlags) ); + + *flags = (short) tmpFlags; + + return TRUE; +} + + +static +ERL_NIF_TERM encode_ioctl_ivalue(ErlNifEnv* env, + ESockDescriptor* descP, + int ivalue) +{ + ERL_NIF_TERM eivalue = MKI(env, ivalue); + + SSDBG( descP, ("UNIX-ESSIO", "encode_ioctl_ivalue -> done with" + "\r\n iValue: %T (%d)" + "\r\n", eivalue, ivalue) ); + + return esock_make_ok2(env, eivalue);; +} + +static +ERL_NIF_TERM encode_ioctl_ifconf_ifreq(ErlNifEnv* env, + ESockDescriptor* descP, + struct ifreq* ifrP) +{ + ERL_NIF_TERM ename, eaddr; + + ESOCK_ASSERT( ifrP != NULL ); + + SSDBG( descP, + ("UNIX-ESSIO", "encode_ioctl_ifconf_ifreq -> encode name\r\n") ); + ename = encode_ioctl_ifreq_name(env, ifrP->ifr_name); + + SSDBG( descP, + ("UNIX-ESSIO", "encode_ioctl_ifconf_ifreq -> encode sockaddr\r\n") ); + eaddr = encode_ioctl_ifreq_sockaddr(env, &ifrP->ifr_addr); + + SSDBG( descP, + ("UNIX-ESSIO", "encode_ioctl_ifconf_ifreq -> make ifreq map with" + "\r\n Name: %T" + "\r\n Sock Addr: %T" + "\r\n", ename, eaddr) ); + return make_ifreq(env, ename, esock_atom_addr, eaddr); +} + +static +ERL_NIF_TERM encode_ioctl_ifreq_name(ErlNifEnv* env, + char* name) +{ + return ((name == NULL) ? esock_atom_undefined : MKS(env, name)); +} + +static +ERL_NIF_TERM encode_ioctl_ifreq_sockaddr(ErlNifEnv* env, struct sockaddr* sa) +{ + ERL_NIF_TERM esa; + + if (sa != NULL) { + + esock_encode_sockaddr(env, (ESockAddress*) sa, -1, &esa); + + } else { + + esa = esock_atom_undefined; - return esock_make_ok2(env, esa); } + + return esa; } +/* The ifreq structure *always* contain a name + * and *one* other element. The second element + * depend on the ioctl request. + */ +static +ERL_NIF_TERM make_ifreq(ErlNifEnv* env, + ERL_NIF_TERM name, + ERL_NIF_TERM key2, + ERL_NIF_TERM val2) +{ + ERL_NIF_TERM keys[2]; + ERL_NIF_TERM vals[2]; + ERL_NIF_TERM res; + + keys[0] = esock_atom_name; + vals[0] = name; + + keys[1] = key2; + vals[1] = val2; + + ESOCK_ASSERT( MKMA(env, keys, vals, NUM(keys), &res) ); + + return res; +} + + + + /* ---------------------------------------------------------------------- * U t i l i t y F u n c t i o n s * ---------------------------------------------------------------------- @@ -3004,7 +4914,7 @@ BOOLEAN_T send_check_writer(ErlNifEnv* env, "\r\n", descP->sock, ref) ); if (! esock_writer_search4pid(env, descP, &caller)) { - esock_writer_push(env, descP, caller, ref); + esock_writer_push(env, descP, caller, ref, NULL); *checkResult = esock_atom_select; } else { /* Writer already in queue */ @@ -3096,7 +5006,7 @@ ERL_NIF_TERM send_check_result(ErlNifEnv* env, res = send_check_retry(env, descP, written, sockRef, sendRef); } else if (dataInTail) { - /* Not the entire package */ + /* We sent all we could, but not everything (data in tail) */ SSDBG( descP, ("UNIX-ESSIO", "send_check_result(%T) {%d} -> " @@ -3178,16 +5088,17 @@ ERL_NIF_TERM send_check_fail(ErlNifEnv* env, int saveErrno, ERL_NIF_TERM sockRef) { - ERL_NIF_TERM reason; + ERL_NIF_TERM reason; ESOCK_CNT_INC(env, descP, sockRef, esock_atom_write_fails, &descP->writeFails, 1); - SSDBG( descP, ("UNIX-ESSIO", "send_check_fail(%T) {%d} -> error: %d\r\n", - sockRef, descP->sock, saveErrno) ); - reason = MKA(env, erl_errno_id(saveErrno)); + SSDBG( descP, + ("UNIX-ESSIO", "send_check_fail(%T) {%d} -> error: %d (%T)\r\n", + sockRef, descP->sock, saveErrno, reason) ); + if (saveErrno != EINVAL) { /* @@ -3205,6 +5116,7 @@ ERL_NIF_TERM send_check_fail(ErlNifEnv* env, descP->currentWriterP = NULL; } } + return esock_make_error(env, reason); } @@ -3382,7 +5294,7 @@ ERL_NIF_TERM send_check_retry(ErlNifEnv* env, * or a (erlang) binary that just needs to be appended to the control * buffer. * - * Our "problem" is that we have no idea much memory we actually need. + * Our "problem" is that we have no idea how much memory we actually need. * */ @@ -3732,7 +5644,7 @@ void encode_msg(ErlNifEnv* env, struct msghdr* msgHdrP, ErlNifBinary* dataBufP, ErlNifBinary* ctrlBufP, - ERL_NIF_TERM* eSockAddr) + ERL_NIF_TERM* eMsg) { ERL_NIF_TERM addr, iov, ctrl, flags; @@ -3802,7 +5714,7 @@ void encode_msg(ErlNifEnv* env, if (msgHdrP->msg_namelen == 0) numKeys--; // No addr - ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eSockAddr) ); + ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eMsg) ); SSDBG( descP, ("UNIX-ESSIO", @@ -4318,6 +6230,317 @@ ERL_NIF_TERM essio_sendfile_ok(ErlNifEnv* env, #endif // #ifdef HAVE_SENDFILE +/* ==================================================================== + * + * NIF (I/O backend) Resource callback functions: dtor, stop and down + * + * ==================================================================== + */ + +extern +void essio_dtor(ErlNifEnv* env, + ESockDescriptor* descP) +{ + SGDBG( ("UNIX-ESSIO", "dtor -> entry\r\n") ); + + if (IS_SELECTED(descP)) { + /* We have used the socket in the select machinery, + * so we must have closed it properly to get here + */ + ESOCK_ASSERT( IS_CLOSED(descP->readState) ); + ESOCK_ASSERT( IS_CLOSED(descP->writeState) ); + ESOCK_ASSERT( descP->sock == INVALID_SOCKET ); + } else { + /* The socket is only opened, should be safe to close nonblocking */ + (void) sock_close(descP->sock); + descP->sock = INVALID_SOCKET; + } + + SGDBG( ("UNIX-ESSIO", "dtor -> set state and pattern\r\n") ); + descP->readState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED); + descP->writeState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED); + descP->pattern = (ESOCK_DESC_PATTERN_DTOR | ESOCK_STATE_CLOSED); + + esock_free_env("dtor reader", descP->currentReader.env); + descP->currentReader.env = NULL; + + esock_free_env("dtor writer", descP->currentWriter.env); + descP->currentWriter.env = NULL; + + esock_free_env("dtor acceptor", descP->currentAcceptor.env); + descP->currentAcceptor.env = NULL; + + SGDBG( ("UNIX-ESSIO", "dtor -> try free readers request queue\r\n") ); + esock_free_request_queue(&descP->readersQ); + + SGDBG( ("UNIX-ESSIO", "dtor -> try free writers request queue\r\n") ); + esock_free_request_queue(&descP->writersQ); + + SGDBG( ("UNIX-ESSIO", "dtor -> try free acceptors request queue\r\n") ); + esock_free_request_queue(&descP->acceptorsQ); + +#ifdef HAVE_SENDFILE + ESOCK_ASSERT( descP->sendfileHandle == INVALID_HANDLE ); + if (descP->sendfileCountersP != NULL) { + FREE(descP->sendfileCountersP); + descP->sendfileCountersP = NULL; + } +#endif + + esock_free_env("dtor close env", descP->closeEnv); + descP->closeEnv = NULL; + + esock_free_env("dtor meta env", descP->meta.env); + descP->meta.env = NULL; + + SGDBG( ("UNIX-ESSIO", "dtor -> done\r\n") ); +} + + +extern +void essio_stop(ErlNifEnv* env, + ESockDescriptor* descP) +{ +#ifdef HAVE_SENDFILE + if (descP->sendfileCountersP != NULL) { + ESockSendfileCounters* cntP = descP->sendfileCountersP; + + SSDBG( descP, ("UNIX-ESSIO", "esock_stop(%d) -> sendfileCounters:" + "\r\n cnt: %lu" + "\r\n byteCnt: %lu" + "\r\n fails: %lu" + "\r\n max: %lu" + "\r\n pkg: %lu" + "\r\n pkgMax %lu" + "\r\n tries: %lu" + "\r\n waits: %lu" + "\r\n", + descP->sock, + (unsigned long) cntP->cnt, + (unsigned long) cntP->byteCnt, + (unsigned long) cntP->fails, + (unsigned long) cntP->max, + (unsigned long) cntP->pkg, + (unsigned long) cntP->pkgMax, + (unsigned long) cntP->tries, + (unsigned long) cntP->waits) ); + } +#endif + + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Inform waiting Closer, or close socket + * + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + + if (! enif_is_pid_undefined(&descP->closerPid)) { + /* We have a waiting closer process after nif_close() + * - send message to trigger nif_finalize_close() + */ + + SSDBG( descP, + ("UNIX-ESSIO", + "esock_stop(%d) -> send close msg to %T\r\n", + descP->sock, MKPID(env, &descP->closerPid)) ); + + esock_send_close_msg(env, descP, &descP->closerPid); + /* Message send frees closeEnv */ + descP->closeEnv = NULL; + descP->closeRef = esock_atom_undefined; + + } else { + int err; + + /* We do not have a closer process + * - have to do an unclean (non blocking) close */ + +#ifdef HAVE_SENDFILE + if (descP->sendfileHandle != INVALID_HANDLE) + esock_send_sendfile_deferred_close_msg(env, descP); +#endif + + err = esock_close_socket(env, descP, FALSE); + + if (err != 0) + esock_warning_msg("[UNIX-ESSIO] Failed closing socket without " + "closer process: " + "\r\n Controlling Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d (%T)" + "\r\n", + descP->ctrlPid, descP->sock, + err, MKA(env, erl_errno_id(err))); + } + +} + + +/* A 'down' has occured. + * Check the possible processes we monitor in turn: + * closer, controlling process (owner), connector, reader, acceptor and writer. + * + */ +extern +void essio_down(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP, + const ErlNifMonitor* monP) +{ + if (COMPARE_PIDS(&descP->closerPid, pidP) == 0) { + + /* The closer process went down + * - it will not call nif_finalize_close + */ + + enif_set_pid_undefined(&descP->closerPid); + + if (MON_EQ(&descP->closerMon, monP)) { + MON_INIT(&descP->closerMon); + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down {%d} -> closer process exit\r\n", + descP->sock) ); + + } else { + // The owner is the closer so we used its monitor + + ESOCK_ASSERT( MON_EQ(&descP->ctrlMon, monP) ); + MON_INIT(&descP->ctrlMon); + enif_set_pid_undefined(&descP->ctrlPid); + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down {%d} -> closer controlling process exit\r\n", + descP->sock) ); + } + + /* Since the closer went down there was one, + * hence esock_close() must have run or scheduled esock_stop(), + * or the socket has never been "selected" upon. + */ + + if (descP->closeEnv == NULL) { + int err; + + /* Since there is no closeEnv, + * esock_close() did not schedule esock_stop() + * and is about to call esock_finalize_close() but died, + * or esock_stop() has run, sent close_msg to the closer + * and cleared ->closeEnv but the closer died + * - we have to do an unclean (non blocking) socket close here + */ + +#ifdef HAVE_SENDFILE + if (descP->sendfileHandle != INVALID_HANDLE) + esock_send_sendfile_deferred_close_msg(env, descP); +#endif + + err = esock_close_socket(env, descP, FALSE); + if (err != 0) + esock_warning_msg("[UNIX-ESSIO] " + "Failed closing socket for terminating " + "closer process: " + "\r\n Closer Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d (%T)" + "\r\n", + MKPID(env, pidP), descP->sock, + err, MKA(env, erl_errno_id(err))); + } else { + /* Since there is a closeEnv esock_stop() has not run yet + * - when it finds that there is no closer process + * it will close the socket and ignore the close_msg + */ + esock_clear_env("essio_down - close-env", descP->closeEnv); + esock_free_env("essio_down - close-env", descP->closeEnv); + descP->closeEnv = NULL; + descP->closeRef = esock_atom_undefined; + } + + } else if (MON_EQ(&descP->ctrlMon, monP)) { + MON_INIT(&descP->ctrlMon); + /* The owner went down */ + enif_set_pid_undefined(&descP->ctrlPid); + + if (IS_OPEN(descP->readState)) { + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down {%d} -> controller process exit" + "\r\n initiate close\r\n", + descP->sock) ); + + essio_down_ctrl(env, descP, pidP); + + descP->readState |= ESOCK_STATE_CLOSING; + descP->writeState |= ESOCK_STATE_CLOSING; + + } else { + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down {%d} -> controller process exit" + "\r\n already closed or closing\r\n", + descP->sock) ); + + } + + } else if (descP->connectorP != NULL && + MON_EQ(&descP->connector.mon, monP)) { + MON_INIT(&descP->connector.mon); + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down {%d} -> connector process exit\r\n", + descP->sock) ); + + /* connectorP is only set during connection. + * Forget all about the ongoing connection. + * We might end up connected, but the process that initiated + * the connection has died and will never know + */ + + esock_requestor_release("esock_down->connector", + env, descP, &descP->connector); + descP->connectorP = NULL; + descP->writeState &= ~ESOCK_STATE_CONNECTING; + + } else { + ERL_NIF_TERM sockRef = enif_make_resource(env, descP); + + /* check all operation queue(s): acceptor, writer and reader. + * + * Is it really any point in doing this if the socket is closed? + * + */ + + if (IS_CLOSED(descP->readState)) { + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down(%T) {%d} -> stray down: %T\r\n", + sockRef, descP->sock, pidP) ); + } else { + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down(%T) {%d} -> other process term\r\n", + sockRef, descP->sock) ); + + if (descP->currentReaderP != NULL) + essio_down_reader(env, descP, sockRef, pidP, monP); + if (descP->currentAcceptorP != NULL) + essio_down_acceptor(env, descP, sockRef, pidP, monP); + if (descP->currentWriterP != NULL) + essio_down_writer(env, descP, sockRef, pidP, monP); + } + } + +} + + +/* ==================================================================== */ + /* *** Recv/recvfrom/recvmsg utility functions *** */ /* *** recv_check_reader *** @@ -4353,7 +6576,7 @@ BOOLEAN_T recv_check_reader(ErlNifEnv* env, if (! esock_reader_search4pid(env, descP, &caller)) { if (COMPARE(ref, esock_atom_zero) == 0) goto done_ok; - esock_reader_push(env, descP, caller, ref); + esock_reader_push(env, descP, caller, ref, NULL); *checkResult = esock_atom_select; } else { /* Reader already in queue */ @@ -4967,3 +7190,198 @@ void recv_error_current_reader(ErlNifEnv* env, } +/* *** essio_down_ctrl *** + * + * Stop after a downed controller (controlling process = owner process) + * + * This is 'extern' because its currently called from prim_socket_nif + * (esock_setopt_otp_ctrl_proc). + */ +extern +void essio_down_ctrl(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP) +{ + SSDBG( descP, + ("UNIX-ESSIO", "essio_down_ctrl {%d} ->" + "\r\n Pid: %T" + "\r\n", descP->sock, MKPID(env, pidP)) ); + + if (do_stop(env, descP)) { + /* esock_stop() is scheduled + * - it has to close the socket + */ + SSDBG( descP, + ("UNIX-ESSIO", "essio_down_ctrl {%d} -> stop was scheduled\r\n", + descP->sock) ); + } else { + int err; + + /* Socket is not in the select machinery + * so esock_stop() will not be called + * - we have to do an unclean (non blocking) socket close here + */ + +#ifdef HAVE_SENDFILE + if (descP->sendfileHandle != INVALID_HANDLE) + esock_send_sendfile_deferred_close_msg(env, descP); +#endif + + err = esock_close_socket(env, descP, FALSE); + if (err != 0) + esock_warning_msg("[UNIX-ESSIO] " + "Failed closing socket for terminating " + "owner process: " + "\r\n Owner Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d (%T)" + "\r\n", + MKPID(env, pidP), descP->sock, + err, MKA(env, erl_errno_id(err))); + } +} + + + +/* *** essio_down_acceptor *** + * + * Check and then handle a downed acceptor process. + * + */ +static +void essio_down_acceptor(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP) +{ + if (MON_EQ(&descP->currentAcceptor.mon, monP)) { + MON_INIT(&descP->currentAcceptor.mon); + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down_acceptor(%T) {%d} -> " + "current acceptor - try activate next\r\n", + sockRef, descP->sock) ); + + if (!esock_activate_next_acceptor(env, descP, sockRef)) { + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down_acceptor(%T) {%d} -> no more writers\r\n", + sockRef, descP->sock) ); + + descP->readState &= ~ESOCK_STATE_ACCEPTING; + + descP->currentAcceptorP = NULL; + } + + } else { + + /* Maybe unqueue one of the waiting acceptors */ + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down_acceptor(%T) {%d} -> " + "not current acceptor - maybe a waiting acceptor\r\n", + sockRef, descP->sock) ); + + esock_acceptor_unqueue(env, descP, NULL, pidP); + } +} + + +/* *** essio_down_writer *** + * + * Check and then handle a downed writer process. + * + */ + +static +void essio_down_writer(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP) +{ + if (MON_EQ(&descP->currentWriter.mon, monP)) { + MON_INIT(&descP->currentWriter.mon); + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down_writer(%T) {%d} -> " + "current writer - try activate next\r\n", + sockRef, descP->sock) ); + + if (!esock_activate_next_writer(env, descP, sockRef)) { + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down_writer(%T) {%d} -> no active writer\r\n", + sockRef, descP->sock) ); + + descP->currentWriterP = NULL; + } + + } else { + + /* Maybe unqueue one of the waiting writer(s) */ + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down_writer(%T) {%d} -> " + "not current writer - maybe a waiting writer\r\n", + sockRef, descP->sock) ); + + esock_writer_unqueue(env, descP, NULL, pidP); + } +} + + +/* *** essio_down_reader *** + * + * Check and then handle a downed reader process. + * + */ + +static +void essio_down_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP) +{ + if (MON_EQ(&descP->currentReader.mon, monP)) { + MON_INIT(&descP->currentReader.mon); + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down_reader(%T) {%d} -> " + "current reader - try activate next\r\n", + sockRef, descP->sock) ); + + if (! esock_activate_next_reader(env, descP, sockRef)) { + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down_reader(%T) {%d} -> no more readers\r\n", + sockRef, descP->sock) ); + + descP->currentReaderP = NULL; + } + + } else { + + /* Maybe unqueue one of the waiting reader(s) */ + + SSDBG( descP, + ("UNIX-ESSIO", + "essio_down_reader(%T) {%d} -> " + "not current reader - maybe a waiting reader\r\n", + sockRef, descP->sock) ); + + esock_reader_unqueue(env, descP, NULL, pidP); + } +} + + diff --git a/erts/emulator/nifs/win32/win_socket_asyncio.c b/erts/emulator/nifs/win32/win_socket_asyncio.c new file mode 100644 index 0000000000..f91e7edc48 --- /dev/null +++ b/erts/emulator/nifs/win32/win_socket_asyncio.c @@ -0,0 +1,9600 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2023-2023. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + * + * ---------------------------------------------------------------------- + * Purpose : Windows version of the asyncronous I/O backend. + * ---------------------------------------------------------------------- + * + * Misc: + * This is based of the Windows concept: I/O Completion Port. + * This feature works on "all" kinds of I/O, even file I/O. But, + * our implementation only deals with socket I/O. + * The "asynchronous" access functions (that "can" block) that we + * use are as following: + * + * * WSASend, WSASendTo, WSASendMsg, WSARecv, WSARecvFrom, WSARecvMsg + * * AccepxEx + * (this is actually a function pointer, obtained at run time by + * making a call to the WSAIoctl function with the + * SIO_GET_EXTENSION_FUNCTION_POINTER opcode specified. + * The input buffer passed to the WSAIoctl function must contain + * WSAID_ACCEPTEX). + * To get the local and remote addresses, the GetAcceptExSockaddrs + * must be called. This function is *also* a function pointer + * obtained at run time by making a call to the WSAIoctl function. + * * ConnectEx: + * (this is actually a function pointer, obtained at run time by + * The function pointer for the ConnectEx function must be + * making a call to the WSAIoctl function with the + * SIO_GET_EXTENSION_FUNCTION_POINTER opcode specified. + * The input buffer passed to the WSAIoctl function must contain + * WSAID_CONNECTEX). + * * WSASendMsg & WSARecvMsg are actually *also* function pointers!! + * + * But since we want them to "behave" the same way, we need to add + * some wrapper code to simulate the "completion behaviour". + * + * These functions (in erlang) should return simething *like* this: + * + * ok | completion | {error, Reason} + * + * And if the return value was 'completion', the caller shall expect + * the following message, when the "operation" has "completed" (success + * or failure): + * + * {'$socket', Socket, completion, {Ref, CompletionStatus}} + * + * Where 'Socket' is the socket on which the call was made (for example, + * 'socket:send(Socket, ...)), and CompletionStatus is the result of + * actual operation: ok | {error, Reason} + * + * Examples: + * * https://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancediomethod5i.html + * * https://www.codeproject.com/Articles/13382/A-simple-application-using-I-O-Completion-Ports-an + * * https://www.winsocketdotnetworkprogramming.com/winsock2programming/winsock2advancedscalableapp6b.html + * + * More useful links: + * * https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex + * * https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaioctl + * * https://learn.microsoft.com/en-us/windows/win32/winsock/socket-options-and-ioctls-2 + * + * Note: + * - + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +// #include <Ws2def.h> +// #include <winsock2.h> +// #include <windows.h> +#include <ws2tcpip.h> +#include <mswsock.h> +#include <stdio.h> + +#include <sys.h> + +#include "socket_int.h" +#include "socket_io.h" +#include "socket_asyncio.h" +#include "socket_util.h" +#include "socket_tarray.h" +#include "socket_dbg.h" + + +/* =================================================================== * + * * + * Local Constants * + * * + * =================================================================== */ + +#define ESAIO_OK ESOCK_IO_OK +#define ESAIO_ERR_WINSOCK_INIT 0x0001 +#define ESAIO_ERR_IOCPORT_CREATE 0x0002 +#define ESAIO_ERR_FSOCK_CREATE 0x0003 +#define ESAIO_ERR_IOCTL_ACCEPT_GET 0x0004 +#define ESAIO_ERR_IOCTL_CONNECT_GET 0x0005 +#define ESAIO_ERR_IOCTL_SENDMSG_GET 0x0006 +#define ESAIO_ERR_IOCTL_RECVMSG_GET 0x0007 +#define ESAIO_ERR_THREAD_OPTS_CREATE 0x0011 +#define ESAIO_ERR_THREAD_CREATE 0x0012 + +#define ERRNO_BLOCK WSAEWOULDBLOCK + + +/* ======================================================================== * + * Socket wrappers * + * ======================================================================== * + */ + +#define sock_accept_O(s, as, b, al, rb, o) \ + ctrl.accept((s), (as), (b), 0, (al), (al), (rb), (o)) +#define sock_bind(s, addr, len) bind((s), (addr), (len)) +#define sock_close(s) closesocket((s)) +#define sock_connect(s, a, al) connect((s), (a), (al)) +#define sock_connect_O(s, a, al, sent, o) \ + ctrl.connect((s), (struct sockaddr*) (a), (al), NULL, 0, (sent), (o)) +#define sock_errno() WSAGetLastError() +// #define sock_listen(s, b) listen((s), (b)) +// #define sock_name(s, addr, len) getsockname((s), (addr), (len)) +#define sock_open(domain, type, proto) socket((domain), (type), (proto)) +#define sock_open_O(domain, type, proto) \ + WSASocket((domain), (type), (proto), NULL, 0, WSA_FLAG_OVERLAPPED) +#define sock_recv_O(s,buf,flag,ol) \ + WSARecv((s), (buf), 1, NULL, (flag), (ol), NULL) +#define sock_recvfrom_O(s,buf,flag,fa,fal,ol) \ + WSARecvFrom((s), (buf), 1, NULL, (flag), (fa), (fal), (ol), NULL) +#define sock_recvmsg_O(s,msg,o) \ + ctrl.recvmsg((s), (msg), NULL, (o), NULL) +#define sock_send_O(s,buf,flag,o) \ + WSASend((s), (buf), 1, NULL, (flag), (o), NULL) +/* #define sock_sendmsg_O(s,buf,flag,ol) \ */ +/* WSASendMsg((s), (buf), (flag), NULL, (ol), NULL) */ +#define sock_sendmsg_O(s,buf,flag,ol) \ + ctrl.sendmsg((s), (buf), (flag), NULL, (ol), NULL) +#define sock_sendto_O(s,buf,flag,ta,tal,o) \ + WSASendTo((s), (buf), 1, NULL, (flag), (ta), (tal), (o), NULL) +#define sock_setopt(s,l,o,v,ln) setsockopt((s),(l),(o),(v),(ln)) + + +#define ESAIO_UPDATE_ACCEPT_CONTEXT(AS, LS) \ + sock_setopt( (AS), SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, \ + (char*) &(LS), sizeof( (LS) )) +#define ESAIO_UPDATE_CONNECT_CONTEXT(S) \ + sock_setopt((S), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) + + +#define ESOCK_CMSG_FIRSTHDR(M) WSA_CMSG_FIRSTHDR((M)) +#define ESOCK_CMSG_NXTHDR(M,C) WSA_CMSG_NXTHDR((M), (C)) +#define ESOCK_CMSG_DATA(C) WSA_CMSG_DATA((C)) + + +/* =================================================================== * + * * + * Local types * + * * + * =================================================================== */ + +typedef struct { + Uint16 id; /* Thread id: mainly used for debugging, + * and name creation */ + + /* Thread state(s) */ +#define ESAIO_THREAD_STATE_UNDEF 0xFFFF +#define ESAIO_THREAD_STATE_INITIATING 0x0000 +#define ESAIO_THREAD_STATE_OPERATIONAL 0x0001 +#define ESAIO_THREAD_STATE_TERMINATING 0x0002 +#define ESAIO_THREAD_STATE_TERMINATED 0x0003 + + Uint16 state; /* State of the thread: + * undefined, initiating, operational, terminating */ + + /* Thread error 'state(s)'. + * If the thread is "not running", this value tells why. + */ +#define ESAIO_THREAD_ERROR_UNDEF 0xFFFF +#define ESAIO_THREAD_ERROR_OK 0x0000 +#define ESAIO_THREAD_ERROR_TOCREATE 0x0001 +#define ESAIO_THREAD_ERROR_TCREATE 0x0002 +#define ESAIO_THREAD_ERROR_GET 0x0003 +#define ESAIO_THREAD_ERROR_CMD 0x0004 + + Uint32 error; /* In case the thread exits, + * this is where the (error) reason is stored. + * Not sure i f we would ever be able to + * read this (if things are bad enough that the + * threads terminate)... + */ + + /* Do we need this? + * "The environment of the calling thread (process bound or + * callback environment) or NULL if calling from a custom + * thread not spawned by ERTS." + */ + ErlNifEnv* env; /* Used when sending messages */ + +#define ESAIO_THREAD_CNT_MAX 0xFFFFFFFF + + Uint32 cnt; /* Run-counter: mainly used for debugging */ + + unsigned int latest; /* Latest request (tag) */ +} ESAIOThreadData; + +typedef struct { + ErlNifThreadOpts* optsP; + ErlNifTid tid; + ESAIOThreadData data; +} ESAIOThread; + +typedef struct { + WSADATA wsaData; + HANDLE cport; + + SOCKET dummy; // Used for extracting AcceptEx and ConnectEx + + LPFN_ACCEPTEX accept; + LPFN_CONNECTEX connect; + LPFN_WSASENDMSG sendmsg; + LPFN_WSARECVMSG recvmsg; + + /* Thread pool stuff. + * The size of the pool is configurable. */ + DWORD numThreads; + ESAIOThread* threads; + + /* Misc stuff */ + BOOLEAN_T dbg; + BOOLEAN_T sockDbg; + + /* Counter stuff */ + ErlNifMutex* cntMtx; + ESockCounter unexpectedConnects; + ESockCounter unexpectedAccepts; + ESockCounter unexpectedWrites; + ESockCounter unexpectedReads; + ESockCounter genErrs; + ESockCounter unknownCmds; + +} ESAIOControl; + + +typedef struct __ESAIOOpDataAccept { + /* AcceptEx; ; lookup with WSAID_ACCEPTEX */ + + /* The socket, sock, is created empty and then provided as an + * argumented to AcceptEx (together with the listen socket + * and the other arguments). + * When AcceptEx has completed successfully, the socket, s, is + * usable. + * But in order for the functions sockname and peername to work, + * the SO_UPDATE_ACCEPT_CONTEXT option must be set on the + * accepted socket, sock. */ + SOCKET lsock; /* The listen socket */ + SOCKET asock; /* The "accepted" socket. + * This is created "in advance" + * and then sent to AcceptEx as an argument. + */ + char* buf; /* Size depends on domain. + * This is used for 'initial data', + * 'local address' and 'remote address'. + * We use neither of these, but the + * AcceptEx function requires this argument! + */ + ERL_NIF_TERM lSockRef; /* The listen socket */ + ERL_NIF_TERM accRef; /* The (unique) reference (ID) of the accept */ +} ESAIOOpDataAccept; + +typedef struct __ESAIOOpDataConnect { + /* ConnectEx; ; lookup with WSAID_CONNECTEX */ + + /* When ConnectEx has completed successfully, + * the socket is usable. + * *But*, in order for the functions sockname and peername to work, + * the SO_UPDATE_CONNECT_CONTEXT option must be set on the socket. + */ + ERL_NIF_TERM sockRef; /* The socket */ + ERL_NIF_TERM connRef; /* The (unique) reference (ID) + * of the connect request */ +} ESAIOOpDataConnect; + +typedef struct __ESAIOOpDataSend { + /* WSASend */ + WSABUF wbuf; /* During ongoing sending, this buffer cannot + * be de-allocated. */ + ERL_NIF_TERM sockRef; /* The socket */ + ERL_NIF_TERM sendRef; /* The (unique) reference (ID) + * of the send request */ +} ESAIOOpDataSend; + +typedef struct __ESAIOOpDataSendTo { + /* WSASendTo */ + WSABUF wbuf; /* During ongoing sending, this buffer cannot + * be de-allocated. */ + + /* Do we actually need these (remote address)? + * Debugging/logging? + */ + ESockAddress remoteAddr; + SOCKLEN_T remoteAddrLen; + + ERL_NIF_TERM sockRef; /* The socket */ + ERL_NIF_TERM sendRef; /* The (unique) reference (ID) + * of the send request */ +} ESAIOOpDataSendTo; + +typedef struct __ESAIOOpDataSendMsg { + /* WSASendMsg; lookup with WSAID_WSASENDMSG */ + WSAMSG msg; + ErlNifIOVec* iovec; + char* ctrlBuf; + ESockAddress addr; + ERL_NIF_TERM sockRef; /* The socket */ + ERL_NIF_TERM sendRef; /* The (unique) reference (ID) + * of the send request */ +} ESAIOOpDataSendMsg; + +typedef struct __ESAIOOpDataRecv { + /* WSARecv */ + DWORD toRead; /* Can be 0 (= zero) + * "just to indicate: give me what you got" + */ + ErlNifBinary buf; + ERL_NIF_TERM sockRef; /* The socket */ + ERL_NIF_TERM recvRef; /* The (unique) reference (ID) + * of the recv request */ +} ESAIOOpDataRecv; + +typedef struct __ESAIOOpDataRecvFrom { + /* WSARecvFrom */ + DWORD toRead; /* Can be 0 (= zero) + * "just to indicate: give me what you got" + */ + ErlNifBinary buf; + + ESockAddress fromAddr; + INT addrLen; // SOCKLEN_T + + ERL_NIF_TERM sockRef; /* The socket */ + ERL_NIF_TERM recvRef; /* The (unique) reference (ID) + * of the recv request */ +} ESAIOOpDataRecvFrom; + +typedef struct __ESAIOOpDataRecvMsg { + /* WSARecvMsg; lookup with WSAID_WSARECVMSG */ + + /* If we used I/O - vectors of different size(s), + * we would (maybe) need to malloc them, and simply + * have a pointer here. + */ + + WSAMSG msg; + WSABUF wbufs[1]; + ErlNifBinary data[1]; + ErlNifBinary ctrl; + ESockAddress addr; + + ERL_NIF_TERM sockRef; /* The socket */ + ERL_NIF_TERM recvRef; /* The (unique) reference (ID) + * of the recv request */ +} ESAIOOpDataRecvMsg; + +/* An 'operation', recv/recvfrom/recvmsg and send/sendto/sendmsg, + * accept or connect, is 'encoded' into this structure, which is + * "passed around". + */ +typedef struct __ESAIOOperation { + /* Has to be first and is *only* used by I/O Completion Port framework */ + WSAOVERLAPPED ol; + + /* *** Commands (=tags) *** */ +#define ESAIO_OP_NONE 0x0000 // None + /* "system" commands */ +#define ESAIO_OP_TERMINATE 0x0001 // Terminate +#define ESAIO_OP_DEBUG 0x0002 // Change debug level for thread(s) + /* Commands for establishing connections; connect and accept */ +#define ESAIO_OP_CONNECT 0x0011 // ConnectEx (function pointer) +#define ESAIO_OP_ACCEPT 0x0012 // AcceptEx (function pointer) + /* Commands for sending */ +#define ESAIO_OP_SEND 0x0021 // WSASend +#define ESAIO_OP_SENDTO 0x0022 // WSASendTo +#define ESAIO_OP_SENDMSG 0x0023 // WSASendMsg + /* Commands for receiving */ +#define ESAIO_OP_RECV 0x0031 // WSARecv +#define ESAIO_OP_RECVFROM 0x0032 // WSARecvFrom +#define ESAIO_OP_RECVMSG 0x0033 // WSARecvMsg + + unsigned int tag; /* The 'tag' of the operation */ + + ErlNifPid caller; /* Almost every request (not connect) + * operations require a caller */ + ErlNifEnv* env; /* Almost every request + * needs an environment */ + + /* Generic "data" field. + * This is different for each 'operation'! + * Also, not all opererations have this! + */ + + union { + /* +++ accept +++ */ + ESAIOOpDataAccept accept; + + /* +++ connect +++ */ + ESAIOOpDataConnect connect; + + /* +++ send +++ */ + ESAIOOpDataSend send; + + /* +++ sendto +++ */ + ESAIOOpDataSendTo sendto; + + /* +++ sendmsg +++ */ + ESAIOOpDataSendMsg sendmsg; + + /* +++ recv +++ */ + ESAIOOpDataRecv recv; + + /* +++ recvfrom +++ */ + ESAIOOpDataRecvFrom recvfrom; + + /* +++ recvmsg +++ */ + ESAIOOpDataRecvMsg recvmsg; + + } data; + +} ESAIOOperation; + + + +/* =================================================================== * + * * + * Function Forwards * + * * + * =================================================================== */ + +static ERL_NIF_TERM esaio_connect_stream(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM connRef, + ESockAddress* addrP, + SOCKLEN_T addrLen); +static ERL_NIF_TERM connect_stream_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + BOOL cres); +static ERL_NIF_TERM esaio_connect_dgram(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM connRef, + ESockAddress* addrP, + SOCKLEN_T addrLen); + +static ERL_NIF_TERM accept_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + BOOL ares, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef, + SOCKET accSock, + ErlNifPid caller); +static ERL_NIF_TERM accept_check_pending(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef); +static ERL_NIF_TERM accept_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + SOCKET accSock, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM esaio_accept_accepted(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid pid, + ERL_NIF_TERM sockRef, + SOCKET accSock); + +static ERL_NIF_TERM send_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + int send_result, + ssize_t dataSize, + BOOLEAN_T dataInTail, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + BOOLEAN_T* cleanup); +static ERL_NIF_TERM send_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + DWORD written, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM send_check_pending(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef); +static ERL_NIF_TERM send_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int saveErrno, + ERL_NIF_TERM sockRef); + +static BOOLEAN_T init_sendmsg_sockaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eMsg, + WSAMSG* msgP, + ESockAddress* addrP); +static BOOLEAN_T verify_sendmsg_iovec_size(const ESockData* dataP, + ESockDescriptor* descP, + ErlNifIOVec* iovec); +static BOOLEAN_T verify_sendmsg_iovec_tail(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM* tail); +static BOOLEAN_T check_sendmsg_iovec_overflow(ESockDescriptor* descP, + ErlNifIOVec* iovec, + ssize_t* dataSize); +static BOOLEAN_T decode_cmsghdrs(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eCMsg, + char* cmsgHdrBufP, + size_t cmsgHdrBufLen, + size_t* cmsgHdrBufUsed); +static BOOLEAN_T decode_cmsghdr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eCMsg, + char* bufP, + size_t rem, + size_t* used); +static BOOLEAN_T decode_cmsghdr_value(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + ERL_NIF_TERM eType, + ERL_NIF_TERM eValue, + char* dataP, + size_t dataLen, + size_t* dataUsedP); +static BOOLEAN_T decode_cmsghdr_data(ErlNifEnv* env, + ESockDescriptor* descP, + int level, + ERL_NIF_TERM eType, + ERL_NIF_TERM eData, + char* dataP, + size_t dataLen, + size_t* dataUsedP); +static void encode_msg(ErlNifEnv* env, + ESockDescriptor* descP, + ssize_t read, + WSAMSG* msgP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM* eMsg); +static void encode_cmsgs(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifBinary* cmsgBinP, + WSAMSG* msgP, + ERL_NIF_TERM* eCMsg); +static ERL_NIF_TERM recv_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); + +static ERL_NIF_TERM recv_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + int recv_result, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_pending(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM recv_check_failure(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + int recv_result, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recvfrom_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recvfrom_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + ERL_NIF_TERM sockRef); +static ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + int recv_result, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recvmsg_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef); +static ERL_NIF_TERM recvmsg_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + ERL_NIF_TERM sockRef); + +static void* esaio_completion_main(void* threadDataP); +static BOOLEAN_T esaio_completion_terminate(ESAIOThreadData* dataP, + OVERLAPPED* ovl); +static BOOLEAN_T esaio_completion_unknown(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + DWORD numBytes, + int error); +static void esaio_completion_fail(ErlNifEnv* env, + ESockDescriptor* descP, + const char* opStr, + int error, + BOOLEAN_T inform); + +static BOOLEAN_T esaio_completion_connect(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataConnect* opDataP, + int error); +static void esaio_completion_connect_success(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOpDataConnect* opDataP); +static void esaio_completion_connect_aborted(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOpDataConnect* opDataP); +static void esaio_completion_connect_failure(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOpDataConnect* opDataP, + int error); +static void esaio_completion_connect_completed(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOpDataConnect* opDataPP); +static void esaio_completion_connect_not_active(ESockDescriptor* descP); +static void esaio_completion_connect_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform); + +static BOOLEAN_T esaio_completion_accept(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP, + int error); +static void esaio_completion_accept_success(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP); +static void esaio_completion_accept_aborted(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP); +static void esaio_completion_accept_failure(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP, + int error); +static void esaio_completion_accept_completed(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP, + ESockRequestor* reqP); +static void esaio_completion_accept_not_active(ESockDescriptor* descP); +static void esaio_completion_accept_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform); + +static BOOLEAN_T esaio_completion_send(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataSend* opDataP, + int error); +static void esaio_completion_send_completed(ErlNifEnv* env, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* sender, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + DWORD toWrite, + ESockRequestor* reqP); +static ERL_NIF_TERM esaio_completion_send_done(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + DWORD written); +static ERL_NIF_TERM esaio_completion_send_partial(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + DWORD written); +static void esaio_completion_send_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform); +static void esaio_completion_send_not_active(ESockDescriptor* descP); +static BOOLEAN_T esaio_completion_sendto(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataSendTo* opDataP, + int error); +static void esaio_completion_sendto_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform); +static BOOLEAN_T esaio_completion_sendmsg(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataSendMsg* opDataP, + int error); +static void esaio_completion_sendmsg_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform); +static BOOLEAN_T esaio_completion_recv(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecv* opDataP, + int error); +static void esaio_completion_recv_completed(ErlNifEnv* env, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecv* opDataP, + ESockRequestor* reqP); +static ERL_NIF_TERM esaio_completion_recv_done(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecv* opDataP, + DWORD flags); +static ERL_NIF_TERM esaio_completion_recv_partial(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecv* opDataP, + ESockRequestor* reqP, + DWORD read, + DWORD flags); +static ERL_NIF_TERM esaio_completion_recv_partial_done(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecv* opDataP, + ssize_t read, + DWORD flags); +static ERL_NIF_TERM esaio_completion_recv_partial_part(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecv* opDataP, + ssize_t read, + DWORD flags); +static void esaio_completion_recv_not_active(ESockDescriptor* descP); +static void esaio_completion_recv_closed(ESockDescriptor* descP, + int error); +static void esaio_completion_recv_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform); +static BOOLEAN_T esaio_completion_recvfrom(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecvFrom* opDataP, + int error); +static void esaio_completion_recvfrom_completed(ErlNifEnv* env, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecvFrom* opDataP, + ESockRequestor* reqP); +static ERL_NIF_TERM esaio_completion_recvfrom_done(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecvFrom* opDataP, + DWORD flags); +static ERL_NIF_TERM esaio_completion_recvfrom_partial(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecvFrom* opDataP, + ESockRequestor* reqP, + DWORD read, + DWORD flags); +static void esaio_completion_recvfrom_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform); +static BOOLEAN_T esaio_completion_recvmsg(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecvMsg* opDataP, + int error); +static void esaio_completion_recvmsg_completed(ErlNifEnv* env, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecvMsg* opDataP, + ESockRequestor* reqP); +static ERL_NIF_TERM esaio_completion_recvmsg_done(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecvMsg* opDataP, + DWORD flags); +static ERL_NIF_TERM esaio_completion_recvmsg_partial(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecvMsg* opDataP, + ESockRequestor* reqP, + DWORD read, + DWORD flags); +static void esaio_completion_recvmsg_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform); + +static ERL_NIF_TERM esaio_completion_get_ovl_result_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error); + +static BOOL get_send_ovl_result(SOCKET sock, + OVERLAPPED* ovl, + DWORD* written); +static BOOL get_recv_ovl_result(SOCKET sock, + OVERLAPPED* ovl, + DWORD* read, + DWORD* flags); +static BOOL get_recvmsg_ovl_result(SOCKET sock, + OVERLAPPED* ovl, + DWORD* read); +static BOOL get_ovl_result(SOCKET sock, + OVERLAPPED* ovl, + DWORD* transfer, + DWORD* flags); + +static void esaio_completion_inc(ESAIOThreadData* dataP); + +static int esaio_add_socket(ESockDescriptor* descP); + +static void esaio_send_completion_msg(ErlNifEnv* sendEnv, + ESockDescriptor* descP, + ErlNifPid* pid, + ErlNifEnv* msgEnv, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM connRef); +static ERL_NIF_TERM mk_completion_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM completionRef); + +static void esaio_down_acceptor(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); +static void esaio_down_writer(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); +static void esaio_down_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP); + +static BOOLEAN_T do_stop(ErlNifEnv* env, + ESockDescriptor* descP); + + +/* =================================================================== * + * * + * Local (global) variables * + * * + * =================================================================== */ + +static ESAIOControl ctrl = {0}; + + + +/* =================================================================== * + * * + * Various esaio macros * + * * + * =================================================================== */ + +/* Global socket debug */ +#define SGDBG( proto ) ESOCK_DBG_PRINTF( ctrl.dbg , proto ) + +/* These are just wrapper macros for the I/O Completion Port */ +#define ESAIO_IOCP_CREATE(NT) \ + CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, (u_long) 0, (NT)) +#define ESAIO_IOCP_ADD(SOCK, DP) \ + CreateIoCompletionPort((SOCK), ctrl.cport, (ULONG*) (DP), 0) + +/* These are just wrapper macros for the I/O Completion Port queue */ +#define ESAIO_IOCQ_POP(NB, DP, OLP) \ + GetQueuedCompletionStatus(ctrl.cport, (DP), (DP), (OLP), INFINITE) +#define ESAIO_IOCQ_PUSH(OP) \ + PostQueuedCompletionStatus(ctrl.cport, 0, 0, (OVERLAPPED*) (OP)) +#define ESAIO_IOCQ_CANCEL(H, OP) \ + CancelIoEx((H), (OVERLAPPED*) (OP)) + + +/* =================================================================== * + * * + * I/O Backend exports * + * * + * =================================================================== */ + + +/* ******************************************************************* + * This function is called during (esock) nif loading + * The only argument that we actually *need* is the 'numThreads'. + * 'dataP' is just for convenience (dbg and stuff). + */ +extern +int esaio_init(unsigned int numThreads, + const ESockData* dataP) +{ + int ires, save_errno; + unsigned int i; + DWORD dummy; + GUID guidAcceptEx = WSAID_ACCEPTEX; + GUID guidConnectEx = WSAID_CONNECTEX; + GUID guidSendMsg = WSAID_WSASENDMSG; + GUID guidRecvMsg = WSAID_WSARECVMSG; + + /* Enabling this results in a core dump when calling socket:accept + * multiple times (the second call fails in env alloc).! + */ + // ctrl.dbg = TRUE; + ctrl.dbg = dataP->dbg; + ctrl.sockDbg = dataP->sockDbg; + + SGDBG( ("WIN-ESAIO", "esaio_init -> entry\r\n") ); + + ctrl.cntMtx = MCREATE("win-esaio.cnt"); + ctrl.unexpectedConnects = 0; + ctrl.unexpectedAccepts = 0; + ctrl.unexpectedWrites = 0; + ctrl.unexpectedReads = 0; + ctrl.genErrs = 0; + ctrl.unknownCmds = 0; + + /* We should actually check the value of 'numThreads' + * Since if its zero (the default), we should instead + * assign: 2 * 'number of schedulers' + * Or shall we trust the 'prim_socket' preloaded to + * select the proper value? + */ + ctrl.numThreads = (DWORD) numThreads; + + // Initialize Winsock + SGDBG( ("WIN-ESAIO", "esaio_init -> try initialize winsock\r\n") ); + ires = WSAStartup(MAKEWORD(2, 2), &ctrl.wsaData); + if (ires != NO_ERROR) { + save_errno = sock_errno(); + + esock_error_msg("Failed initialize winsock: %d" + "\r\n %s (%d)" + "\r\n", + ires, erl_errno_id(save_errno), save_errno); + + return ESAIO_ERR_WINSOCK_INIT; + } + + // Create a handle for the completion port + SGDBG( ("WIN-ESAIO", "esaio_init -> try create I/O completion port\r\n") ); + ctrl.cport = CreateIoCompletionPort(INVALID_HANDLE_VALUE, + NULL, (u_long) 0, ctrl.numThreads); + if (ctrl.cport == NULL) { + save_errno = sock_errno(); + + esock_error_msg("Failed create I/O Completion Port:" + "\r\n %s (%d)" + "\r\n", erl_errno_id(save_errno), save_errno); + + WSACleanup(); + + return ESAIO_ERR_IOCPORT_CREATE; + } + + + /* Create the "dummy" socket and then + * extract the AcceptEx and ConnectEx functions. + */ + SGDBG( ("WIN-ESAIO", "esaio_init -> try create 'dummy' socket\r\n") ); + ctrl.dummy = sock_open(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ctrl.dummy == INVALID_SOCKET) { + save_errno = sock_errno(); + + esock_error_msg("Failed create 'dummy' socket: " + "\r\n %s (%d)" + "\r\n", + erl_errno_id(save_errno), save_errno); + + WSACleanup(); + + return ESAIO_ERR_FSOCK_CREATE; + } + + + /* Load the AcceptEx function into memory using WSAIoctl. + * The WSAIoctl function is an extension of the ioctlsocket() + * function that can use overlapped I/O. + * The function's 3rd through 6th parameters are input and output + * buffers where we pass the pointer to our AcceptEx function. + * This is used so that we can call the AcceptEx function directly, + * rather than refer to the Mswsock.lib library. + */ + SGDBG( ("WIN-ESAIO", "esaio_init -> try extract 'accept' function\r\n") ); + ires = WSAIoctl(ctrl.dummy, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidAcceptEx, sizeof (guidAcceptEx), + &ctrl.accept, sizeof (ctrl.accept), + &dummy, NULL, NULL); + if (ires == SOCKET_ERROR) { + save_errno = sock_errno(); + + esock_error_msg("Failed extracting 'accept' function: %d" + "\r\n %s (%d)" + "\r\n", + ires, erl_errno_id(save_errno), save_errno); + + (void) sock_close(ctrl.dummy); + ctrl.dummy = INVALID_SOCKET; + ctrl.accept = NULL; + + WSACleanup(); + + return ESAIO_ERR_IOCTL_ACCEPT_GET; + } + + + /* Basically the same as for AcceptEx above */ + SGDBG( ("WIN-ESAIO", "esaio_init -> try extract 'connect' function\r\n") ); + ires = WSAIoctl(ctrl.dummy, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidConnectEx, sizeof (guidConnectEx), + &ctrl.connect, sizeof (ctrl.connect), + &dummy, NULL, NULL); + if (ires == SOCKET_ERROR) { + save_errno = sock_errno(); + + esock_error_msg("Failed extracting 'connect' function: %d" + "\r\n %s (%d)" + "\r\n", + ires, erl_errno_id(save_errno), save_errno); + + (void) sock_close(ctrl.dummy); + ctrl.dummy = INVALID_SOCKET; + ctrl.accept = NULL; + + WSACleanup(); + return ESAIO_ERR_IOCTL_CONNECT_GET; + } + + + /* Basically the same as for AcceptEx above */ + SGDBG( ("WIN-ESAIO", "esaio_init -> try extract 'sendmsg' function\r\n") ); + ires = WSAIoctl(ctrl.dummy, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidSendMsg, sizeof (guidSendMsg), + &ctrl.sendmsg, sizeof (ctrl.sendmsg), + &dummy, NULL, NULL); + if (ires == SOCKET_ERROR) { + save_errno = sock_errno(); + + esock_error_msg("Failed extracting 'sendmsg' function: %d" + "\r\n %s (%d)" + "\r\n", + ires, erl_errno_id(save_errno), save_errno); + + (void) sock_close(ctrl.dummy); + ctrl.dummy = INVALID_SOCKET; + ctrl.accept = NULL; + ctrl.connect = NULL; + + WSACleanup(); + return ESAIO_ERR_IOCTL_SENDMSG_GET; + } + + + /* Basically the same as for AcceptEx above */ + SGDBG( ("WIN-ESAIO", "esaio_init -> try extract 'recvmsg' function\r\n") ); + ires = WSAIoctl(ctrl.dummy, SIO_GET_EXTENSION_FUNCTION_POINTER, + &guidRecvMsg, sizeof (guidRecvMsg), + &ctrl.recvmsg, sizeof (ctrl.recvmsg), + &dummy, NULL, NULL); + if (ires == SOCKET_ERROR) { + save_errno = sock_errno(); + + esock_error_msg("Failed extracting 'recvmsg' function: %d" + "\r\n %s (%d)" + "\r\n", + ires, erl_errno_id(save_errno), save_errno); + + (void) sock_close(ctrl.dummy); + ctrl.dummy = INVALID_SOCKET; + ctrl.accept = NULL; + ctrl.connect = NULL; + ctrl.sendmsg = NULL; + + WSACleanup(); + return ESAIO_ERR_IOCTL_RECVMSG_GET; + } + + + /* + * Create the completion port thread pool. + */ + SGDBG( ("WIN-ESAIO", "esaio_init -> try alloc thread pool memory\r\n") ); + ctrl.threads = MALLOC(numThreads * sizeof(ESAIOThread)); + ESOCK_ASSERT( ctrl.threads != NULL ); + + SGDBG( ("WIN-ESAIO", "esaio_init -> basic init of thread data\r\n") ); + for (i = 0; i < numThreads; i++) { + ctrl.threads[i].data.id = i; + ctrl.threads[i].data.state = ESAIO_THREAD_STATE_UNDEF; + ctrl.threads[i].data.error = ESAIO_THREAD_ERROR_UNDEF; + ctrl.threads[i].data.env = NULL; + ctrl.threads[i].data.cnt = 0; + } + + SGDBG( ("WIN-ESAIO", "esaio_init -> try create thread(s)\r\n") ); + for (i = 0; i < numThreads; i++) { + char buf[64]; /* Buffer used for building the names */ + int j; + + /* We set these here to avoid raise with the thread. + * *If* we fail in the creation, then we set an error */ + + ctrl.threads[i].data.state = ESAIO_THREAD_STATE_INITIATING; + ctrl.threads[i].data.error = ESAIO_THREAD_ERROR_OK; + + SGDBG( ("WIN-ESAIO", + "esaio_init -> try create %d thread opts\r\n", i) ); + sprintf(buf, "esaio-opts[%d]", i); + ctrl.threads[i].optsP = TOCREATE(buf); + if (ctrl.threads[i].optsP == NULL) { + esock_error_msg("Failed create thread opts %d\r\n"); + + ctrl.threads[i].data.error = ESAIO_THREAD_ERROR_TOCREATE; + + for (j = 0; j < i; j++) { + SGDBG( ("WIN-ESAIO", + "esaio_init -> destroy thread opts %d\r\n", j) ); + TODESTROY(ctrl.threads[j].optsP); + } + return ESAIO_ERR_THREAD_OPTS_CREATE; + } + + SGDBG( ("WIN-ESAIO", + "esaio_init -> try create thread %d\r\n", i) ); + sprintf(buf, "esaio[%d]", i); + if (0 != TCREATE(buf, + &ctrl.threads[i].tid, + esaio_completion_main, + (void*) &ctrl.threads[i].data, + ctrl.threads[i].optsP)) { + + ctrl.threads[i].data.error = ESAIO_THREAD_ERROR_TCREATE; + + for (j = 0; j <= i; j++) { + SGDBG( ("WIN-ESAIO", + "esaio_init -> destroy thread opts %d\r\n", j) ); + TODESTROY(ctrl.threads[j].optsP); + } + return ESAIO_ERR_THREAD_CREATE; + } + + } + + + SGDBG( ("WIN-ESAIO", "esaio_init -> done\r\n") ); + + return ESAIO_OK; +} + + + +/* ******************************************************************* + * Finish, terminate, the ESock Async I/O backend. + * This means principally to terminate (threads of) the thread pool. + * Issue a "message" via PostQueuedCompletionStatus + * instructing all (completion) threads to terminate. + */ +extern +void esaio_finish() +{ + int t, lastThread; + + SGDBG( ("WIN-ESAIO", "esaio_finish -> entry\r\n") ); + + if (ctrl.dummy != INVALID_SOCKET) { + SGDBG( ("WIN-ESAIO", "esaio_finish -> close 'dummy' socket\r\n") ); + (void) sock_close(ctrl.dummy); + ctrl.dummy = INVALID_SOCKET; + } + + SGDBG( ("WIN-ESAIO", + "esaio_finish -> try terminate %d worker threads\r\n", + ctrl.numThreads) ); + for (t = 0, lastThread = -1, lastThread; t < ctrl.numThreads; t++) { + ESAIOOperation* opP; + BOOL qres; + + SGDBG( ("WIN-ESAIO", + "esaio_finish -> " + "[%d] try allocate (terminate-) operation\r\n", t) ); + + /* Where is this FREE'ed?? + * By the thread after it has been received? + */ + + opP = MALLOC( sizeof(ESAIOOperation) ); + + /* We should actually check that the alloc was successful + * and if not ... + * Note that this function is only called when we are terminating + * the VM. So, is there actuall any pointy in "doing" something? + * Or should we solve this another way? Instead of allocating + * a memory block; Send in a constant, ESAIO_OP_TERMINATE, + * *instead of* the overlapped pointer! + */ + + /* If this dows not work, there is not much we can do! + * And since this is *only* done when we terminate the VM... + */ + + if (opP != NULL) { + sys_memzero((char *) opP, sizeof(ESAIOOperation)); + + opP->tag = ESAIO_OP_TERMINATE; + + SGDBG( ("WIN-ESAIO", + "esaio_finish -> " + "try post (terminate-) package %d\r\n", t) ); + + qres = PostQueuedCompletionStatus(ctrl.cport, + 0, 0, (OVERLAPPED*) opP); + + if (!qres) { + int save_errno = sock_errno(); + esock_error_msg("Failed posting 'terminate' command: " + "\r\n %s (%d)" + "\r\n", erl_errno_id(save_errno), save_errno); + break; + } else { + lastThread = t; + } + + } else { + + SGDBG( ("WIN-ESAIO", + "esaio_finish -> " + "failed allocate (terminate-) operation %d\r\n", t) ); + + } + } + + if (lastThread >= 0) { + SGDBG( ("WIN-ESAIO", + "esaio_finish -> await (worker) thread(s) termination\r\n") ); + for (t = 0; t < (lastThread+1); t++) { + + SGDBG( ("WIN-ESAIO", + "esaio_finish -> try join with thread %d\r\n", t) ); + + (void) TJOIN(ctrl.threads[t].tid, NULL); + + SGDBG( ("WIN-ESAIO", "esaio_finish -> joined with %d\r\n", t) ); + + } + } + + /* This is overkill, + * since this function, esaio_finish, is called when the VM is halt'ing... + * ...but just to be a nice citizen... + */ + SGDBG( ("WIN-ESAIO", "esaio_finish -> free the thread pool data\r\n") ); + FREE( ctrl.threads ); + + SGDBG( ("WIN-ESAIO", "esaio_finish -> invalidate functions\r\n") ); + ctrl.accept = NULL; + ctrl.connect = NULL; + + SGDBG( ("WIN-ESAIO", "esaio_finish -> done\r\n") ); + + return; +} + + + +/* ******************************************************************* + * esaio_info - Return info "about" this I/O backend. + */ + +extern +ERL_NIF_TERM esaio_info(ErlNifEnv* env) +{ + ERL_NIF_TERM info, numThreads, + numUnexpAccs, numUnexpConns, numUnexpWs, numUnexpRs, + numGenErrs, + numUnknownCmds; + + numThreads = MKUI(env, ctrl.numThreads); + + MLOCK(ctrl.cntMtx); + + numUnexpConns = MKUI(env, ctrl.unexpectedConnects); + numUnexpAccs = MKUI(env, ctrl.unexpectedAccepts); + numUnexpWs = MKUI(env, ctrl.unexpectedWrites); + numUnexpRs = MKUI(env, ctrl.unexpectedReads); + numGenErrs = MKUI(env, ctrl.genErrs); + numUnknownCmds = MKUI(env, ctrl.unknownCmds); + + MUNLOCK(ctrl.cntMtx); + + { + ERL_NIF_TERM cntKeys[] = {esock_atom_num_unexpected_connects, + esock_atom_num_unexpected_accepts, + esock_atom_num_unexpected_writes, + esock_atom_num_unexpected_reads, + esock_atom_num_general_errors, + esock_atom_num_unknown_cmds}; + ERL_NIF_TERM cntVals[] = {numUnexpConns, numUnexpAccs, + numUnexpWs, numUnexpRs, + numGenErrs, + numUnknownCmds}; + unsigned int numCntKeys = NUM(cntKeys); + unsigned int numCntVals = NUM(cntVals); + ERL_NIF_TERM counters; + + ESOCK_ASSERT( numCntKeys == numCntVals ); + ESOCK_ASSERT( MKMA(env, cntKeys, cntVals, numCntKeys, &counters) ); + + { + ERL_NIF_TERM keys[] = {esock_atom_name, + esock_atom_num_threads, + esock_atom_counters}; + ERL_NIF_TERM vals[] = {MKA(env, "win_esaio"), + numThreads, + counters}; + unsigned int numKeys = NUM(keys); + unsigned int numVals = NUM(vals); + + ESOCK_ASSERT( numKeys == numVals ); + ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, &info) ); + } + } + + return info; +} + + + +/* ******************************************************************* + * esaio_open_plain - create an endpoint (from an existing fd) for + * communication. + * + * Create an *overlapped* socket, then add it to the I/O + * completion port. + */ + +extern +ERL_NIF_TERM esaio_open_plain(ErlNifEnv* env, + int domain, + int type, + int protocol, + ERL_NIF_TERM eopts, + const ESockData* dataP) +{ + /* We do not actually need the dataP since we already have the dbg... */ + BOOLEAN_T dbg = esock_open_is_debug(env, eopts, dataP->sockDbg); + BOOLEAN_T useReg = esock_open_use_registry(env, eopts, dataP->useReg); + ESockDescriptor* descP; + ERL_NIF_TERM sockRef; + int proto = protocol; + DWORD dwFlags = WSA_FLAG_OVERLAPPED; + SOCKET sock = INVALID_SOCKET; + ErlNifPid self; + int res, save_errno; + + /* Keep track of the creator + * This should not be a problem, but just in case + * the *open* function is used with the wrong kind + * of environment... + */ + ESOCK_ASSERT( enif_self(env, &self) != NULL ); + + SSDBG2( dbg, + ("WIN-ESAIO", "esaio_open_plain -> entry with" + "\r\n domain: %d" + "\r\n type: %d" + "\r\n protocol: %d" + "\r\n eopts: %T" + "\r\n", domain, type, protocol, eopts) ); + + sock = sock_open_O(domain, type, proto); + + if (sock == INVALID_SOCKET) { + save_errno = sock_errno(); + return esock_make_error_errno(env, save_errno); + } + + SSDBG2( dbg, ("WIN-ESAIO", + "esaio_open_plain -> open success: %d\r\n", sock) ); + + /* NOTE that if the protocol = 0 (default) and the domain is not + * local (AF_LOCAL) we need to explicitly get the protocol here! + */ + + if (proto == 0) + (void) esock_open_which_protocol(sock, &proto); + + /* Create and initiate the socket "descriptor" */ + descP = esock_alloc_descriptor(sock); + descP->ctrlPid = self; + descP->domain = domain; + descP->type = type; + descP->protocol = proto; + + SSDBG2( dbg, ("WIN-ESAIO", + "esaio_open_plain -> add to completion port\r\n") ); + + if (ESAIO_OK != (save_errno = esaio_add_socket(descP))) { + // See esock_dtor for what needs done! + ERL_NIF_TERM tag = esock_atom_add_socket; + ERL_NIF_TERM reason = MKA(env, erl_errno_id(save_errno)); + + SSDBG2( dbg, ("WIN-ESAIO", + "esaio_open_plain -> " + "failed adding socket to completion port: " + "%T (%d)\r\n", reason, save_errno) ); + + esock_dealloc_descriptor(env, descP); + sock_close(sock); + + /* This should really be: + * {error, {invalid, {add_to_completion_port, Reason}}} + */ + + return esock_make_error_t2r(env, tag, reason); + } + + SSDBG2( dbg, ("WIN-ESAIO", + "esaio_open_plain -> create socket ref\r\n") ); + + sockRef = enif_make_resource(env, descP); + enif_release_resource(descP); + + SSDBG2( dbg, ("WIN-ESAIO", + "esaio_open_plain -> monitor owner %T\r\n", descP->ctrlPid) ); + + ESOCK_ASSERT( MONP("esaio_open -> ctrl", + env, descP, + &descP->ctrlPid, + &descP->ctrlMon) == 0 ); + + descP->dbg = dbg; + descP->useReg = useReg; + esock_inc_socket(domain, type, proto); + + SSDBG2( dbg, ("WIN-ESAIO", + "esaio_open_plain -> maybe update registry\r\n") ); + + /* And finally (maybe) update the registry */ + if (descP->useReg) esock_send_reg_add_msg(env, descP, sockRef); + + SSDBG2( dbg, ("WIN-ESAIO", + "esaio_open_plain -> done\r\n") ); + + return esock_make_ok2(env, sockRef); +} + + + +/* ******************************************************************* + * esaio_bind - Bind a name to a socket. + * + */ +extern +ERL_NIF_TERM esaio_bind(ErlNifEnv* env, + ESockDescriptor* descP, + ESockAddress* sockAddrP, + SOCKLEN_T addrLen) +{ + if (! IS_OPEN(descP->readState)) + return esock_make_error_closed(env); + + if (sock_bind(descP->sock, &sockAddrP->sa, addrLen) < 0) { + return esock_make_error_errno(env, sock_errno()); + } + + descP->writeState |= ESOCK_STATE_BOUND; + + return esock_atom_ok; +} + + + +/* ******************************************************************* + * esaio_connect - Connect the socket to a specified address + * + * The function we use here, ConnectEx, is intended for STREAM/TCP. + * But (traditional) connect can be used with DGRAM/UDP. + * So, does ConnectEx work with DGRAM/UDP sockets? I think not. + * So we may need to test what kind of socket we have, and for + * DGRAM/UDP use the "old" connect: + * + * if (type == DGRAM) && (protocol == UDP) + * connect(...); + * else + * ConnectEx(...); + * + * Quote from the Microsoft documentation: + * "The ConnectEx function can only be used with connection-oriented sockets. + * The socket passed in the s parameter must be created with a socket type + * of SOCK_STREAM, SOCK_RDM, or SOCK_SEQPACKET." + * + * Quote from the Microsoft documentation: + * When the ConnectEx function successfully completes, the "connected socket" + * can be passed to only the following functions: + * + * * ReadFile (not provided by our API) + * * WriteFile (not provided by our API) + * * send or WSASend (send not used by us) + * * recv or WSARecv (recv not used by us) + * * TransmitFile (not provided by our API) + * * closesocket + * + * Socket used *must* be *bound* before calling ConnectEx! + */ + +extern +ERL_NIF_TERM esaio_connect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM connRef, + ESockAddress* addrP, + SOCKLEN_T addrLen) +{ + /* + * Verify that we are in the proper state + */ + + SSDBG( descP, + ("WIN-ESAIO", "esaio_connect(%T, %d) -> verify open\r\n", + sockRef, descP->sock) ); + + if (! IS_OPEN(descP->writeState)) + return esock_make_error(env, esock_atom_closed); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_connect(%T, %d) -> verify type: %s\r\n", + sockRef, descP->sock, TYPE2STR(descP->type)) ); + + switch (descP->type) { + case SOCK_STREAM: + return esaio_connect_stream(env, + descP, sockRef, connRef, + addrP, addrLen); + break; + + case SOCK_DGRAM: + return esaio_connect_dgram(env, + descP, sockRef, connRef, + addrP, addrLen); + break; + + default: + return enif_make_badarg(env); + } +} + + + +/* ******************************************************************* + * esaio_connect_stream - Connect the (stream) socket + */ +static +ERL_NIF_TERM esaio_connect_stream(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM connRef, + ESockAddress* addrP, + SOCKLEN_T addrLen) +{ + int save_errno; + BOOL cres; + ESAIOOperation* opP; + ERL_NIF_TERM eres; + ErlNifPid self; + + ESOCK_ASSERT( enif_self(env, &self) != NULL ); + + + /* ConnectEx *requires* the socket to be bound */ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_connect_stream(%T) -> verify bound\r\n", sockRef) ); + if (! IS_BOUND(descP->writeState)) + return esock_make_error(env, esock_atom_not_bound); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_connect_stream(%T) -> check if ongoing\r\n", + sockRef) ); + if (descP->connectorP != NULL) { + + /* Connect already in progress, check if its us */ + + if (COMPARE_PIDS(&self, &descP->connector.pid) != 0) { + /* *Other* process has connect in progress */ + if (addrP != NULL) { + eres = esock_make_error(env, esock_atom_already); + } else { + /* This is a bad call sequence + * - connect without an address is only allowed + * for the connecting process + */ + eres = esock_raise_invalid(env, esock_atom_state); + } + } else { + + /* TRHIS IS NOT HOW IT WORKS ON WINDOWS! + * This function should never be called *again* + * The completion message contains the full and final answer. + * No need to call again! + */ + + eres = esock_raise_invalid(env, esock_atom_state); + } + + } else if (addrP == NULL) { + + /* This is a bad call sequence + * - connect without an address is not valid on Windows. + */ + eres = esock_raise_invalid(env, esock_atom_state); + + } else { + + DWORD sentDummy = 0; + + /* No connect in progress */ + + /* Initial connect call, with address */ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_connect_stream(%T) -> allocate (connect) operation\r\n", + sockRef) ); + + opP = MALLOC( sizeof(ESAIOOperation) ); + ESOCK_ASSERT( opP != NULL); + sys_memzero((char*) opP, sizeof(ESAIOOperation)); + + opP->tag = ESAIO_OP_CONNECT; + + /* Its a bit annoying that we have to alloc an env and then + * copy the ref *before* we know that we actually need it. + * How much does this cost? + */ + + /* Initiate connector */ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_connect_stream(%T) -> initiate connector\r\n", + sockRef) ); + + descP->connector.pid = self; + ESOCK_ASSERT( MONP("esaio_connect_stream -> conn", + env, descP, + &self, &descP->connector.mon) == 0 ); + descP->connector.dataP = (void*) opP; + descP->connector.env = esock_alloc_env("connector"); + descP->connector.ref = CP_TERM(descP->connector.env, connRef); + descP->connectorP = &descP->connector; + descP->writeState |= + (ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED); + + opP->env = esock_alloc_env("esaio-connect-stream"); + opP->caller = self; + opP->data.connect.sockRef = CP_TERM(opP->env, sockRef); + opP->data.connect.connRef = CP_TERM(opP->env, connRef); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_connect_stream {%d} -> try connect\r\n", + descP->sock) ); + + /* + * BOOL LpfnConnectex( + * [in] SOCKET s, + * [in] const sockaddr *name, + * [in] int namelen, + * [in, optional] PVOID lpSendBuffer, + * [in] DWORD dwSendDataLength, + * [out] LPDWORD lpdwBytesSent, + * [in] LPOVERLAPPED lpOverlapped + * ) + */ + + cres = sock_connect_O(descP->sock, + addrP, addrLen, + &sentDummy, (OVERLAPPED*) opP); + + /* + * We need to keep using the requestor "queues"! + * That is the "only" way to handle the monitoring of + * the requestor. + * That is if the request (for instance a connect) is + * is scheduled, WSA_IO_PENDING, then we need to store + * the info about the requestor somewhere we can access it, + * in case the requestor for example dies (and we need to + * clean up). + */ + + eres = connect_stream_check_result(env, descP, opP, cres); + + } + + SSDBG( descP, ("WIN-ESAIO", "esaio_connect {%d} -> done with" + "\r\n eres: %T" + "\r\n", + descP->sock, eres) ); + + return eres; +} + + +static +ERL_NIF_TERM connect_stream_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + BOOL cres) +{ + ERL_NIF_TERM eres, tag, reason; + int save_errno; + + if (cres) { + + /* Success already! */ + + int err; + + SSDBG( descP, + ("WIN-ESAIO", + "connect_stream_check_result(%d) -> connected\r\n", + descP->sock) ); + + /* Clean up the connector stuff, no need for that anymore */ + esock_requestor_release("connect_stream_check_result -> success", + env, descP, &descP->connector); + descP->connectorP = NULL; + + /* We need to make sure peername and sockname works! */ + + SSDBG( descP, + ("WIN-ESAIO", + "connect_stream_check_result {%d} -> " + "update connect context\r\n", + descP->sock) ); + + err = ESAIO_UPDATE_CONNECT_CONTEXT( descP->sock ); + + if (err == 0) { + + descP->writeState &= + ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED); + descP->writeState |= ESOCK_STATE_CONNECTED; + + eres = esock_atom_ok; + + } else { + + save_errno = sock_errno(); + tag = esock_atom_update_connect_context; + reason = ENO2T(env, save_errno); + + SSDBG( descP, ("WIN-ESAIO", + "connect_stream_check_result(%d) -> " + "connect context update failed: %T\r\n", + descP->sock, reason) ); + + sock_close(descP->sock); + descP->writeState = ESOCK_STATE_CLOSED; + + WSACleanup(); + + eres = esock_make_error_t2r(env, tag, reason); + } + + } else { + + /* Connect returned error, check which */ + + save_errno = sock_errno(); + + if (save_errno == WSA_IO_PENDING) { + + SSDBG( descP, + ("WIN-ESAIO", + "connect_stream_check_result(%d) -> connect scheduled\r\n", + descP->sock) ); + + eres = esock_atom_completion; + + } else { + ERL_NIF_TERM ereason = ENO2T(env, save_errno); + + SSDBG( descP, ("WIN-ESAIO", + "connect_stream_check_result(%d) -> " + "connect attempt failed: %T\r\n", + descP->sock, ereason) ); + + /* Clean up the connector stuff, no need for that anymore */ + esock_requestor_release("connect_stream_check_result -> failure", + env, descP, &descP->connector); + descP->connectorP = NULL; + + /* Will an event be generetade in this case? + * Assume not => We need to clean up here! + */ + esock_clear_env("connect_stream_check_result", opP->env); + esock_free_env("connect_stream_check_result", opP->env); + FREE( opP ); + + sock_close(descP->sock); + descP->writeState = ESOCK_STATE_CLOSED; + WSACleanup(); + + eres = esock_make_error(env, ereason); + } + } + + return eres; +} + + + +/* *** esaio_connect_dgram *** + * Handle the "fake" connect of a DGRAM socket ("bind" to + * a remote address, so user can use send/recv instead of + * sendto/recvfrom). Should use sock_connect(...) + * This is corrently just a placeholder! + */ +static +ERL_NIF_TERM esaio_connect_dgram(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM connRef, + ESockAddress* addrP, + SOCKLEN_T addrLen) +{ + return enif_make_badarg(env); +} + + + +/* *** esaio_listen *** */ + + +/* ======================================================================== + */ +extern +ERL_NIF_TERM esaio_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef) +{ + ErlNifPid caller; + ERL_NIF_TERM eres; + SOCKET accSock; + ESAIOOperation* opP; + BOOLEAN_T ares; + unsigned int addrSz, bufSz; + DWORD recvBytes; + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + /* Ensure that this caller does not already have a + * (accept) request waiting */ + if (esock_acceptor_search4pid(env, descP, &caller)) { + /* Acceptor already in queue */ + return esock_raise_invalid(env, esock_atom_state); + } + + /* Accept and Read uses the same flag so they can not be simultaneous. + */ + SSDBG( descP, ("WIN-ESAIO", "esaio_accept {%d} -> verify not reading\r\n", + descP->sock) ); + if (descP->readersQ.first != NULL) + return esock_make_error_invalid(env, esock_atom_state); + + /* Should we verify domain, type and protocol? */ + + /* Allocate 'operation' */ + SSDBG( descP, ("WIN-ESAIO", + "esaio_accept {%d} -> allocate 'operation'\r\n", + descP->sock) ); + opP = MALLOC( sizeof(ESAIOOperation) ); + ESOCK_ASSERT( opP != NULL); + sys_memzero((char*) opP, sizeof(ESAIOOperation)); + + opP->tag = ESAIO_OP_ACCEPT; + + /* Its a bit annoying that we have to alloc an env and then + * copy the ref *before* we know that we actually need it. + * How much does this cost? + */ + SSDBG( descP, ("WIN-ESAIO", + "esaio_accept {%d} -> initiate 'operation'\r\n", + descP->sock) ); + opP->env = esock_alloc_env("esaio_accept - operation"); + opP->data.accept.lSockRef = CP_TERM(opP->env, sockRef); + opP->data.accept.accRef = CP_TERM(opP->env, accRef); + opP->data.accept.lsock = descP->sock; + opP->caller = caller; + + /* Create the accepting socket + * domain - should be AF_INET | AF_INET6 (sould we make sure?) + * type - should be SOCK_STREAM | SOCK_SEQPACKET (should we make sure?) + * protocol - should be IPPROTO_TCP | IPPROTO_SCTP (should we make sure?) + * See check above! + */ + SSDBG( descP, ("WIN-ESAIO", + "esaio_accept {%d} -> try create 'accepting' socket\r\n", + descP->sock) ); + accSock = sock_open(descP->domain, descP->type, descP->protocol); + if (accSock == INVALID_SOCKET) { + int save_errno = sock_errno(); + ERL_NIF_TERM reason = MKA(env, erl_errno_id(save_errno)); + ERL_NIF_TERM tag = esock_atom_create_accept_socket; + + esock_clear_env("esaio_accept - invalid accept socket", opP->env); + esock_free_env("esaio_accept - invalid accept socket", opP->env); + FREE( opP ); + WSACleanup(); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_accept {%d} -> failed create 'accepting' socket:" + "\r\n %T (%d)" + "\r\n", + descP->sock, reason, save_errno) ); + + return esock_make_error_t2r(env, tag, reason); + } + + opP->data.accept.asock = accSock; + + /* According to the (Microsoft) documentation, the buffer size of the + * local and remote address must be 16 bytes *more* than the size of + * the sockaddr structure for the transport protocol in use. + */ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_accept {%d} -> " + "try calculate address and address buffer size(s)\r\n", + descP->sock) ); + switch (descP->domain) { + case AF_INET: + addrSz = sizeof(struct sockaddr_in) + 16; + break; + case AF_INET6: + addrSz = sizeof(struct sockaddr_in6) + 16; + break; + default: + return esock_make_error_invalid(env, esock_atom_domain); + break; + } + bufSz = 2 * addrSz; + + opP->data.accept.buf = MALLOC( bufSz ); + ESOCK_ASSERT( opP->data.accept.buf != NULL); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_accept(%T, %d) -> try accept\r\n", + sockRef, descP->sock) ); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_acc_tries, &descP->accTries, 1); + + ares = sock_accept_O(descP->sock, accSock, + opP->data.accept.buf, + addrSz, + &recvBytes, + (OVERLAPPED*) opP); + + eres = accept_check_result(env, descP, opP, ares, + sockRef, accRef, accSock, caller); + + SSDBG( descP, ("WIN-ESAIO", "esaio_accept(%T, %d) -> done when" + "\r\n eres: %T" + "\r\n", sockRef, descP->sock, eres) ); + + return eres; +} + + + +static +ERL_NIF_TERM accept_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + BOOL ares, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef, + SOCKET accSock, + ErlNifPid caller) +{ + ERL_NIF_TERM eres; + + if (ares) { + + /* Success already! + * So, no need to store the data (in the "queue"). + * And then allocate and initiate the new descriptor. + */ + + eres = esaio_accept_accepted(env, descP, caller, sockRef, accSock); + + } else { + + /* Accept returned error, check which */ + + int save_errno = sock_errno(); + + /* As pointed out above, there are basically two kinds of errors: + * 1) Pending: + * An overlapped operation was successfully initiated. + * Completion will be "indicated" at a later time. + * 2) An actual error + */ + + if (save_errno == WSA_IO_PENDING) { + + /* We need to store the data in the queue! */ + + eres = accept_check_pending(env, descP, opP, caller, + sockRef, accRef); + + } else { + + eres = accept_check_fail(env, descP, opP, save_errno, + accSock, sockRef); + + } + } + + SSDBG( descP, + ("WIN-ESAIO", + "accept_check_result(%T, %d) -> done with" + "\r\n result: %T" + "\r\n", sockRef, descP->sock, eres) ); + + return eres; +} + + + +static +ERL_NIF_TERM accept_check_pending(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM accRef) +{ + SSDBG( descP, + ("WIN-ESAIO", + "accept_check_pending(%T, %d) -> entry with" + "\r\n accRef: %T" + "\r\n", sockRef, descP->sock, accRef) ); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_acc_waits, &descP->accWaits, 1); + + if (descP->acceptorsQ.first == NULL) + descP->readState |= ESOCK_STATE_ACCEPTING; + + /* Will be picked up by the (worker) threads when the event comes */ + esock_acceptor_push(env, descP, caller, accRef, opP); + + return esock_atom_completion; + +} + + +static +ERL_NIF_TERM accept_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + SOCKET accSock, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM reason; + + SSDBG( descP, + ("WIN-ESAIO", + "accept_check_fail(%T, %d) -> entry with" + "\r\n errno: %d" + "\r\n (acc) socket: %d" + "\r\n", sockRef, descP->sock, saveErrno, accSock) ); + + reason = MKA(env, erl_errno_id(saveErrno)); + + /* Will an event be generetade in this case? + * Assume not => We need to clean up here! + */ + esock_clear_env("esaio_accept", opP->env); + esock_free_env("esaio_accept", opP->env); + FREE( opP->data.accept.buf ); + FREE( opP ); + + sock_close(accSock); + WSACleanup(); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_acc_fails, &descP->accFails, 1); + + return esock_make_error(env, reason); +} + + + +/* *** esaio_accept_accepted *** + * + * Generic function handling a successful accept. + */ +static +ERL_NIF_TERM esaio_accept_accepted(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid pid, + ERL_NIF_TERM sockRef, + SOCKET accSock) +{ + ESockDescriptor* accDescP; + ERL_NIF_TERM accRef; + int save_errno; + + /* + * We got one + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_accept_accepted(%T, %d) -> entry with" + "\r\n (accept) socket: %T" + "\r\n", sockRef, descP->sock, accSock) ); + + // Allocate the descriptor + accDescP = esock_alloc_descriptor(accSock); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_accept_accepted(%T, %d) -> add to completion port\r\n", + sockRef, descP->sock) ); + + if (ESAIO_OK != (save_errno = esaio_add_socket(accDescP))) { + // See esock_dtor for what needs done! + ERL_NIF_TERM tag = esock_atom_add_socket; + ERL_NIF_TERM reason = MKA(env, erl_errno_id(save_errno)); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_acc_fails, &descP->accFails, 1); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_accept_accepted(%T, %d) -> " + "failed adding (accepted) socket to completion port: " + "%T (%d)\r\n", sockRef, descP->sock, reason, save_errno) ); + + esock_dealloc_descriptor(env, accDescP); + sock_close(accSock); + + /* This should really be: + * {error, {invalid, {add_to_completion_port, Reason}}} + */ + + return esock_make_error_t2r(env, tag, reason); + } + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_acc_success, &descP->accSuccess, 1); + + accDescP->domain = descP->domain; + accDescP->type = descP->type; + accDescP->protocol = descP->protocol; + + MLOCK(descP->writeMtx); + + accDescP->rBufSz = descP->rBufSz; // Inherit buffer size + accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer size + accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size + accDescP->iow = descP->iow; // Inherit iow + accDescP->dbg = descP->dbg; // Inherit debug flag + accDescP->useReg = descP->useReg; // Inherit useReg flag + esock_inc_socket(accDescP->domain, accDescP->type, accDescP->protocol); + + accRef = enif_make_resource(env, accDescP); + enif_release_resource(accDescP); + + accDescP->ctrlPid = pid; + /* pid has actually been compared equal to self() + * in this code path just a little while ago + */ + ESOCK_ASSERT( MONP("esaio_accept_accepted -> ctrl", + env, accDescP, + &accDescP->ctrlPid, + &accDescP->ctrlMon) == 0 ); + + accDescP->writeState |= ESOCK_STATE_CONNECTED; + + MUNLOCK(descP->writeMtx); + + /* And finally (maybe) update the registry */ + if (descP->useReg) esock_send_reg_add_msg(env, descP, accRef); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_accept_accepted(%T, %d) -> done\r\n", + sockRef, descP->sock) ); + + return esock_make_ok2(env, accRef); + +} + + + +/* ======================================================================== + * Do the actual send. + * Do some initial writer checks, do the actual send and then + * analyze the result. + * + * The following flags are "valid": + * + * MSG_DONTROUTE, MSG_PARTIAL, and MSG_OOB + * + */ +extern +ERL_NIF_TERM esaio_send(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* sndDataP, + int flags) +{ + ErlNifPid caller; + ERL_NIF_TERM eres; + BOOLEAN_T cleanup = FALSE; + int wres; + DWORD toWrite; + char* buf; + DWORD f = (DWORD) flags; + ESAIOOperation* opP; + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + if (! IS_OPEN(descP->writeState)) { + ESOCK_EPRINTF("esaio_send(%T, %d) -> NOT OPEN\r\n", + sockRef, descP->sock); + return esock_make_error_closed(env); + } + + /* Connect and Write can not be simultaneous? */ + if (descP->connectorP != NULL) { + ESOCK_EPRINTF("esaio_send(%T, %d) -> CONNECTING\r\n", + sockRef, descP->sock); + return esock_make_error_invalid(env, esock_atom_state); + } + + /* Ensure that this caller does not *already* have a + * (send) request waiting */ + if (esock_writer_search4pid(env, descP, &caller)) { + /* Sender already in queue */ + ESOCK_EPRINTF("esaio_send(%T, %d) -> ALREADY SENDING\r\n", + sockRef, descP->sock); + return esock_raise_invalid(env, esock_atom_state); + } + + /* This is a size check, + * to ensure we do not try to send something *to* large */ + toWrite = (DWORD) sndDataP->size; + if ((size_t) toWrite != sndDataP->size) + return esock_make_error_invalid(env, esock_atom_data_size); + + /* Once the send function has been called, this memory + * is "owned" by the system. That is, we cannot free it + * (or do anything with it) until the *operation* has completed, + * so the free is done by the thread(s). + */ + buf = MALLOC( toWrite ); + ESOCK_ASSERT( buf != NULL ); + sys_memcpy(buf, sndDataP->data, toWrite); + + opP = MALLOC( sizeof(ESAIOOperation) ); + ESOCK_ASSERT( opP != NULL); + sys_memzero((char*) opP, sizeof(ESAIOOperation)); + + opP->tag = ESAIO_OP_SEND; + /* Its a bit annoying that we have to alloc an env and then + * copy the ref *before* we know that we actually need it. + * But after the call is to late, so... + */ + opP->env = esock_alloc_env("esaio-send - operation"); + opP->data.send.sendRef = CP_TERM(opP->env, sendRef); + opP->data.send.sockRef = CP_TERM(opP->env, sockRef); + opP->data.send.wbuf.buf = buf; + opP->data.send.wbuf.len = toWrite; + opP->caller = caller; + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_tries, &descP->writeTries, 1); + + wres = sock_send_O(descP->sock, &opP->data.send.wbuf, f, (OVERLAPPED*) opP); + + eres = send_check_result(env, descP, opP, caller, + wres, toWrite, FALSE, + sockRef, sendRef, &cleanup); + + if (cleanup) { + + /* "Manually" allocated buffer */ + FREE( opP->data.send.wbuf.buf ); + + esock_clear_env("esaio_send - cleanup", opP->env); + esock_free_env("esaio_send - cleanup", opP->env); + + FREE( opP ); + + } + + SSDBG( descP, + ("WIN-ESAIO", "esaio_send {%d} -> done (%s)" + "\r\n %T" + "\r\n", descP->sock, B2S(cleanup), eres) ); + + return eres; +} + + + +/* ======================================================================== + * Do the actual send. + * Do some initial writer checks, do the actual send and then + * analyze the result. + * + * The following flags are "valid": + * + * MSG_DONTROUTE, MSG_PARTIAL, and MSG_OOB + * + * "Explicit binding is discouraged for client applications." + */ + +extern +ERL_NIF_TERM esaio_sendto(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ErlNifBinary* sndDataP, + int flags, + ESockAddress* toAddrP, + SOCKLEN_T toAddrLen) +{ + ErlNifPid caller; + ERL_NIF_TERM eres; + BOOLEAN_T cleanup = FALSE; + int wres; + DWORD toWrite; + char* buf; + DWORD f = (DWORD) flags; + ESAIOOperation* opP; + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + if (! IS_OPEN(descP->writeState)) + return esock_make_error_closed(env); + + /* Connect and Write can not be simultaneous? */ + if (descP->connectorP != NULL) + return esock_make_error_invalid(env, esock_atom_state); + + /* Empty address to allowed */ + if (toAddrP == NULL) + return esock_make_invalid(env, esock_atom_sockaddr); + + /* Ensure that this caller does not *already* have a + * (send) request waiting */ + if (esock_writer_search4pid(env, descP, &caller)) { + /* Sender already in queue */ + return esock_raise_invalid(env, esock_atom_state); + } + + /* This is a size check, + * to ensure we do not try to send something *to* large */ + toWrite = (DWORD) sndDataP->size; + if ((size_t) toWrite != sndDataP->size) + return esock_make_error_invalid(env, esock_atom_data_size); + + /* Once the send function has been called, this memory + * (buf) "belongs" to the "system" (so no need to free it). + */ + buf = MALLOC( toWrite ); + ESOCK_ASSERT( buf != NULL ); + sys_memcpy(buf, sndDataP->data, toWrite); + + opP = MALLOC( sizeof(ESAIOOperation) ); + ESOCK_ASSERT( opP != NULL); + sys_memzero((char*) opP, sizeof(ESAIOOperation)); + + opP->tag = ESAIO_OP_SENDTO; + /* Its a bit annoying that we have to alloc an env and then + * copy the ref *before* we know that we actually need it. + * But after the call is to late, so... + */ + opP->env = esock_alloc_env("esaio-sendto - operation"); + opP->data.sendto.sendRef = CP_TERM(opP->env, sendRef); + opP->data.sendto.sockRef = CP_TERM(opP->env, sockRef); + opP->data.sendto.wbuf.buf = buf; + opP->data.sendto.wbuf.len = toWrite; + opP->data.sendto.remoteAddr = *toAddrP; // Do we need this? + opP->data.sendto.remoteAddrLen = toAddrLen;// Do we need this? + opP->caller = caller; + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_tries, &descP->writeTries, 1); + + wres = sock_sendto_O(descP->sock, &opP->data.sendto.wbuf, f, + (struct sockaddr*) toAddrP, toAddrLen, + (OVERLAPPED*) opP); + + eres = send_check_result(env, descP, opP, caller, + wres, toWrite, FALSE, + sockRef, sendRef, &cleanup); + + if (cleanup) { + + /* "Manually" allocated buffer */ + FREE( opP->data.sendto.wbuf.buf ); + + esock_clear_env("esaio_sendto - cleanup", opP->env); + esock_free_env("esaio_sendto - cleanup", opP->env); + + FREE( opP ); + + } + + SSDBG( descP, + ("WIN-ESAIO", "esaio_sendto {%d} -> done (%s)" + "\r\n %T" + "\r\n", descP->sock, B2S(cleanup), eres) ); + + return eres; +} + + + +/* ======================================================================== + * Do the actual sendmsg. + * Do some initial writer checks, do the actual send and then + * analyze the result. + * + * The following flags are "valid": + * + * MSG_DONTROUTE, MSG_PARTIAL, and MSG_OOB + * + * "Explicit binding is discouraged for client applications." + * + * Also, according to Microsoft documentation: + * + * "can only be used with datagrams and raw sockets." + * + * So, should we check, or let the user crash and burn? + * + * Note that this operation *only* works for socket + * of types SOCK_DGRAM and SOCK_RAW! Should we check + * and throw 'enotsup' otherwise? Would make testing + * easier... + */ + +extern +ERL_NIF_TERM esaio_sendmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + ERL_NIF_TERM eMsg, + int flags, + ERL_NIF_TERM eIOV, + const ESockData* dataP) +{ + ErlNifPid caller; + ERL_NIF_TERM eres; + BOOLEAN_T cleanup = FALSE; + int wres; + ERL_NIF_TERM tail; + ERL_NIF_TERM eAddr, eCtrl; + ssize_t dataSize; + size_t ctrlBufLen, ctrlBufUsed; + WSABUF* wbufs = NULL; + ESAIOOperation* opP = NULL; + + SSDBG( descP, ("WIN-ESAIO", "esaio_sendmsg(%T, %d) -> entry with" + "\r\n", sockRef, descP->sock) ); + + /* This *only* works on socket type(s) DGRAM or RAW. + * Other socket types results in einval, which is not very + * helpful. So, in order to, atleast, help with testing, + * we do this... + */ + if (! ((descP->type == SOCK_DGRAM) || (descP->type == SOCK_RAW))) { + return enif_raise_exception(env, MKA(env, "notsup")); + } + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + if (! IS_OPEN(descP->writeState)) + return esock_make_error_closed(env); + + /* Connect and Write can not be simultaneous? */ + if (descP->connectorP != NULL) + return esock_make_error_invalid(env, esock_atom_state); + + /* Ensure that this caller does not *already* have a + * (send) request waiting */ + if (esock_writer_search4pid(env, descP, &caller)) { + /* Sender already in queue */ + return esock_raise_invalid(env, esock_atom_state); + } + + opP = MALLOC( sizeof(ESAIOOperation) ); + ESOCK_ASSERT( opP != NULL); + sys_memzero((char*) opP, sizeof(ESAIOOperation)); + + opP->tag = ESAIO_OP_SENDMSG; + + if (! init_sendmsg_sockaddr(env, descP, eMsg, + &opP->data.sendmsg.msg, + &opP->data.sendmsg.addr)) { + + FREE( opP ); + + return esock_make_invalid(env, esock_atom_addr); + } + + /* Its a bit annoying that we have to alloc an env and then + * copy the ref *before* we know that we actually need it. + * How much does this cost? + */ + opP->env = esock_alloc_env("esaio_sendmsg - operation"); + + /* Extract the *mandatory* 'iov', which must be an erlang:iovec(), + * from which we take at most IOV_MAX binaries. + * The env *cannot* be NULL because we don't actually know if + * the send succeeds *now*. It could be sceduled! + */ + if ((! enif_inspect_iovec(opP->env, + dataP->iov_max, eIOV, &tail, + &opP->data.sendmsg.iovec))) { + + SSDBG( descP, ("WIN-ESAIO", + "essaio_sendmsg {%d} -> not an iov\r\n", + descP->sock) ); + + esock_free_env("esaio-sendmsg - iovec failure", opP->env); + FREE( opP ); + + return esock_make_error_invalid(env, esock_atom_iov); + } + + SSDBG( descP, ("WIN-ESAIO", "esaio_sendmsg {%d} ->" + "\r\n iovcnt: %lu" + "\r\n tail: %s" + "\r\n", descP->sock, + (unsigned long) opP->data.sendmsg.iovec->iovcnt, + B2S(! enif_is_empty_list(opP->env, tail))) ); + + + /* We now have an allocated iovec - verify vector size */ + + if (! verify_sendmsg_iovec_size(dataP, descP, opP->data.sendmsg.iovec)) { + + /* We can not send the whole packet in one sendmsg() call */ + SSDBG( descP, ("WIN-ESAIO", + "esaio_sendmsg {%d} -> iovcnt > iov_max\r\n", + descP->sock) ); + + // No need - belongs to op env: FREE_IOVEC( opP->data.sendmsg.iovec ); + esock_free_env("esaio-sendmsg - iovec failure", opP->env); + FREE( opP ); + + return esock_make_error_invalid(env, esock_atom_iov); + } + + + /* Verify that we can send the entire message. + * On DGRAM the tail must be "empty" (= everything must fit in one message). + */ + if (! verify_sendmsg_iovec_tail(opP->env, descP, &tail)) { + + // No need - belongs to op env: FREE_IOVEC( opP->data.sendmsg.iovec ); + esock_free_env("esaio-sendmsg - iovec tail failure", opP->env); + FREE( opP ); + + return esock_make_error_invalid(env, esock_atom_iov); + + } + + if (! check_sendmsg_iovec_overflow(descP, + opP->data.sendmsg.iovec, &dataSize)) { + + // No need - belongs to op env: FREE_IOVEC( opP->data.sendmsg.iovec ); + esock_free_env("esaio-sendmsg - iovec size failure", opP->env); + FREE( opP ); + + return esock_make_error_invalid(env, esock_atom_iov); + + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_sendmsg {%d} -> iovec size verified" + "\r\n iov length: %lu" + "\r\n data size: %u" + "\r\n", + descP->sock, + (unsigned long) opP->data.sendmsg.iovec->iovcnt, + (long) dataSize) ); + + wbufs = MALLOC(opP->data.sendmsg.iovec->iovcnt * sizeof(WSABUF)); + ESOCK_ASSERT( wbufs != NULL ); + for (int i = 0; i < opP->data.sendmsg.iovec->iovcnt; i++) { + wbufs[i].len = opP->data.sendmsg.iovec->iov[i].iov_len; + wbufs[i].buf = opP->data.sendmsg.iovec->iov[i].iov_base; + } + + opP->data.sendmsg.msg.lpBuffers = wbufs; + opP->data.sendmsg.msg.dwBufferCount = opP->data.sendmsg.iovec->iovcnt; + + /* And now for the control headers - some default first */ + eCtrl = esock_atom_undefined; + ctrlBufLen = 0; + opP->data.sendmsg.ctrlBuf = NULL; + + /* Extract the *optional* 'ctrl' out of the eMsg map */ + if (GET_MAP_VAL(env, eMsg, esock_atom_ctrl, &eCtrl)) { + ctrlBufLen = descP->wCtrlSz; + opP->data.sendmsg.ctrlBuf = (char*) MALLOC(ctrlBufLen); + ESOCK_ASSERT( opP->data.sendmsg.ctrlBuf != NULL ); + } + + SSDBG( descP, ("WIN-ESAIO", "esaio_sendmsg {%d} -> optional ctrl: " + "\r\n ctrlBuf: %p" + "\r\n ctrlBufLen: %lu" + "\r\n eCtrl: %T" + "\r\n", descP->sock, + opP->data.sendmsg.ctrlBuf, + (unsigned long) ctrlBufLen, eCtrl) ); + + if (opP->data.sendmsg.ctrlBuf != NULL) { + if (! decode_cmsghdrs(env, descP, + eCtrl, + opP->data.sendmsg.ctrlBuf, ctrlBufLen, + &ctrlBufUsed)) { + + FREE( opP->data.sendmsg.ctrlBuf ); + FREE( opP->data.sendmsg.msg.lpBuffers ); + // No need - belongs to op env: FREE_IOVEC( opP->data.sendmsg.iovec ); + esock_free_env("esaio-sendmsg - iovec size failure", opP->env); + FREE( opP ); + + return esock_make_invalid(env, esock_atom_ctrl); + } + } else { + ctrlBufUsed = 0; + } + opP->data.sendmsg.msg.Control.len = ctrlBufUsed; + opP->data.sendmsg.msg.Control.buf = opP->data.sendmsg.ctrlBuf; + + /* We do not yet handle the flags (see function header above), + * so zero it just in case. */ + opP->data.sendmsg.msg.dwFlags = 0; + + opP->tag = ESAIO_OP_SENDMSG; + opP->caller = caller; + opP->data.sendmsg.sockRef = CP_TERM(opP->env, sockRef); + opP->data.sendmsg.sendRef = CP_TERM(opP->env, sendRef); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_tries, &descP->writeTries, 1); + + wres = sock_sendmsg_O(descP->sock, &opP->data.sendmsg.msg, flags, + (OVERLAPPED*) opP); + + eres = send_check_result(env, descP, opP, caller, + wres, dataSize, + (! enif_is_empty_list(opP->env, tail)), + sockRef, sendRef, &cleanup); + + if (cleanup) { + + /* "Manually" allocated buffers */ + FREE( opP->data.sendmsg.msg.lpBuffers ); + if (opP->data.sendmsg.ctrlBuf != NULL) + FREE( opP->data.sendmsg.ctrlBuf ); + + /* The i/o vector belongs to the op env, + * so it goes when the env goes. + */ + esock_clear_env("esaio_sendto - cleanup", opP->env); + esock_free_env("esaio_sendto - cleanup", opP->env); + + FREE( opP ); + + } + + SSDBG( descP, + ("WIN-ESAIO", "esaio_sendmsg {%d} -> done (%s)" + "\r\n %T" + "\r\n", descP->sock, B2S(cleanup), eres) ); + + return eres; +} + + +static +BOOLEAN_T init_sendmsg_sockaddr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eMsg, + WSAMSG* msgP, + ESockAddress* addrP) +{ + ERL_NIF_TERM eAddr; + + if (! GET_MAP_VAL(env, eMsg, esock_atom_addr, &eAddr)) { + + SSDBG( descP, ("WIN-ESAIO", + "init_sendmsg_sockaddr {%d} -> no address\r\n", + descP->sock) ); + + msgP->name = NULL; + msgP->namelen = 0; + + } else { + + SSDBG( descP, ("WIN-ESAIO", "init_sendmsg_sockaddr {%d} ->" + "\r\n address: %T" + "\r\n", descP->sock, eAddr) ); + + msgP->name = (void*) addrP; + msgP->namelen = sizeof(ESockAddress); + sys_memzero((char *) msgP->name, msgP->namelen); + + if (! esock_decode_sockaddr(env, eAddr, + (ESockAddress*) msgP->name, + (SOCKLEN_T*) &msgP->namelen)) { + + SSDBG( descP, ("WIN-ESAIO", + "init_sendmsg_sockaddr {%d} -> invalid address\r\n", + descP->sock) ); + + return FALSE; + } + } + + return TRUE; + +} + + + +static +BOOLEAN_T verify_sendmsg_iovec_size(const ESockData* dataP, + ESockDescriptor* descP, + ErlNifIOVec* iovec) +{ + if (iovec->iovcnt > dataP->iov_max) { + if (descP->type == SOCK_STREAM) { + iovec->iovcnt = dataP->iov_max; + } else { + return FALSE; + } + } + + return TRUE; +} + + + +static +BOOLEAN_T verify_sendmsg_iovec_tail(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM* tail) +{ + ERL_NIF_TERM h, t, tmp = *tail; + ErlNifBinary bin; + + /* Find out if there is remaining data in the tail. + * Skip empty binaries otherwise break. + * If 'tail' after loop exit is the empty list + * there was no more data. Otherwise there is more + * data or the 'iov' is invalid. + */ + + for (;;) { + if (enif_get_list_cell(env, tmp, &h, &t) && + enif_inspect_binary(env, h, &bin) && + (bin.size == 0)) { + tmp = t; + continue; + } else + break; + } + + *tail = tmp; + + if ((! enif_is_empty_list(env, tmp)) && + (descP->type != SOCK_STREAM)) { + + /* We can not send the whole packet in one sendmsg() call */ + SSDBG( descP, ("WIN-ESAIO", + "essio_sendmsg {%d} -> invalid tail\r\n", + descP->sock) ); + + return FALSE; + } + + return TRUE; + +} + + + +static +BOOLEAN_T check_sendmsg_iovec_overflow(ESockDescriptor* descP, + ErlNifIOVec* iovec, + ssize_t* dataSize) +{ + ssize_t dsz = 0; + size_t i; + + for (i = 0; i < iovec->iovcnt; i++) { + size_t len = iovec->iov[i].iov_len; + dsz += len; + if (dsz < len) { + + /* Overflow */ + + SSDBG( descP, ("WIN-ESAIO", + "verify_sendmsg_iovec_size {%d} -> Overflow" + "\r\n i: %lu" + "\r\n len: %lu" + "\r\n dataSize: %ld" + "\r\n", descP->sock, (unsigned long) i, + (unsigned long) len, (long) dsz) ); + + *dataSize = dsz; + + return FALSE; + + } + } + + *dataSize = dsz; + + return TRUE; + +} + + + +/* *** Control message utility functions *** */ + +/* +++ decode_cmsghdrs +++ + * + * Decode a list of cmsg(). There can be 0 or more "blocks". + * + * Each element can either be a (erlang) map that needs to be decoded, + * or a (erlang) binary that just needs to be appended to the control + * buffer. + * + * Our "problem" is that we have no idea how much memory we actually need. + * + */ + +static +BOOLEAN_T decode_cmsghdrs(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eCMsg, + char* cmsgHdrBufP, + size_t cmsgHdrBufLen, + size_t* cmsgHdrBufUsed) +{ + ERL_NIF_TERM elem, tail, list; + char* bufP; + size_t rem, used, totUsed = 0; + unsigned int len; + int i; + + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdrs {%d} -> entry with" + "\r\n eCMsg: %T" + "\r\n cmsgHdrBufP: 0x%lX" + "\r\n cmsgHdrBufLen: %d" + "\r\n", descP->sock, + eCMsg, cmsgHdrBufP, cmsgHdrBufLen) ); + + if (! GET_LIST_LEN(env, eCMsg, &len)) + return FALSE; + + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdrs {%d} -> list length: %d\r\n", + descP->sock, len) ); + + for (i = 0, list = eCMsg, rem = cmsgHdrBufLen, bufP = cmsgHdrBufP; + i < len; i++) { + + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdrs {%d} -> process elem %d:" + "\r\n (buffer) rem: %u" + "\r\n (buffer) totUsed: %u" + "\r\n", descP->sock, i, rem, totUsed) ); + + /* Extract the (current) head of the (cmsg hdr) list */ + if (! GET_LIST_ELEM(env, list, &elem, &tail)) + return FALSE; + + used = 0; // Just in case... + if (! decode_cmsghdr(env, descP, elem, bufP, rem, &used)) + return FALSE; + +#ifdef __WIN32__ + bufP = CHARP( bufP + used ); +#else + bufP = CHARP( ULONG(bufP) + used ); +#endif + rem = SZT( rem - used ); + list = tail; + totUsed += used; + + } + + *cmsgHdrBufUsed = totUsed; + + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdrs {%d} -> done" + "\r\n all %u ctrl headers processed" + "\r\n totUsed = %lu\r\n", + descP->sock, len, (unsigned long) totUsed) ); + + return TRUE; +} + + + +/* +++ decode_cmsghdr +++ + * + * Decode one cmsg(). Put the "result" into the buffer and advance the + * pointer (of the buffer) afterwards. Also update 'rem' accordingly. + * But before the actual decode, make sure that there is enough room in + * the buffer for the cmsg header (sizeof(*hdr) < rem). + * + * The eCMsg should be a map with three fields: + * + * level :: socket | protocol() | integer() + * type :: atom() | integer() + * What values are valid depend on the level + * data :: binary() | integer() | boolean() + * The type of the data depends on + * or level and type, but can be a binary, + * which means that the data is already coded. + * value :: term() Which is a term matching the decode function + */ + +static +BOOLEAN_T decode_cmsghdr(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM eCMsg, + char* bufP, + size_t rem, + size_t* used) +{ + ERL_NIF_TERM eLevel, eType, eData, eValue; + int level; + + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> entry with" + "\r\n eCMsg: %T" + "\r\n", descP->sock, eCMsg) ); + + // Get 'level' field + if (! GET_MAP_VAL(env, eCMsg, esock_atom_level, &eLevel)) + return FALSE; + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> eLevel: %T" + "\r\n", descP->sock, eLevel) ); + + // Get 'type' field + if (! GET_MAP_VAL(env, eCMsg, esock_atom_type, &eType)) + return FALSE; + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> eType: %T" + "\r\n", descP->sock, eType) ); + + // Decode Level + if (! esock_decode_level(env, eLevel, &level)) + return FALSE; + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d}-> level: %d\r\n", + descP->sock, level) ); + + // Get 'data' field + if (! GET_MAP_VAL(env, eCMsg, esock_atom_data, &eData)) { + + // Get 'value' field + if (! GET_MAP_VAL(env, eCMsg, esock_atom_value, &eValue)) + return FALSE; + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> eValue: %T" + "\r\n", descP->sock, eValue) ); + + // Decode Value + if (! decode_cmsghdr_value(env, descP, level, eType, eValue, + bufP, rem, used)) + return FALSE; + + } else { + + // Verify no 'value' field + if (GET_MAP_VAL(env, eCMsg, esock_atom_value, &eValue)) + return FALSE; + + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d} -> eData: %T" + "\r\n", descP->sock, eData) ); + + // Decode Data + if (! decode_cmsghdr_data(env, descP, level, eType, eData, + bufP, rem, used)) + return FALSE; + } + + SSDBG( descP, ("WIN-ESAIO", "decode_cmsghdr {%d}-> used: %lu\r\n", + descP->sock, (unsigned long) *used) ); + + return TRUE; +} + + +static +BOOLEAN_T 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) +{ + int type; + struct cmsghdr* cmsgP = (struct cmsghdr *) bufP; + ESockCmsgSpec* cmsgTable; + ESockCmsgSpec* cmsgSpecP = NULL; + size_t num = 0; + + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_value {%d} -> entry \r\n" + " eType: %T\r\n" + " eValue: %T\r\n", + descP->sock, eType, eValue) ); + + // We have decode functions only for symbolic (atom) types + if (! IS_ATOM(env, eType)) { + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_value {%d} -> FALSE:\r\n" + " eType not an atom\r\n", + descP->sock) ); + return FALSE; + } + + /* Try to look up the symbolic type + */ + if (((cmsgTable = esock_lookup_cmsg_table(level, &num)) == NULL) || + ((cmsgSpecP = esock_lookup_cmsg_spec(cmsgTable, num, eType)) == NULL) || + (cmsgSpecP->decode == NULL)) { + + /* We found no table for this level, + * we found no symbolic type in the level table, + * or no decode function for this type + */ + + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_value {%d} -> FALSE:\r\n" + " cmsgTable: %p\r\n" + " cmsgSpecP: %p\r\n", + descP->sock, cmsgTable, cmsgSpecP) ); + + return FALSE; + } + + if (! cmsgSpecP->decode(env, eValue, cmsgP, rem, usedP)) { + // Decode function failed + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_value {%d} -> FALSE:\r\n" + " decode function failed\r\n", + descP->sock) ); + return FALSE; + } + + // Successful decode + + type = cmsgSpecP->type; + + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_value {%d} -> TRUE:\r\n" + " level: %d\r\n" + " type: %d\r\n", + " *usedP: %lu\r\n", + descP->sock, level, type, (unsigned long) *usedP) ); + + cmsgP->cmsg_level = level; + cmsgP->cmsg_type = type; + return TRUE; +} + + +static +BOOLEAN_T 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) +{ + int type; + ErlNifBinary bin; + struct cmsghdr* cmsgP = (struct cmsghdr *) bufP; + ESockCmsgSpec* cmsgSpecP = NULL; + + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_data {%d} -> entry \r\n" + " eType: %T\r\n" + " eData: %T\r\n", + descP->sock, eType, eData) ); + + // Decode Type + if (! GET_INT(env, eType, &type)) { + ESockCmsgSpec* cmsgTable = NULL; + size_t num = 0; + + /* Try to look up the symbolic (atom) type + */ + if ((! IS_ATOM(env, eType)) || + ((cmsgTable = esock_lookup_cmsg_table(level, &num)) == NULL) || + ((cmsgSpecP = esock_lookup_cmsg_spec(cmsgTable, num, eType)) == NULL)) { + /* Type was not an atom, + * we found no table for this level, + * or we found no symbolic type in the level table + */ + + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_data {%d} -> FALSE:\r\n" + " cmsgTable: %p\r\n" + " cmsgSpecP: %p\r\n", + descP->sock, cmsgTable, cmsgSpecP) ); + return FALSE; + } + + type = cmsgSpecP->type; + } + + // Decode Data + if (GET_BIN(env, eData, &bin)) { + void *p; + + p = esock_init_cmsghdr(cmsgP, rem, bin.size, usedP); + if (p == NULL) { + /* No room for the data + */ + + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_data {%d} -> FALSE:\r\n" + " rem: %lu\r\n" + " bin.size: %lu\r\n", + descP->sock, + (unsigned long) rem, + (unsigned long) bin.size) ); + return FALSE; + } + + // Copy the binary data + sys_memcpy(p, bin.data, bin.size); + + } else if ((! esock_cmsg_decode_int(env, eData, cmsgP, rem, usedP)) && + (! esock_cmsg_decode_bool(env, eData, cmsgP, rem, usedP))) { + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_data {%d} -> FALSE\r\n", + descP->sock) ); + return FALSE; + } + + // Successful decode + + SSDBG( descP, + ("WIN-ESAIO", + "decode_cmsghdr_data {%d} -> TRUE:\r\n" + " level: %d\r\n" + " type: %d\r\n" + " *usedP: %lu\r\n", + descP->sock, level, type, (unsigned long) *usedP) ); + + cmsgP->cmsg_level = level; + cmsgP->cmsg_type = type; + return TRUE; +} + + + + +/* +++ encode_msg +++ + * + * Encode a msg() (recvmsg). In erlang its represented as + * a map, which has a specific set of attributes: + * + * addr (source address) - sockaddr() + * iov - [binary()] + * ctrl - [cmsg()] + * flags - msg_flags() + */ + +static +void encode_msg(ErlNifEnv* env, + ESockDescriptor* descP, + ssize_t read, + WSAMSG* msgP, + ErlNifBinary* dataBufP, + ErlNifBinary* ctrlBufP, + ERL_NIF_TERM* eMsg) +{ + ERL_NIF_TERM addr, iov, ctrl, flags; + + SSDBG( descP, + ("WIN-ESAIO", "encode_msg {%d} -> entry with" + "\r\n read: %ld" + "\r\n", descP->sock, (long) read) ); + + /* The address is not used if we are connected (unless, maybe, + * family is 'local'), so check (length = 0) before we try to encodel + */ + if (msgP->namelen != 0) { + esock_encode_sockaddr(env, + (ESockAddress*) msgP->name, + msgP->namelen, + &addr); + } else { + addr = esock_atom_undefined; + } + + SSDBG( descP, + ("WIN-ESAIO", "encode_msg {%d} -> encode iov" + "\r\n num vectors: %lu" + "\r\n", descP->sock, (unsigned long) msgP->dwBufferCount) ); + + esock_encode_iov(env, read, + (SysIOVec*) msgP->lpBuffers, msgP->dwBufferCount, dataBufP, + &iov); + + SSDBG( descP, + ("WIN-ESAIO", + "encode_msg {%d} -> try encode cmsgs\r\n", + descP->sock) ); + + encode_cmsgs(env, descP, ctrlBufP, msgP, &ctrl); + + SSDBG( descP, + ("WIN-ESAIO", + "encode_msg {%d} -> try encode flags\r\n", + descP->sock) ); + + esock_encode_msg_flags(env, descP, msgP->dwFlags, &flags); + + SSDBG( descP, + ("WIN-ESAIO", "encode_msg {%d} -> components encoded:" + "\r\n addr: %T" + "\r\n ctrl: %T" + "\r\n flags: %T" + "\r\n", descP->sock, addr, ctrl, flags) ); + + { + ERL_NIF_TERM keys[] = {esock_atom_iov, + esock_atom_ctrl, + esock_atom_flags, + esock_atom_addr}; + ERL_NIF_TERM vals[] = {iov, ctrl, flags, addr}; + size_t numKeys = NUM(keys); + + ESOCK_ASSERT( numKeys == NUM(vals) ); + + SSDBG( descP, + ("WIN-ESAIO", + "encode_msg {%d} -> create map\r\n", + descP->sock) ); + + if (msgP->namelen == 0) + numKeys--; // No addr + ESOCK_ASSERT( MKMA(env, keys, vals, numKeys, eMsg) ); + + SSDBG( descP, + ("WIN-ESAIO", + "encode_msg {%d}-> map encoded\r\n", + descP->sock) ); + } + + SSDBG( descP, + ("WIN-ESAIO", "encode_msg {%d} -> done\r\n", descP->sock) ); +} + + + +/* +++ encode_cmsgs +++ + * + * Encode a list of cmsg(). There can be 0 or more cmsghdr "blocks". + * + * Our "problem" is that we have no idea how many control messages + * we have. + * + * The cmsgHdrP arguments points to the start of the control data buffer, + * an actual binary. Its the only way to create sub-binaries. So, what we + * need to continue processing this is to turn that into an binary erlang + * term (which can then in turn be turned into sub-binaries). + * + * We need the cmsgBufP (even though cmsgHdrP points to it) to be able + * to create sub-binaries (one for each cmsg hdr). + * + * The TArray (term array) is created with the size of 128, which should + * be enough. But if its not, then it will be automatically realloc'ed during + * add. Once we are done adding hdr's to it, we convert the tarray to a list. + */ + +static +void encode_cmsgs(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifBinary* cmsgBinP, + WSAMSG* msgP, + ERL_NIF_TERM* eCMsg) +{ + ERL_NIF_TERM ctrlBuf = MKBIN(env, cmsgBinP); // The *entire* binary + SocketTArray cmsghdrs = TARRAY_CREATE(128); + WSACMSGHDR* firstP = ESOCK_CMSG_FIRSTHDR(msgP); + WSACMSGHDR* currentP; + + SSDBG( descP, ("WIN-ESAIO", "encode_cmsgs {%d} -> entry when" + "\r\n msg ctrl len: %d" + "\r\n (ctrl) firstP: 0x%lX" + "\r\n", descP->sock, msgP->Control.len, firstP) ); + + for (currentP = firstP; + (currentP != NULL); + /* nifs\win32\win_socket_asyncio.c(3167): + * warning C4116: unnamed type definition in parentheses + */ + currentP = ESOCK_CMSG_NXTHDR(msgP, currentP)) { + + SSDBG( descP, + ("WIN-ESAIO", "encode_cmsgs {%d} -> process cmsg header when" + "\r\n TArray Size: %d" + "\r\n", descP->sock, TARRAY_SZ(cmsghdrs)) ); + + /* MUST check this since on Linux the returned "cmsg" may actually + * go too far! + */ + if (((CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP)) > + msgP->Control.len) { + + /* Ouch, fatal error - give up + * We assume we cannot trust any data if this is wrong. + */ + + SSDBG( descP, + ("WIN-ESAIO", "encode_cmsgs {%d} -> check failed when: " + "\r\n currentP: 0x%lX" + "\r\n (current) cmsg_len: %d" + "\r\n firstP: 0x%lX" + "\r\n => %d" + "\r\n msg ctrl len: %d" + "\r\n", descP->sock, + CHARP(currentP), currentP->cmsg_len, CHARP(firstP), + (CHARP(currentP) + currentP->cmsg_len) - CHARP(firstP), + msgP->Control.len) ); + + TARRAY_ADD(cmsghdrs, esock_atom_bad_data); + break; + + } else { + unsigned char* dataP = UCHARP(ESOCK_CMSG_DATA(currentP)); + size_t dataPos = dataP - cmsgBinP->data; + size_t dataLen = + (UCHARP(currentP) + currentP->cmsg_len) - dataP; + ERL_NIF_TERM + cmsgHdr, + keys[] = + {esock_atom_level, + esock_atom_type, + esock_atom_data, + esock_atom_value}, + vals[NUM(keys)]; + size_t numKeys = NUM(keys); + BOOLEAN_T have_value; + + SSDBG( descP, + ("WIN-ESAIO", "encode_cmsgs {%d} -> cmsg header data: " + "\r\n dataPos: %d" + "\r\n dataLen: %d" + "\r\n", descP->sock, dataPos, dataLen) ); + + vals[0] = esock_encode_level(env, currentP->cmsg_level); + vals[2] = MKSBIN(env, ctrlBuf, dataPos, dataLen); + have_value = esock_encode_cmsg(env, + currentP->cmsg_level, + currentP->cmsg_type, + dataP, dataLen, &vals[1], &vals[3]); + + SSDBG( descP, + ("WIN-ESAIO", "encode_cmsgs {%d} -> " + "\r\n %T: %T" + "\r\n %T: %T" + "\r\n %T: %T" + "\r\n", descP->sock, + keys[0], vals[0], keys[1], vals[1], keys[2], vals[2]) ); + if (have_value) + SSDBG( descP, + ("WIN-ESAIO", "encode_cmsgs {%d} -> " + "\r\n %T: %T" + "\r\n", descP->sock, keys[3], vals[3]) ); + + /* Guard against cut-and-paste errors */ + ESOCK_ASSERT( numKeys == NUM(vals) ); + ESOCK_ASSERT( MKMA(env, keys, vals, + numKeys - (have_value ? 0 : 1), &cmsgHdr) ); + + /* And finally add it to the list... */ + TARRAY_ADD(cmsghdrs, cmsgHdr); + } + } + + SSDBG( descP, + ("WIN-ESAIO", "encode_cmsgs {%d} -> cmsg headers processed when" + "\r\n TArray Size: %d" + "\r\n", descP->sock, TARRAY_SZ(cmsghdrs)) ); + + /* The tarray is populated - convert it to a list */ + TARRAY_TOLIST(cmsghdrs, env, eCMsg); +} + + + +/* ====== Receive functions ====== */ + + +/* ======================================================================== + * The (read) buffer handling should be optimized! + * But for now we make it easy for ourselves by + * allocating a binary (of the specified or default + * size) and then throwing it away... + * + * The following flags are supported (with overlapped): + * MSG_OOB - Processes OOB data. + * MSG_PARTIAL - *message-oriented* sockets only. + * Both intput to *and* output from recv. + * MSG_PUSH_IMMEDIATE - *stream-oriented* sockets only. + * Hint rather than an actual guarantee. + * MSG_WAITALL - *stream-oriented* sockets only. + * The request will complete only when one of + * the following conditions apply: + * - buffer is completely full. + * - The connection has been closed. + * - The request has been canceled or an error occurred. + */ +extern +ERL_NIF_TERM esaio_recv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + ssize_t len, + int flags) +{ + ErlNifPid caller; + ESAIOOperation* opP; + int rres; + WSABUF wbuf; + DWORD f = flags; + size_t bufSz = (len != 0 ? len : descP->rBufSz); + + SSDBG( descP, ("WIN-ESAIO", "esaio_recv {%d} -> entry with" + "\r\n length: %ld" + "\r\n (buffer) size: %lu" + "\r\n", descP->sock, + (long) len, (unsigned long) bufSz) ); + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + if (! IS_OPEN(descP->readState)) + return esock_make_error_closed(env); + + /* Accept and Read can not be simultaneous? */ + if (descP->acceptorsQ.first != NULL) + return esock_make_error_invalid(env, esock_atom_state); + + /* Ensure that this caller does not *already* have a + * (recv) request waiting */ + if (esock_reader_search4pid(env, descP, &caller)) { + /* Reader already in queue */ + return esock_raise_invalid(env, esock_atom_state); + } + + /* Allocate the operation */ + opP = MALLOC( sizeof(ESAIOOperation) ); + ESOCK_ASSERT( opP != NULL); + sys_memzero((char*) opP, sizeof(ESAIOOperation)); + + opP->tag = ESAIO_OP_RECV; + /* Its a bit annoying that we have to alloc an env and then + * copy the ref *before* we know that we actually need it. + * How much does this cost? + */ + opP->env = esock_alloc_env("esaio-recv - operation"); + opP->data.recv.recvRef = CP_TERM(opP->env, recvRef); + opP->data.recv.sockRef = CP_TERM(opP->env, sockRef); + opP->caller = caller; + + /* Allocate a buffer: + * Either as much as we want to read or (if zero (0)) use the "default" + * size (what has been configured). + */ + ESOCK_ASSERT( ALLOC_BIN(bufSz, &opP->data.recv.buf) ); + + opP->data.recv.toRead = len; + wbuf.buf = opP->data.recv.buf.data; + wbuf.len = opP->data.recv.buf.size; + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_tries, &descP->readTries, 1); + + SSDBG( descP, ("WIN-ESAIO", "esaio_recv {%d} -> try read (%lu)\r\n", + descP->sock, (unsigned long) bufSz) ); + + rres = sock_recv_O(descP->sock, &wbuf, &f, (OVERLAPPED*) opP); + + return recv_check_result(env, descP, opP, caller, rres, + sockRef, recvRef); +} + + +/* *** recv_check_result *** + * + * Analyze the result of a receive attempt. + * The receive may have been completed directly or scheduled (overlapped). + */ +static +ERL_NIF_TERM recv_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + int recv_result, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM eres; + + if (recv_result == 0) { + + /* +++ Success +++ */ + + eres = recv_check_ok(env, descP, opP, caller, sockRef, recvRef); + + } else { + int err; + + /* +++ Failure or pending +++ */ + + err = sock_errno(); + + /* As pointed out above, there are basically two kinds of errors: + * 1) Pending: + * An overlapped operation was successfully initiated. + * Completion will be indicated at a later time. + * 2) An actual error + */ + + if (err == WSA_IO_PENDING) { + + if (! IS_ZERO(recvRef)) { + + eres = recv_check_pending(env, descP, opP, caller, + sockRef, recvRef); + + } else { + + /* We are not allowed to wait! => cancel */ + + SSDBG( descP, + ("WIN-ESAIO", + "recv_check_result(%T, %d) -> " + "pending - but we are not allowed to wait => cancel" + "\r\n", sockRef, descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) { + int save_errno = sock_errno(); + ERL_NIF_TERM tag = esock_atom_cancel; + ERL_NIF_TERM reason = ENO2T(env, save_errno); + + SSDBG( descP, + ("WIN-ESAIO", + "recv_check_result(%T, %d) -> " + "failed cancel pending operation" + "\r\n %T" + "\r\n", sockRef, descP->sock, reason) ); + + eres = esock_make_error(env, MKT2(env, tag, reason)); + + } else { + + eres = esock_atom_ok; + + } + + } + + } else { + + eres = recv_check_fail(env, descP, opP, err, sockRef); + + } + + } + + return eres; +} + + + +/* *** recv_check_ok *** + * + * A successful recv. We *know* that in this case the buffer is filled! + */ + +static +ERL_NIF_TERM recv_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM data, result; + DWORD read = 0, flags = 0; + + SSDBG( descP, + ("WIN-ESAIO", + "recv_check_ok -> try get overlapped result\r\n") ); + + if (get_recv_ovl_result(descP->sock, (OVERLAPPED*) opP, &read, &flags)) { + + SSDBG( descP, + ("WIN-ESAIO", + "recv_check_ok -> overlapped success result: " + "\r\n read: %d" + "\r\n flags: 0x%X" + "\r\n", read, flags) ); + + (void) flags; // We should really do something with this... + + /* <KOLLA> + * + * We need to handle read = 0 for other type(s) (DGRAM) when + * its actually valid to read 0 bytes. + * + * </KOLLA> + */ + + if ((read == 0) && (descP->type == SOCK_STREAM)) { + + /* + * When a stream socket peer has performed an orderly + * shutdown, the return value will be 0 (the traditional + * "end-of-file" return). + * + * *We* do never actually try to read 0 bytes! + */ + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_fails, &descP->readFails, 1); + + result = esock_make_error(env, esock_atom_closed); + + } else { + + if (read == opP->data.recv.buf.size) { + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(env, &opP->data.recv.buf); + + } else { + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(env, &opP->data.recv.buf); + data = MKSBIN(env, data, 0, read); + + } + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_byte, &descP->readByteCnt, read); + + /* (maybe) Update max */ + if (read > descP->readPkgMax) + descP->readPkgMax = read; + + + result = esock_make_ok2(env, data); + + } + + } else { + + int save_errno = sock_errno(); + + switch (save_errno) { + case WSA_IO_INCOMPLETE: + /* + * WSA_IO_INCOMPLETE + * + * Even though it (the I/O Completion Port framework) told + * us it was done, it was not. So we need to postpone and let + * the (worker) threads deal with it anyway...effing framework... + */ + + if (! IS_ZERO(recvRef)) { + + result = recv_check_pending(env, descP, opP, caller, + sockRef, recvRef); + } else { + + /* But we are not allowed to wait! => cancel */ + + SSDBG( descP, + ("WIN-ESAIO", + "recv_check_ok(%T, %d) -> " + "incomplete - but we are not allowed to wait => cancel" + "\r\n", sockRef, descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) { + int save_errno = sock_errno(); + ERL_NIF_TERM tag = esock_atom_cancel; + ERL_NIF_TERM reason = ENO2T(env, save_errno); + + SSDBG( descP, + ("WIN-ESAIO", + "recv_check_ok(%T, %d) -> " + "failed cancel incomplete operation" + "\r\n %T" + "\r\n", sockRef, descP->sock, reason) ); + + result = esock_make_error(env, MKT2(env, tag, reason)); + + } else { + + result = esock_atom_ok; // Will trigger {error, timeout} + + } + } + break; + + default: + { + ERL_NIF_TERM eerrno = ENO2T(env, save_errno); + ERL_NIF_TERM reason = MKT2(env, + esock_atom_get_overlapped_result, + eerrno); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_fails, &descP->readFails, 1); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.genErrs, 1); + + MUNLOCK(ctrl.cntMtx); + + result = esock_make_error(env, reason); + } + break; + } + } + + SSDBG( descP, + ("WIN-ESAIO", "recv_check_ok(%T) {%d} -> done with" + "\r\n result: %T" + "\r\n", + sockRef, descP->sock, result) ); + + return result; +} + + + +/* *** recv_check_pending *** + * + * The recv operation was scheduled, that is, its now in the hands + * of the I/O Completion Port framework. + */ +static +ERL_NIF_TERM recv_check_pending(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + + SSDBG( descP, + ("WIN-ESAIO", + "recv_check_pending(%T, %d) -> entry with" + "\r\n recvRef: %T" + "\r\n", sockRef, descP->sock, recvRef) ); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_waits, &descP->readWaits, 1); + + descP->readState |= ESOCK_STATE_SELECTED; + + esock_reader_push(env, descP, caller, recvRef, opP); + + return esock_atom_completion; + +} + + + +/* *** recv_check_fail *** + * + * Processing done upon failed 'recv'. + * An actual failure. + */ +static +ERL_NIF_TERM recv_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + ERL_NIF_TERM sockRef) +{ + SSDBG( descP, + ("WIN-ESAIO", "recv_check_fail(%T) {%d} -> entry with" + "\r\n errno: %d" + "\r\n", + sockRef, descP->sock, saveErrno) ); + + FREE_BIN( &opP->data.recv.buf ); + + return recv_check_failure(env, descP, opP, saveErrno, sockRef); +} + + + +/* *** recv_check_failure *** + * + * Processing done upon failed recv. + * An actual failure. + */ +static +ERL_NIF_TERM recv_check_failure(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM reason = MKA(env, erl_errno_id(saveErrno)); + + SSDBG( descP, + ("WIN-ESAIO", "recv_check_failure(%T) {%d} -> error: %d (%T)\r\n", + sockRef, descP->sock, saveErrno, reason) ); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_fails, &descP->readFails, 1); + + esock_clear_env("recv_check_failure", opP->env); + esock_free_env("recv_check_failure", opP->env); + FREE( opP ); + + return esock_make_error(env, reason); +} + + + +/* ======================================================================== + * esaio_recvfrom - Read a "packet" from a socket + * + * The (read) buffer handling *must* be optimized! + * But for now we make it easy for ourselves by + * allocating a binary (of the specified or default + * size) and then throw'ing it away... + */ +extern +ERL_NIF_TERM esaio_recvfrom(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + ssize_t len, + int flags) +{ + ErlNifPid caller; + ESAIOOperation* opP; + int rres; + WSABUF wbuf; + DWORD f = flags; + size_t bufSz = (len != 0 ? len : descP->rBufSz); + + SSDBG( descP, ("WIN-ESAIO", "essio_recvfrom {%d} -> entry with" + "\r\n bufSz: %d" + "\r\n", descP->sock, bufSz) ); + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + if (! IS_OPEN(descP->readState)) + return esock_make_error_closed(env); + + /* Accept and Read can not be simultaneous? */ + if (descP->acceptorsQ.first != NULL) + return esock_make_error_invalid(env, esock_atom_state); + + /* Ensure that this caller does not *already* have a + * (recv) request waiting */ + if (esock_reader_search4pid(env, descP, &caller)) { + /* Reader already in queue */ + return esock_raise_invalid(env, esock_atom_state); + } + + /* Allocate the operation */ + opP = MALLOC( sizeof(ESAIOOperation) ); + ESOCK_ASSERT( opP != NULL); + sys_memzero((char*) opP, sizeof(ESAIOOperation)); + + opP->tag = ESAIO_OP_RECVFROM; + /* Its a bit annoying that we have to alloc an env and then + * copy the ref *before* we know that we actually need it. + * How much does this cost? + */ + opP->env = esock_alloc_env("esaio-recvfrom - operation"); + opP->data.recvfrom.recvRef = CP_TERM(opP->env, recvRef); + opP->data.recvfrom.sockRef = CP_TERM(opP->env, sockRef); + opP->caller = caller; + + /* Allocate a buffer: + * Either as much as we want to read or (if zero (0)) use the "default" + * size (what has been configured). + */ + ESOCK_ASSERT( ALLOC_BIN(bufSz, &opP->data.recv.buf) ); + + opP->data.recvfrom.toRead = len; + wbuf.buf = opP->data.recvfrom.buf.data; + wbuf.len = opP->data.recvfrom.buf.size; + + opP->data.recvfrom.addrLen = sizeof(ESockAddress); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_tries, &descP->readTries, 1); + + SSDBG( descP, ("WIN-ESAIO", "esaio_recvfrom {%d} -> try read (%lu)\r\n", + descP->sock, (unsigned long) bufSz) ); + + rres = sock_recvfrom_O(descP->sock, &wbuf, &f, + (struct sockaddr*) &opP->data.recvfrom.fromAddr, + &opP->data.recvfrom.addrLen, (OVERLAPPED*) opP); + + return recvfrom_check_result(env, descP, opP, caller, rres, + sockRef, recvRef); +} + + + +/* *** recvfrom_check_result *** + * + * Analyze the result of a receive attempt. + * The receive may have been completed directly or scheduled (overlapped). + */ +static +ERL_NIF_TERM recvfrom_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + int recv_result, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM eres; + + if (recv_result == 0) { + + /* +++ Success +++ */ + + eres = recvfrom_check_ok(env, descP, opP, caller, sockRef, recvRef); + + } else { + int err; + + /* +++ Failure +++ */ + + err = sock_errno(); + + /* As pointed out above, there are basically two kinds of errors: + * 1) Pending: + * An overlapped operation was successfully initiated. + * Completion will be indicated at a later time. + * 2) An actual error + */ + + if (err == WSA_IO_PENDING) { + + if (! IS_ZERO(recvRef)) { + + eres = recv_check_pending(env, descP, opP, caller, + sockRef, recvRef); + } else { + + /* We are not allowed to wait! => cancel */ + + SSDBG( descP, + ("WIN-ESAIO", + "recvfrom_check_result(%T, %d) -> " + "pending - but we are not allowed to wait => cancel" + "\r\n", sockRef, descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) { + int save_errno = sock_errno(); + ERL_NIF_TERM tag = esock_atom_cancel; + ERL_NIF_TERM reason = ENO2T(env, save_errno); + + SSDBG( descP, + ("WIN-ESAIO", + "recvfrom_check_result(%T, %d) -> " + "failed cancel pending operation" + "\r\n %T" + "\r\n", sockRef, descP->sock, reason) ); + + eres = esock_make_error(env, MKT2(env, tag, reason)); + + } else { + + eres = esock_atom_ok; // Will trigger {error, timeout} + + } + + } + + } else { + + eres = recvfrom_check_fail(env, descP, opP, err, sockRef); + + } + + } + + return eres; +} + + + +/* *** recvfrom_check_ok *** + * + * A successful recvfrom. We *know* that in this case the buffer is filled! + */ + +static +ERL_NIF_TERM recvfrom_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM data, result; + DWORD read = 0, flags = 0; + + SSDBG( descP, + ("WIN-ESAIO", + "recvfrom_check_ok -> try get overlapped result\r\n") ); + + if (get_recv_ovl_result(descP->sock, (OVERLAPPED*) opP, &read, &flags)) { + + ERL_NIF_TERM eSockAddr; + + SSDBG( descP, + ("WIN-ESAIO", + "recvfrom_check_ok -> overlapped result: " + "\r\n read: %d" + "\r\n flags: 0x%X" + "\r\n", read, flags) ); + + (void) flags; // We should really do something with this... + + esock_encode_sockaddr(env, + &opP->data.recvfrom.fromAddr, + opP->data.recvfrom.addrLen, + &eSockAddr); + + if (read == opP->data.recvfrom.buf.size) { + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(env, &opP->data.recvfrom.buf); + } else { + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(env, &opP->data.recvfrom.buf); + data = MKSBIN(env, data, 0, read); + } + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_byte, &descP->readByteCnt, read); + + /* (maybe) Update max */ + if (read > descP->readPkgMax) + descP->readPkgMax = read; + + /* + * This is: {ok, {Source, Data}} + * But it should really be: {ok, {Source, Flags, Data}} + */ + result = esock_make_ok2(env, MKT2(env, eSockAddr, data)); + + } else { + + int save_errno = sock_errno(); + + switch (save_errno) { + case WSA_IO_INCOMPLETE: + /* + * WSA_IO_INCOMPLETE + * + * Even though it (the I/O Completion Port framework) told + * us it was done, it was not. So we need to postpone and let + * the (worker) threads deal with it anyway...effing framework... + */ + + if (! IS_ZERO(recvRef)) { + + result = recv_check_pending(env, descP, opP, caller, + sockRef, recvRef); + + } else { + + /* But we are not allowed to wait! => cancel */ + + SSDBG( descP, + ("WIN-ESAIO", + "recvfrom_check_ok(%T, %d) -> " + "incomplete - but we are not allowed to wait => cancel" + "\r\n", sockRef, descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) { + int save_errno = sock_errno(); + ERL_NIF_TERM tag = esock_atom_cancel; + ERL_NIF_TERM reason = ENO2T(env, save_errno); + + SSDBG( descP, + ("WIN-ESAIO", + "recvfrom_check_ok(%T, %d) -> " + "failed cancel incomplete operation" + "\r\n %T" + "\r\n", sockRef, descP->sock, reason) ); + + result = esock_make_error(env, MKT2(env, tag, reason)); + + } else { + + result = esock_atom_ok; // Will trigger {error, timeout} + + } + } + break; + + default: + { + ERL_NIF_TERM eerrno = ENO2T(env, save_errno); + ERL_NIF_TERM reason = MKT2(env, + esock_atom_get_overlapped_result, + eerrno); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_fails, &descP->readFails, 1); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.genErrs, 1); + + MUNLOCK(ctrl.cntMtx); + + result = esock_make_error(env, reason); + } + break; + } + } + + SSDBG( descP, + ("WIN-ESAIO", "recvfrom_check_ok(%T) {%d} -> done with" + "\r\n result: %T" + "\r\n", + sockRef, descP->sock, result) ); + + return result; +} + + + +/* *** recvfrom_check_fail *** + * + * Processing done upon failed 'recvfrom'. + * An actual failure. + */ +static +ERL_NIF_TERM recvfrom_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + ERL_NIF_TERM sockRef) +{ + SSDBG( descP, + ("WIN-ESAIO", "recfrom_check_fail(%T) {%d} -> entry with" + "\r\n errno: %d" + "\r\n", + sockRef, descP->sock, saveErrno) ); + + FREE_BIN( &opP->data.recvfrom.buf ); + + return recv_check_failure(env, descP, opP, saveErrno, sockRef); +} + + + + +/* ======================================================================== + * esaio_recvmsg - Read a "message" from a socket + * The (read) buffer handling *must* be optimized! + * But for now we make it easy for ourselves by + * allocating a binary (of the specified or default + * size) and then throwing it away... + * + * Note that this operation *only* works for socket + * of types SOCK_DGRAM and SOCK_RAW! Should we check + * and throw 'enotsup' otherwise? Would make testing + * easier... + */ +extern +ERL_NIF_TERM esaio_recvmsg(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef, + ssize_t bufLen, + ssize_t ctrlLen, + int flags) +{ + ErlNifPid caller; + ESAIOOperation* opP; + SOCKLEN_T addrLen; + size_t bufSz = (bufLen != 0 ? bufLen : descP->rBufSz); + size_t ctrlSz = (ctrlLen != 0 ? ctrlLen : descP->rCtrlSz); + int rres; + ERL_NIF_TERM eres; + + (void) flags; + + SSDBG( descP, ("WIN-ESAIO", "esaio_recvmsg(%T) {%d} -> entry with" + "\r\n bufSz: %lu (%ld)" + "\r\n ctrlSz: %ld (%ld)" + "\r\n", sockRef, descP->sock, + (unsigned long) bufSz, (long) bufLen, + (unsigned long) ctrlSz, (long) ctrlLen) ); + + /* This *only* works on socket type(s) DGRAM or RAW. + * Other socket types results in einval, which is not very + * helpful. So, in order to, atleast, help with testing, + * we do this... + */ + if (! ((descP->type == SOCK_DGRAM) || (descP->type == SOCK_RAW))) { + return enif_raise_exception(env, MKA(env, "notsup")); + } + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + if (! IS_OPEN(descP->readState)) + return esock_make_error_closed(env); + + /* Accept and Read can not be simultaneous? */ + if (descP->acceptorsQ.first != NULL) + return esock_make_error_invalid(env, esock_atom_state); + + /* Ensure that this caller does not *already* have a + * (recv) request waiting */ + if (esock_reader_search4pid(env, descP, &caller)) { + /* Reader already in queue */ + return esock_raise_invalid(env, esock_atom_state); + } + + /* Allocate the operation */ + opP = MALLOC( sizeof(ESAIOOperation) ); + ESOCK_ASSERT( opP != NULL); + sys_memzero((char*) opP, sizeof(ESAIOOperation)); + + opP->tag = ESAIO_OP_RECVMSG; + /* Its a bit annoying that we have to alloc an env and then + * copy the ref *before* we know that we actually need it. + * How much does this cost? + */ + opP->env = esock_alloc_env("esaio-recvmsg - operation"); + opP->data.recvmsg.recvRef = CP_TERM(opP->env, recvRef); + opP->data.recvmsg.sockRef = CP_TERM(opP->env, sockRef); + opP->caller = caller; + + /* Allocate the (msg) data buffer: + */ + ESOCK_ASSERT( ALLOC_BIN(bufSz, &opP->data.recvmsg.data[0]) ); + + /* Allocate the ctrl (buffer): + */ + ESOCK_ASSERT( ALLOC_BIN(ctrlSz, &opP->data.recvmsg.ctrl) ); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_tries, &descP->readTries, 1); + + addrLen = sizeof(opP->data.recvmsg.addr); + sys_memzero((char*) &opP->data.recvmsg.addr, addrLen); + sys_memzero((char*) &opP->data.recvmsg.msg, sizeof(opP->data.recvmsg.msg)); + + opP->data.recvmsg.wbufs[0].buf = opP->data.recvmsg.data[0].data; + opP->data.recvmsg.wbufs[0].len = opP->data.recvmsg.data[0].size; + + opP->data.recvmsg.msg.name = (SOCKADDR*) &opP->data.recvmsg.addr; + opP->data.recvmsg.msg.namelen = addrLen; + opP->data.recvmsg.msg.lpBuffers = opP->data.recvmsg.wbufs; + opP->data.recvmsg.msg.dwBufferCount = 1; // Should be calculated... + opP->data.recvmsg.msg.Control.buf = opP->data.recvmsg.ctrl.data; + opP->data.recvmsg.msg.Control.len = opP->data.recvmsg.ctrl.size; + opP->data.recvmsg.msg.dwFlags = 0; // TMP + + rres = sock_recvmsg_O(descP->sock, + &opP->data.recvmsg.msg, + (OVERLAPPED*) opP); + + eres = recvmsg_check_result(env, descP, opP, caller, rres, + sockRef, recvRef); + + SSDBG( descP, ("WIN-ESAIO", "esaio_recvmsg(%T) {%d} -> done\r\n", + sockRef, descP->sock) ); + + return eres; +} + + +static +BOOLEAN_T recv_check_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid* caller, + ERL_NIF_TERM ref, + ERL_NIF_TERM* checkResult) +{ + BOOLEAN_T result; + + /* Check if already reader */ + if (! esock_reader_search4pid(env, descP, caller)) { + /* No; check if we can wait for a result */ + if (COMPARE(ref, esock_atom_zero) == 0) + return FALSE; + } else { + *checkResult = esock_raise_invalid(env, esock_atom_state); + } + + return TRUE; +} + + +/* *** recvmsg_check_result *** + * + * The recvmsg function (maybe) delivers one (1) message. If our buffer + * is to small, the message will be truncated. So, regardless of + * if we filled the buffer or not, we have got what we are going + * to get regarding this message. + */ +static +ERL_NIF_TERM recvmsg_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + int recv_result, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM eres; + + SSDBG( descP, + ("WIN-ESAIO", "recvmsg_check_result(%T) {%d} -> entry with" + "\r\n recv_result: %d" + "\r\n recvRef: %T" + "\r\n", sockRef, descP->sock, recv_result, recvRef) ); + + if (recv_result == 0) { + + /* +++ Success +++ */ + + eres = recvmsg_check_ok(env, descP, opP, caller, sockRef, recvRef); + + } else { + int err; + + /* +++ Failure +++ */ + + err = sock_errno(); + + /* As pointed out above, there are basically two kinds of errors: + * 1) Pending: + * An overlapped operation was successfully initiated. + * Completion will be indicated at a later time. + * 2) An actual error + */ + + if (err == WSA_IO_PENDING) { + + if (! IS_ZERO(recvRef)) { + + eres = recv_check_pending(env, descP, opP, caller, + sockRef, recvRef); + + } else { + + /* We are not allowed to wait! => cancel */ + + SSDBG( descP, + ("WIN-ESAIO", + "recvmsg_check_result(%T, %d) -> " + "pending - but we are not allowed to wait => cancel" + "\r\n", sockRef, descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) { + int save_errno = sock_errno(); + ERL_NIF_TERM tag = esock_atom_cancel; + ERL_NIF_TERM reason = ENO2T(env, save_errno); + + SSDBG( descP, + ("WIN-ESAIO", + "recvmsg_check_result(%T, %d) -> " + "failed cancel pending operation" + "\r\n %T" + "\r\n", sockRef, descP->sock, reason) ); + + eres = esock_make_error(env, MKT2(env, tag, reason)); + + } else { + + eres = esock_atom_ok; // Will trigger {error, timeout} + + } + + } + + } else { + + eres = recvmsg_check_fail(env, descP, opP, err, sockRef); + + } + + } + + SSDBG( descP, + ("WIN-ESAIO", "recvmsg_check_result(%T) {%d} -> done\r\n", + sockRef, descP->sock) ); + + return eres; +} + + +/* *** recvmsg_check_ok *** + * + * A successful recvmsg. We *know* that in this case the buffer is filled! + */ + +static +ERL_NIF_TERM recvmsg_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM recvRef) +{ + ERL_NIF_TERM eMsg, result; + DWORD read = 0, flags = 0; + + SSDBG( descP, + ("WIN-ESAIO", + "recvmsg_check_ok(%T) {%d} -> try get overlapped result\r\n", + sockRef, descP->sock) ); + + if (get_recv_ovl_result(descP->sock, (OVERLAPPED*) opP, &read, &flags)) { + + ERL_NIF_TERM eSockAddr; + + SSDBG( descP, + ("WIN-ESAIO", + "recvmsg_check_ok(%T, %d) -> overlapped success result: " + "\r\n read: %d" + "\r\n flags: 0x%X" + "\r\n", sockRef, descP->sock, read, flags) ); + + (void) flags; // We should really do something with this... + + encode_msg(env, descP, read, + &opP->data.recvmsg.msg, + opP->data.recvmsg.data, + &opP->data.recvmsg.ctrl, + &eMsg); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_byte, &descP->readByteCnt, read); + + /* (maybe) Update max */ + if (read > descP->readPkgMax) + descP->readPkgMax = read; + + result = esock_make_ok2(env, eMsg); + + } else { + + int save_errno = sock_errno(); + + switch (save_errno) { + case WSA_IO_INCOMPLETE: + /* + * WSA_IO_INCOMPLETE + * + * Even though it (the I/O Completion Port framework) told + * us it was done, it was not. So we need to postpone and let + * the (worker) threads deal with it anyway...effing framework... + */ + + if (! IS_ZERO(recvRef)) { + + result = recv_check_pending(env, descP, opP, caller, + sockRef, recvRef); + + } else { + + /* But we are not allowed to wait! => cancel */ + + SSDBG( descP, + ("WIN-ESAIO", + "recvmsg_check_ok(%T, %d) -> " + "incomplete - but we are not allowed to wait => cancel" + "\r\n", sockRef, descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) opP)) { + int save_errno = sock_errno(); + ERL_NIF_TERM tag = esock_atom_cancel; + ERL_NIF_TERM reason = ENO2T(env, save_errno); + + SSDBG( descP, + ("WIN-ESAIO", + "recvmsg_check_ok(%T, %d) -> " + "failed cancel incomplete operation" + "\r\n %T" + "\r\n", sockRef, descP->sock, reason) ); + + result = esock_make_error(env, MKT2(env, tag, reason)); + + } else { + + result = esock_atom_ok; // Will trigger {error, timeout} + + } + } + break; + + default: + { + ERL_NIF_TERM eerrno = ENO2T(env, save_errno); + ERL_NIF_TERM reason = MKT2(env, + esock_atom_get_overlapped_result, + eerrno); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_fails, &descP->readFails, 1); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.genErrs, 1); + + MUNLOCK(ctrl.cntMtx); + + result = esock_make_error(env, reason); + } + break; + } + } + + SSDBG( descP, + ("WIN-ESAIO", "recvmsg_check_ok(%T) {%d} -> done with" + "\r\n result: %T" + "\r\n", + sockRef, descP->sock, result) ); + + return result; +} + + + +/* *** recvmsg_check_fail *** + * + * Processing done upon failed 'recvmsg'. + * An actual failure. + */ +static +ERL_NIF_TERM recvmsg_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + int saveErrno, + ERL_NIF_TERM sockRef) +{ + SSDBG( descP, + ("WIN-ESAIO", "recvmsg_check_fail(%T) {%d} -> entry with" + "\r\n errno: %d" + "\r\n", + sockRef, descP->sock, saveErrno) ); + + FREE_BIN( &opP->data.recvmsg.data[0] ); + FREE_BIN( &opP->data.recvmsg.ctrl ); + + return recv_check_failure(env, descP, opP, saveErrno, sockRef); +} + + + + + +/* ******************************************************************* + * esaio_close - Close a socket + * + * Stage 1 of the socket close + */ + +extern +ERL_NIF_TERM esaio_close(ErlNifEnv* env, + ESockDescriptor* descP) +{ + if (! IS_OPEN(descP->readState)) { + /* A bit of cheeting; maybe not closed yet - do we need a queue? */ + return esock_make_error_closed(env); + } + + /* Store the PID of the caller, + * since we need to inform it when we + * (that is, the stop callback function) + * completes. + */ + ESOCK_ASSERT( enif_self(env, &descP->closerPid) != NULL ); + + /* If the caller is not the owner; monitor the caller, + * since we should complete this operation even if the caller dies + * (for whatever reason). + */ + if (COMPARE_PIDS(&descP->closerPid, &descP->ctrlPid) != 0) { + + ESOCK_ASSERT( MONP("esaio_close-check -> closer", + env, descP, + &descP->closerPid, + &descP->closerMon) == 0 ); + } + + /* Prepare for closing the socket */ + descP->readState |= ESOCK_STATE_CLOSING; + descP->writeState |= ESOCK_STATE_CLOSING; + if (do_stop(env, descP)) { + + // stop() has been scheduled - wait for it + SSDBG( descP, + ("WIN-ESAIO", "esaio_close {%d} -> stop was scheduled\r\n", + descP->sock) ); + + // Create closeRef for the close msg that esock_stop() will send + descP->closeEnv = esock_alloc_env("esock_close_do - close-env"); + descP->closeRef = MKREF(descP->closeEnv); + + return esock_make_ok2(env, CP_TERM(env, descP->closeRef)); + + } else { + // The socket may be closed - tell caller to finalize + SSDBG( descP, + ("WIN-ESAIO", + "esaio_close {%d} -> stop was called\r\n", + descP->sock) ); + + return esock_atom_ok; + } +} + + + +static +BOOLEAN_T do_stop(ErlNifEnv* env, + ESockDescriptor* descP) +{ + BOOLEAN_T ret; + ERL_NIF_TERM sockRef; + + sockRef = enif_make_resource(env, descP); + + if (IS_SELECTED(descP)) { + + SSDBG( descP, + ("WIN-ESAIO", + "do_stop {%d} -> cancel outstanding I/O operations\r\n", + descP->sock) ); + + /* Cancel *all* outstanding I/O operations on the socket. + * We have to wait for the worker threads to process these ops! + * (will result in OPERATION_ABORTED for the threads). + */ + if (! CancelIoEx((HANDLE) descP->sock, NULL) ) { + int save_errno = sock_errno(); + + SSDBG( descP, + ("WIN-ESAIO", + "do_stop {%d} -> cancel I/O failed: %s (%d)\r\n", + descP->sock, erl_errno_id(save_errno), save_errno) ); + + /* Only issue an error message for errors *other* than + * 'not found' (since 'not found' means there is no active + * requests = already completed => race). + */ + + if (save_errno != ERROR_NOT_FOUND) + esock_error_msg("Failed cancel outstanding I/O operations:" + "\r\n Socket: " SOCKET_FORMAT_STR + "\r\n Reason: %s (%d)" + "\r\n", + descP->sock, + erl_errno_id(save_errno), save_errno); + + ret = FALSE; + + } else { + + /* Cancel of all active requests (to the I/O completion port + * machinery) has been successfully requested. + * The requests will be aborted and handled by the worker threads. + */ + + SSDBG( descP, + ("WIN-ESAIO", + "do_stop {%d} -> successfully canceled\r\n", descP->sock) ); + + ret = TRUE; + } + + } else { + + /* No active requests in the I/O completion port machinery */ + + SSDBG( descP, + ("WIN-ESAIO", + "do_stop {%d} -> no active I/O requests\r\n", descP->sock) ); + + ret = FALSE; + } + + /* We do nothing here with the requests in the various queues + * They are handled by the working threads, when the abort is triggered + * (one for each request)! + */ + + /* +++++++ Connector +++++++ + * Note that there should not be Writers and a Connector + * at the same time so the check for if the + * current Writer/Connecter was deselected is only correct + * under that assumption + */ + + if (descP->connectorP != NULL) { + + /* We have a Connector; + * + * The Connector will not get a select message + * - send it an abort message + */ + + esock_stop_handle_current(env, + "connector", + descP, sockRef, &descP->connector); + + descP->connectorP = NULL; + } + + return ret; +} + + + +/* ======================================================================== + * Perform the final step in the socket close. + */ +extern +ERL_NIF_TERM esaio_fin_close(ErlNifEnv* env, + ESockDescriptor* descP) +{ + int err; + ErlNifPid self; + + ESOCK_ASSERT( enif_self(env, &self) != NULL ); + + if (IS_CLOSED(descP->readState)) + return esock_make_error_closed(env); + + if (! IS_CLOSING(descP->readState)) { + // esock_close() has not been called + return esock_raise_invalid(env, esock_atom_state); + } + + if (IS_SELECTED(descP) && (descP->closeEnv != NULL)) { + // esock_stop() is scheduled but has not been called + return esock_raise_invalid(env, esock_atom_state); + } + + if (COMPARE_PIDS(&descP->closerPid, &self) != 0) { + // This process is not the closer + return esock_raise_invalid(env, esock_atom_state); + } + + // Close the socket + + /* Stop monitoring the closer. + * Demonitoring may fail since this is a dirty NIF + * - the caller may have died already. + */ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_fin_close {%d} -> demonitor closer process %T\r\n", + descP->sock, descP->closerPid) ); + enif_set_pid_undefined(&descP->closerPid); + if (descP->closerMon.isActive) { + (void) DEMONP("esaio_fin_close -> closer", + env, descP, &descP->closerMon); + } + + /* Stop monitoring the owner */ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_fin_close {%d} -> demonitor owner process %T\r\n", + descP->sock, descP->ctrlPid) ); + enif_set_pid_undefined(&descP->ctrlPid); + (void) DEMONP("esaio_fin_close -> ctrl", + env, descP, &descP->ctrlMon); + /* Not impossible to still get a esock_down() call from a + * just triggered owner monitor down + */ + + /* This nif-function is executed in a dirty scheduler just so + * that it can "hang" (with minimum effect on the VM) while the + * kernel writes our buffers. IF we have set the linger option + * for this ({true, integer() > 0}). + */ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_fin_close {%d} -> (try) close the socket\r\n", + descP->sock, descP->ctrlPid) ); + err = esock_close_socket(env, descP, TRUE); + + if (err != 0) { + if (err == ERRNO_BLOCK) { + /* Not all data in the buffers where sent, + * make sure the caller gets this. + */ + return esock_make_error(env, esock_atom_timeout); + } else { + return esock_make_error_errno(env, err); + } + } + + SSDBG( descP, ("WIN-ESAIO", "esaio_fin_close -> done\r\n") ); + + return esock_atom_ok; +} + + + +/* ======================================================================== + * Cancel a connect request. + */ +extern +ERL_NIF_TERM esaio_cancel_connect(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM opRef) +{ + ERL_NIF_TERM res; + ErlNifPid self; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_cancel_connect {%d} -> entry with" + "\r\n writeState: 0x%X" + "\r\n opRef: %T" + "\r\n", + descP->sock, descP->writeState, opRef) ); + + ESOCK_ASSERT( enif_self(env, &self) != NULL ); + + if (! IS_OPEN(descP->writeState)) { + + res = esock_make_error_closed(env); + + } else if ((descP->connectorP == NULL) || + (COMPARE_PIDS(&self, &descP->connector.pid) != 0) || + (COMPARE(opRef, descP->connector.ref) != 0)) { + + res = esock_make_error(env, esock_atom_not_found); + + } else { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_cancel_connect {%d} -> " + "try cancel connect I/O request\r\n", + descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, + (OVERLAPPED*) descP->connector.dataP)) { + /* What does this mean? + * One of the possible reasons is that the connect succeeded. + * In which case, one of the threads in the thread-pool will + * be triggered (eventually). + * Then we have to deal with a connected socket that no one wants... + */ + int save_errno = sock_errno(); + res = esock_make_error_errno(env, save_errno); + } else { + res = esock_atom_ok; + } + + esock_requestor_release("esock_cancel_connect", + env, descP, &descP->connector); + descP->connectorP = NULL; + descP->writeState &= ~ESOCK_STATE_CONNECTING; + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_cancel_connect {%d} -> done when" + "\r\n res: %T" + "\r\n", + descP->sock, descP->writeState, + opRef, res) ); + + return res; +} + + + +/* *** esock_cancel_accept *** + * + * We have three different cases: + * *) Socket is closed: + * return error: closed + * *) Active accept (found in the request store): + * Cancel the completion request! + * Success will trigger an event (delivered) to the + * (completion) worker threads. + * *) Not found (in the request store): + * This request has already completed (race): + * return: not_found + * + */ +extern +ERL_NIF_TERM esaio_cancel_accept(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) +{ + ERL_NIF_TERM res; + ESockRequestor req; + ErlNifPid caller; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_cancel_accept(%T), {%d,0x%X} ->" + "\r\n opRef: %T" + "\r\n", sockRef, descP->sock, descP->readState, opRef) ); + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + if (! IS_OPEN(descP->readState)) { + + res = esock_make_error_closed(env); + + } else if (esock_acceptor_get(env, descP, &opRef, &caller, &req)) { + + ESOCK_ASSERT( DEMONP("esaio_cancel_accept -> acceptor", + env, descP, &req.mon) == 0); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_cancel_accept {%d} -> try cancel accept I/O request\r\n", + descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) req.dataP)) { + + /* What does this mean? + * One of the possible reasons is that the accept succeeded. + * In which case, one of the threads in the thread-pool will + * be triggered (eventually). + * Then we have to deal with a connected socket that no one wants... + */ + + int save_errno = sock_errno(); + res = esock_make_error_errno(env, save_errno); + + } else { + + res = esock_atom_ok; + + } + + /* Request cleanup (demonitor already done above) */ + esock_clear_env("esaio_cancel_accept -> req cleanup", req.env); + esock_free_env("esaio_cancel_accept -> req cleanup", req.env); + + /* *Maybe* update listen socket (read) state + * (depends on if the queue is now empty) + */ + if (descP->acceptorsQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_ACCEPTING; + } + + } else { + + res = esock_make_error(env, esock_atom_not_found); + + } + + SSDBG( descP, + ("WIN-ESAIO", "esaio_cancel_accept(%T) -> done with result:" + "\r\n %T" + "\r\n", sockRef, res) ); + + return res; +} + + + + +/* *** esock_cancel_send *** + * + * We have three different cases: + * *) Socket is closed: + * return error: closed + * *) Active send (found in the request store): + * Cancel the completion request! + * Success will trigger an event (delivered) to the + * (completion) worker threads. + * *) Not found (in the request store): + * This request has already completed (race): + * return: not_found + * + */ +extern +ERL_NIF_TERM esaio_cancel_send(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) +{ + ERL_NIF_TERM res; + ESockRequestor req; + ErlNifPid caller; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_cancel_send(%T), {%d,0x%X} ->" + "\r\n opRef: %T" + "\r\n", sockRef, descP->sock, descP->readState, opRef) ); + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + if (! IS_OPEN(descP->writeState)) { + + res = esock_make_error_closed(env); + + } else if (esock_writer_get(env, descP, &opRef, &caller, &req)) { + + ESOCK_ASSERT( DEMONP("esaio_cancel_send -> sender", + env, descP, &req.mon) == 0); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_cancel_send {%d} -> try cancel send I/O request\r\n", + descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) req.dataP)) { + + /* What does this mean? + * One of the possible reasons is that the send succeeded. + * In which case, one of the threads in the thread-pool will + * be triggered (eventually). + */ + + int save_errno = sock_errno(); + res = esock_make_error_errno(env, save_errno); + + } else { + + res = esock_atom_ok; + + } + + /* Request cleanup (demonitor already done above) */ + esock_clear_env("esaio_cancel_send -> req cleanup", req.env); + esock_free_env("esaio_cancel_send -> req cleanup", req.env); + + } else { + + res = esock_make_error(env, esock_atom_not_found); + + } + + SSDBG( descP, + ("WIN-ESAIO", "esaio_cancel_send(%T) -> done with result:" + "\r\n %T" + "\r\n", sockRef, res) ); + + return res; +} + + + + +/* *** esock_cancel_recv *** + * + * We have three different cases: + * *) Socket is closed: + * return error: closed + * *) Active receive (found in the request store): + * Cancel the completion request! + * Success will trigger an event (delivered) to the + * (completion) worker threads. + * *) Not found (in the request store): + * This request has already completed (race): + * return: not_found + * + */ +extern +ERL_NIF_TERM esaio_cancel_recv(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM opRef) +{ + ERL_NIF_TERM res; + ESockRequestor req; + ErlNifPid caller; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_cancel_recv(%T), {%d,0x%X} ->" + "\r\n opRef: %T" + "\r\n", sockRef, descP->sock, descP->readState, opRef) ); + + ESOCK_ASSERT( enif_self(env, &caller) != NULL ); + + if (! IS_OPEN(descP->readState)) { + + res = esock_make_error_closed(env); + + } else if (esock_reader_get(env, descP, &opRef, &caller, &req)) { + + ESOCK_ASSERT( DEMONP("esaio_cancel_recv -> reader", + env, descP, &req.mon) == 0); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_cancel_recv {%d} -> try cancel send I/O request\r\n", + descP->sock) ); + + if (! CancelIoEx((HANDLE) descP->sock, (OVERLAPPED*) req.dataP)) { + + /* What does this mean? + * One of the possible reasons is that the recv succeeded. + * In which case, one of the threads in the thread-pool will + * be triggered (eventually). + */ + + int save_errno = sock_errno(); + res = esock_make_error_errno(env, save_errno); + + } else { + + res = esock_atom_ok; + + } + + /* Request cleanup (demonitor already done above) */ + esock_clear_env("esaio_cancel_recv -> req cleanup", req.env); + esock_free_env("esaio_cancel_recv -> req cleanup", req.env); + + } else { + + res = esock_make_error(env, esock_atom_not_found); + + } + + SSDBG( descP, + ("WIN-ESAIO", "esaio_cancel_recv(%T) -> done with result:" + "\r\n %T" + "\r\n", sockRef, res) ); + + return res; +} + + + + +/* ==================================================================== + * + * The "worker" thread of the I/O Completion Port thread pool. + * Shall each thread have its own environment? + * + * ==================================================================== + */ + +static +void* esaio_completion_main(void* threadDataP) +{ + char envName[64]; /* Used for building the (env-) name */ + BOOLEAN_T done = FALSE; + ESAIOThreadData* dataP = (ESAIOThreadData*) threadDataP; + ESockDescriptor* descP = NULL; + ESAIOOperation* opP; + OVERLAPPED* olP; + BOOL res; + DWORD numBytes, flags = 0; + int save_errno; + + SGDBG( ("WIN-ESAIO", "esaio_completion_main -> entry\r\n") ); + + dataP->state = ESAIO_THREAD_STATE_INITIATING; + + sprintf(envName, "esaio-completion-main[%d]", dataP->id); + dataP->env = esock_alloc_env(envName); + + dataP->state = ESAIO_THREAD_STATE_OPERATIONAL; + + SGDBG( ("WIN-ESAIO", "esaio_completion_main -> initiated\r\n") ); + + while (!done) { + /* + * If this function *fails*, return value FALSE, the (out-) arguments: + * - lpNumberOfBytes (numBytes) + * - lpCompletionKey (descP) + * - lpOverlapped (olP) + * *can* contain particular value combinations as follows: + * + * * If *lpOverlapped is NULL, the function did not dequeue a + * completion packet from the completion port. + * In this case, the function does not store information in the + * variables pointed to by the lpNumberOfBytes and lpCompletionKey + * parameters, and their values are indeterminate. + * + * * If *lpOverlapped is not NULL and the function dequeues a + * completion packet for a failed I/O operation from the + * completion port, the function stores information about the + * failed operation in the variables pointed to by lpNumberOfBytes, + * lpCompletionKey, and lpOverlapped. + * To get extended error information, call GetLastError. + * + */ + + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> [%d] try dequeue packet\r\n", + dataP->cnt) ); + + res = GetQueuedCompletionStatus(ctrl.cport, + &numBytes, + (PULONG_PTR) &descP, + &olP, + INFINITE); + save_errno = NO_ERROR; + + if (!res) { + + save_errno = sock_errno(); // Details + + if (olP == NULL) { + + /* First alt. + * What shall we do here? Quit? Try again? + */ + + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> [failure 1]" + "\r\n %s (%d)" + "\r\n", erl_errno_id(save_errno), save_errno) ); + + dataP->state = ESAIO_THREAD_STATE_TERMINATING; + dataP->error = ESAIO_THREAD_ERROR_GET; + opP = NULL; + done = TRUE; + break; + + } else { + + /* Second alt. + * Dequeued a complete packet for a *failed* I/O operation. + */ + + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> [failure 2] " + "\r\n %s (%d)" + "\r\n", erl_errno_id(save_errno), save_errno) ); + + opP = CONTAINING_RECORD(olP, ESAIOOperation, ol); + esaio_completion_inc(dataP); + + } + } else { + opP = CONTAINING_RECORD(olP, ESAIOOperation, ol); + esaio_completion_inc(dataP); + + SGDBG( ("WIN-ESAIO", "esaio_completion_main -> success\r\n") ); + + } /* if (!res) */ + + dataP->latest = opP->tag; + + switch (opP->tag) { + case ESAIO_OP_TERMINATE: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received terminate cmd\r\n") ); + done = esaio_completion_terminate(dataP, (OVERLAPPED*) opP); + break; + + case ESAIO_OP_CONNECT: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received connect cmd\r\n") ); + done = esaio_completion_connect(dataP, descP, (OVERLAPPED*) opP, + opP->env, &opP->caller, + &opP->data.connect, + save_errno); + break; + + case ESAIO_OP_ACCEPT: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received accept cmd\r\n") ); + done = esaio_completion_accept(dataP, descP, (OVERLAPPED*) opP, + opP->env, &opP->caller, + &opP->data.accept, + save_errno); + break; + + case ESAIO_OP_SEND: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received send cmd\r\n") ); + done = esaio_completion_send(dataP, descP, (OVERLAPPED*) opP, + opP->env, &opP->caller, + &opP->data.send, + save_errno); + break; + + case ESAIO_OP_SENDTO: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received sendto cmd\r\n") ); + done = esaio_completion_sendto(dataP, descP, (OVERLAPPED*) opP, + opP->env, &opP->caller, + &opP->data.sendto, + save_errno); + break; + + case ESAIO_OP_SENDMSG: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received sendmsg cmd\r\n") ); + done = esaio_completion_sendmsg(dataP, descP, (OVERLAPPED*) opP, + opP->env, &opP->caller, + &opP->data.sendmsg, + save_errno); + break; + + case ESAIO_OP_RECV: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received recv cmd\r\n") ); + done = esaio_completion_recv(dataP, descP, (OVERLAPPED*) opP, + opP->env, &opP->caller, + &opP->data.recv, + save_errno); + break; + + case ESAIO_OP_RECVFROM: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received recvfrom cmd\r\n") ); + done = esaio_completion_recvfrom(dataP, descP, (OVERLAPPED*) opP, + opP->env, &opP->caller, + &opP->data.recvfrom, + save_errno); + break; + + case ESAIO_OP_RECVMSG: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received recvmsg cmd\r\n") ); + done = esaio_completion_recvmsg(dataP, descP, (OVERLAPPED*) opP, + opP->env, &opP->caller, + &opP->data.recvmsg, + save_errno); + break; + + default: + SGDBG( ("WIN-ESAIO", + "esaio_completion_main -> received unknown cmd: " + "\r\n %d" + "\r\n", + opP->tag) ); + done = esaio_completion_unknown(dataP, descP, (OVERLAPPED*) opP, + numBytes, save_errno); + break; + + } + + FREE(opP); + + } /* while (!done) */ + + SGDBG( ("WIN-ESAIO", "esaio_completion_main -> terminating\r\n") ); + + TEXIT(threadDataP); + + SGDBG( ("WIN-ESAIO", "esaio_completion_main -> terminated\r\n") ); + + dataP->state = ESAIO_THREAD_STATE_TERMINATED; + + SGDBG( ("WIN-ESAIO", "esaio_completion_main -> done\r\n") ); + + return threadDataP; +} + + +/* *** esaio_completion_terminate *** + * + * We are done + * + */ +static +BOOLEAN_T esaio_completion_terminate(ESAIOThreadData* dataP, + OVERLAPPED* ovl) +{ + (void) ovl; + + dataP->state = ESAIO_THREAD_STATE_TERMINATING; + dataP->error = ESAIO_THREAD_ERROR_CMD; + + return TRUE; +} + + +/* *** esaio_completion_connect *** + * + * Handle a completed 'connect' (completion) request. + * Send a 'completion' message (to requestor) with the request status. + * + * Completion message: + * {'socket tag', socket(), completion, CompletionInfo} + * + * CompletionInfo: {CompletionHandle, CompletionStatus} + * CompletionHandle: reference() + * Result: ok | {error, Reason} + * + * + * There is a possibillity of a race here. That is, if the user + * calls socket:connect(Socket, ..., nowait), the connect is + * scheduled, and then just as it has completed, but before this + * thread has been activated to handle the 'connect completed' + * the user calls socket:close(Socket) (or exits). + * Then when this function is called, the socket is closed. + * What to do? + */ +static +BOOLEAN_T esaio_completion_connect(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataConnect* opDataP, + int error) +{ + ErlNifEnv* env = dataP->env; + ERL_NIF_TERM reason; + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_connect(%d) -> entry\r\n", + descP->sock, error) ); + + (void) opCaller; + + switch (error) { + case NO_ERROR: + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_connect(%d) -> success" + "\r\n", descP->sock) ); + MLOCK(descP->writeMtx); + + esaio_completion_connect_success(env, descP, opDataP); + + MUNLOCK(descP->writeMtx); + break; + + case WSA_OPERATION_ABORTED: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_connect(%d) -> operation aborted" + "\r\n", descP->sock) ); + /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */ + MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); + + esaio_completion_connect_aborted(env, descP, opDataP); + + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); + break; + + default: + /* We do not know what this is + * but we can "assume" that the request failed so we need to + * remove it from the "queue" if its still there... + * And cleanup... + */ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_connect(%d) -> unknown failure:" + "\r\n %T" + "\r\n", descP->sock, ENO2T(env, error)) ); + MLOCK(descP->writeMtx); + + esaio_completion_connect_failure(env, descP, opDataP, error); + + MUNLOCK(descP->writeMtx); + break; + } + + SGDBG( ("WIN-ESAIO", + "esaio_completion_connect -> clear and delete op env\r\n") ); + + /* No need for this "stuff" anymore */ + esock_clear_env("esaio_completion_connect", opEnv); + esock_free_env("esaio_completion_connect", opEnv); + + SGDBG( ("WIN-ESAIO", "esaio_completion_connect -> done\r\n") ); + + return FALSE; +} + + + +/* *** esaio_completion_connect_success *** + * The 'connect' operation was successful. + */ +static +void esaio_completion_connect_success(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOpDataConnect* opDataP) +{ + if (descP->connectorP != NULL) { + if (IS_OPEN(descP->writeState)) { + esaio_completion_connect_completed(env, descP, opDataP); + } else { + /* A completed (active) request for a socket that is not open. + * Is this even possible? + * A race (completed just as the socket was closed). + */ + + /* Clean up the connector stuff, no need for that anymore */ + esock_requestor_release("esaio_completion_connect_success -> " + "not active", + env, descP, &descP->connector); + descP->connectorP = NULL; + + descP->writeState &= + ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED); + + esaio_completion_connect_not_active(descP); + } + } else { + /* Connect was actually completed directly + * (and 'connector' was therefor not initiated) + * => Nothing to do here, other than cleanup. + */ + descP->writeState &= ~ESOCK_STATE_SELECTED; + } +} + + + +/* *** esaio_completion_connect_aborted *** + * The 'connect' operation was aborted. + * The only thing *we* do that could cause an abort is the + * 'CancelIoEx' call, which we do when closing the socket + * (or cancel a request). + * But if we have done that; + * - Socket state will not be 'open' and + * - we have also set closer (pid and ref). + */ + +static +void esaio_completion_connect_aborted(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOpDataConnect* opDataP) +{ + if (descP->connectorP != NULL) { + + ERL_NIF_TERM reason = esock_atom_closed; + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + descP->connectorP, reason); + + /* Clean up the connector stuff, no need for that anymore */ + esock_requestor_release("connect_stream_check_result -> abort", + env, descP, &descP->connector); + descP->connectorP = NULL; + descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED); + + /* The socket not being open (assumed closing), + * means we are in the closing phase... + */ + if (! IS_OPEN(descP->writeState)) { + + esaio_stop(env, descP); + + } + } else { + descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED); + } +} + + +/* *** esaio_completion_connect_failure * + * A "general" failure happened while performing the 'connect' operation. + */ +static +void esaio_completion_connect_failure(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOpDataConnect* opDataP, + int error) +{ + if (descP->connectorP != NULL) { + /* Figure out the reason */ + ERL_NIF_TERM reason = MKT2(env, + esock_atom_get_overlapped_result, + ENO2T(env, error)); + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + descP->connectorP, reason); + esaio_completion_connect_fail(env, descP, error, FALSE); + + /* Clean up the connector stuff, no need for that anymore */ + esock_requestor_release("connect_stream_check_result -> failure", + env, descP, &descP->connector); + descP->connectorP = NULL; + descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED); + + } else { + esaio_completion_connect_fail(env, descP, error, TRUE); + } +} + + +/* *** esaio_completion_connect_completed *** + * The connect request has completed. + */ +static +void esaio_completion_connect_completed(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOpDataConnect* opDataP) +{ + ERL_NIF_TERM completionStatus, completionInfo; + int ucres; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_connect_completed(%d) -> " + "success - try update context\r\n", descP->sock) ); + + ucres = ESAIO_UPDATE_CONNECT_CONTEXT( descP->sock ); + + if (ucres == 0) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_connect_completed({%d) -> success\r\n", + descP->sock) ); + + descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED); + descP->writeState |= ESOCK_STATE_CONNECTED; + + completionStatus = esock_atom_ok; + + } else { + + /* It is actually possible that this is an error "we do not know" + * which will result in the atom 'unknown', which is not very useful... + * So, we should really test if is 'unknown' and if so use the actual + * value (the integer) instead. + */ + int save_errno = sock_errno(); + ERL_NIF_TERM tag = esock_atom_update_connect_context; + ERL_NIF_TERM reason = ENO2T(env, save_errno); + + SSDBG( descP, ("WIN-ESAIO", + "esaio_completion_connect_completed(%d) -> " + "failed update connect context: %T\r\n", + descP->sock, reason) ); + + descP->writeState = ESOCK_STATE_CLOSED; + + sock_close(descP->sock); + + WSACleanup(); + + completionStatus = esock_make_error_t2r(descP->connector.env, + tag, reason); + + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_connect_completed {%d} -> " + "completion status: %T\r\n", + descP->sock, completionStatus) ); + + completionInfo = MKT2(descP->connector.env, + descP->connector.ref, + completionStatus); + + /* Send a 'connect' completion message */ + esaio_send_completion_msg(env, + descP, + &descP->connector.pid, + descP->connector.env, + CP_TERM(descP->connector.env, opDataP->sockRef), + completionInfo); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_connect_completed {%d} -> cleanup\r\n", + descP->sock) ); + + /* Clean up the connector stuff, no need for that anymore */ + esock_requestor_release("esaio_completion_connect_completed", + env, descP, &descP->connector); + descP->connectorP = NULL; + +} + + + +/* *** esaio_completion_connect_not_active *** + * A connect has completed but the operation is no longer valid. + */ +static +void esaio_completion_connect_not_active(ESockDescriptor* descP) +{ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_connect_not_active -> " + "success for cancelled connect\r\n") ); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.unexpectedConnects, 1); + + MUNLOCK(ctrl.cntMtx); + +} + + + +/* *** esaio_completion_connect_fail *** + * Unknown operation failure. + */ +static +void esaio_completion_connect_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform) +{ + descP->writeState &= ~(ESOCK_STATE_CONNECTING | ESOCK_STATE_SELECTED); + esaio_completion_fail(env, descP, "connect", error, inform); +} + + + + +/* === accept 'stuff' === */ + +/* *** esaio_completion_accept *** + * + * Handle a completed 'accept' (completion) request. + * Send a 'completion' message (to requestor) with the request status. + * + * Completion message: + * {'socket tag', socket(), completion, CompletionInfo} + * + * CompletionInfo: {CompletionHandle, CompletionStatus} + * CompletionHandle: reference() + * Result: ok | {error, Reason} + * + * + * There is a possibillity of a race here. That is, if the user + * calls socket:accept(Socket, ..., nowait), the accept is + * scheduled, and then just as it has completed, but before this + * thread has been activated to handle the 'accept completed' + * the user calls socket:close(Socket) (or exits). + * Then when this function is called, the socket is closed. + * What to do? + */ +static +BOOLEAN_T esaio_completion_accept(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP, + int error) +{ + ErlNifEnv* env = dataP->env; + ESockRequestor req; + ERL_NIF_TERM reason; + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_accept(%d) -> entry with" + "\r\n error: %s (%d)" + "\r\n", descP->sock, erl_errno_id(error), error) ); + + switch (error) { + case NO_ERROR: + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_accept(%d) -> success" + "\r\n", descP->sock) ); + MLOCK(descP->readMtx); + + esaio_completion_accept_success(env, descP, opEnv, opCaller, opDataP); + + MUNLOCK(descP->readMtx); + break; + + case WSA_OPERATION_ABORTED: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_accept(%d) -> operation aborted" + "\r\n", descP->sock) ); + /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */ + MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); + + esaio_completion_accept_aborted(env, descP, opCaller, opDataP); + + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); + break; + + default: + /* We do not know what this is + * but we can "assume" that the request failed so we need to + * remove it from the "queue" if its still there... + * And cleanup... + */ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_accept(%d) -> unknown failure" + "\r\n", descP->sock) ); + MLOCK(descP->readMtx); + + esaio_completion_accept_failure(env, descP, opCaller, opDataP, error); + + MUNLOCK(descP->readMtx); + break; + } + + SGDBG( ("WIN-ESAIO", + "esaio_completion_accept -> clear and delete op env\r\n") ); + + /* "Manually" allocated buffer */ + FREE( opDataP->buf ); + + /* No need for this "stuff" anymore */ + esock_clear_env("esaio_completion_accept - op cleanup", opEnv); + esock_free_env("esaio_completion_accept - op cleanup", opEnv); + + SGDBG( ("WIN-ESAIO", "esaio_completion_accept -> done\r\n") ); + + return FALSE; + +} + + +/* *** esaio_completion_accept_success *** + * The 'accept' operation was successful. + */ +static +void esaio_completion_accept_success(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP) +{ + ESockRequestor req; + + if (esock_acceptor_get(env, descP, + &opDataP->accRef, + opCaller, + &req)) { + if (IS_OPEN(descP->readState)) { + esaio_completion_accept_completed(env, descP, + opEnv, opCaller, opDataP, + &req); + } else { + /* A completed (active) request for a socket that is not open. + * Is this even possible? + * A race (completed just as the socket was closed). + */ + esaio_completion_accept_not_active(descP); + } + } else { + /* Request was actually completed directly + * (and was therefor not put into the "queue") + * => Nothing to do here, other than cleanup (see below). + * => But we do not free the "buffer" since it was "used up" + * when we (as assumed) got the result (directly)... + */ + } + + /* *Maybe* update socket (read) state + * (depends on if the queue is now empty) + */ + if (descP->acceptorsQ.first == NULL) + descP->readState &= ~ESOCK_STATE_SELECTED; +} + + +/* *** esaio_completion_accept_aborted *** + * The 'accept' operation was aborted. + * The only thing *we* do that could cause an abort is the + * 'CancelIoEx' call, which we do when closing the socket + * (or cancel a request). + * But if we have done that; + * - Socket state will not be 'open' and + * - we have also set closer (pid and ref). + */ + +static +void esaio_completion_accept_aborted(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP) +{ + ESockRequestor req; + ERL_NIF_TERM reason; + + if (esock_acceptor_get(env, descP, + &opDataP->accRef, + opCaller, + &req)) { + + ERL_NIF_TERM reason = esock_atom_closed; + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->lSockRef, + &req, reason); + + /* The socket not being open (assumed closing), + * means we are in the closing phase... + */ + if (! IS_OPEN(descP->readState)) { + + /* We can only send the 'close' message to the closer + * when all requests has been processed! + */ + + /* Check "our" queue */ + if (descP->acceptorsQ.first == NULL) { + + /* Check "other" queue(s) and if there is a closer pid */ + if ((descP->readersQ.first == NULL) && + (descP->writersQ.first == NULL)) { + + esaio_stop(env, descP); + + } + } + } + + } + + /* *Maybe* update socket (read) state + * (depends on if the queue is now empty) + */ + if (descP->acceptorsQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + +} + + +/* *** esaio_completion_accept_failure * + * A "general" failure happened while performing the 'accept' operation. + */ +static +void esaio_completion_accept_failure(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP, + int error) +{ + ESockRequestor req; + ERL_NIF_TERM reason; + + if (esock_acceptor_get(env, descP, + &opDataP->accRef, + opCaller, + &req)) { + + reason = MKT2(env, + esock_atom_get_overlapped_result, + ENO2T(env, error)); + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->lSockRef, + &req, reason); + esaio_completion_accept_fail(env, descP, error, FALSE); + } else { + esaio_completion_accept_fail(env, descP, error, TRUE); + } + + /* *Maybe* update socket (read) state + * (depends on if the queue is now empty) + */ + if (descP->acceptorsQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + +} + + +/* *** esaio_completion_accept_completed *** + * The accept request has completed. + */ +static +void esaio_completion_accept_completed(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataAccept* opDataP, + ESockRequestor* reqP) +{ + ERL_NIF_TERM completionStatus, completionInfo; + int ucres; + ESockDescriptor* accDescP; + ERL_NIF_TERM accRef, accSocket; + + ESOCK_ASSERT( DEMONP("esaio_completion_accept_completed - acceptor", + env, descP, &reqP->mon) == 0); + + /* We need to make sure peername and sockname works! */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_accept_completed -> " + "success - try update context\r\n") ); + + ucres = ESAIO_UPDATE_ACCEPT_CONTEXT( opDataP->asock, opDataP->lsock ); + + if (ucres == 0) { + + int save_errno; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_accept_completed -> " + "create (accepted) descriptor\r\n") ); + + accDescP = esock_alloc_descriptor(opDataP->asock); + + if (ESAIO_OK != (save_errno = esaio_add_socket(accDescP))) { + // See esock_dtor for what needs done! + ERL_NIF_TERM tag = esock_atom_add_socket; + ERL_NIF_TERM reason = ENO2T(opEnv, save_errno); + + ESOCK_CNT_INC(env, descP, CP_TERM(env, opDataP->lSockRef), + esock_atom_acc_fails, &descP->accFails, 1); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_accept_completed -> " + "failed adding (accepted) socket to completion port: " + "%T\r\n", reason) ); + + esock_dealloc_descriptor(env, accDescP); + sock_close(opDataP->asock); + + /* This should really be: + * {error, {invalid, {add_to_completion_port, Reason}}} + */ + + completionStatus = esock_make_error_t2r(opEnv, tag, reason); + + } else { + + ESOCK_CNT_INC(env, descP, CP_TERM(env, opDataP->lSockRef), + esock_atom_acc_success, &descP->accSuccess, 1); + + accDescP->domain = descP->domain; + accDescP->type = descP->type; + accDescP->protocol = descP->protocol; + + MLOCK(descP->writeMtx); + + accDescP->rBufSz = descP->rBufSz; // Inherit buffer size + accDescP->rCtrlSz = descP->rCtrlSz; // Inherit buffer size + accDescP->wCtrlSz = descP->wCtrlSz; // Inherit buffer size + accDescP->iow = descP->iow; // Inherit iow + accDescP->dbg = descP->dbg; // Inherit debug flag + accDescP->useReg = descP->useReg; // Inherit useReg flag + esock_inc_socket(accDescP->domain, accDescP->type, + accDescP->protocol); + + accRef = enif_make_resource(env, accDescP); + enif_release_resource(accDescP); + accSocket = esock_mk_socket(opEnv, CP_TERM(opEnv, accRef)); + + accDescP->ctrlPid = *opCaller; + + ESOCK_ASSERT( MONP("esaio_completion_accept_completed -> ctrl", + env, accDescP, + &accDescP->ctrlPid, + &accDescP->ctrlMon) == 0 ); + + accDescP->writeState |= ESOCK_STATE_CONNECTED; + + MUNLOCK(descP->writeMtx); + + /* And finally (maybe) update the registry */ + if (descP->useReg) + esock_send_reg_add_msg(env, descP, accRef); + + completionStatus = esock_make_ok2(opEnv, accSocket); + + } + + } else { + + /* It is actually possible that this is an error "we do not know" + * which will result in the atom 'unknown', which is not very useful... + * So, we should really test if is 'unknown' and if so use the actual + * value (the integer) instead. + */ + int save_errno = sock_errno(); + ERL_NIF_TERM tag = esock_atom_update_accept_context; + ERL_NIF_TERM reason = ENO2T(env, save_errno); + + SSDBG( descP, ("WIN-ESAIO", + "esaio_completion_accept_completed(%d) -> " + "accept context update failed: %T (%d)\r\n", + descP->sock, reason, save_errno) ); + + sock_close(descP->sock); + descP->writeState = ESOCK_STATE_CLOSED; + + WSACleanup(); + + completionStatus = esock_make_error_t2r(opEnv, tag, reason); + + } + + completionInfo = MKT2(opEnv, opDataP->accRef, completionStatus); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_accept_completed -> " + "send completion message to %T with" + "\r\n CompletionInfo: %T" + "\r\n", MKPID(env, opCaller), completionInfo) ); + + /* Send a 'accept' completion message */ + esaio_send_completion_msg(env, // Send env + descP, // Descriptor + opCaller, // Msg destination + opEnv, // Msg env + opDataP->lSockRef, // Dest socket + completionInfo); // Info + + /* *** Finalize *** */ + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_accept_completed -> finalize\r\n") ); + + /* Request cleanup (demonitor already done above) */ + esock_clear_env("esaio_completion_accept_completed -> req cleanup", + reqP->env); + esock_free_env("esaio_completion_accept_completed -> req cleanup", + reqP->env); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_accept_completed -> done\r\n") ); +} + + + +/* *** esaio_completion_accept_not_active *** + * A accept request has completed but the request is no longer valid. + */ +static +void esaio_completion_accept_not_active(ESockDescriptor* descP) +{ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_accept_not_active(%d) -> " + "success for not active accept request\r\n", descP->sock) ); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.unexpectedAccepts, 1); + + MUNLOCK(ctrl.cntMtx); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_accept_not_active(%d) -> done\r\n", + descP->sock) ); + +} + + +/* *** esaio_completion_accept_fail *** + * Unknown operation failure. + */ +static +void esaio_completion_accept_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform) +{ + esaio_completion_fail(env, descP, "accept", error, inform); +} + + + + +/* === send 'stuff' === */ + +/* *** esaio_completion_send *** + * + * Handle a completed 'send' (completion) request. + * Send a 'completion' message (to requestor) with the request status. + * + * Completion message: + * {'socket tag', socket(), completion, CompletionInfo} + * + * CompletionInfo: {CompletionHandle, CompletionStatus} + * CompletionHandle: reference() + * Result: ok | {error, Reason} + * + * + * There is a possibillity of a race here. That is, if the user + * calls socket:send(Socket, ..., nowait), the send is scheduled, + * and then just as it has completed, but before this + * thread has been activated to handle the 'send completed' + * the user calls socket:close(Socket) (or exits). + * Then when this function is called, the socket is closed. + * What to do? + * + * We need to use 'WSAGetOverlappedResult' to actually figure out the + * "transfer result" (how much was sent). + */ +static +BOOLEAN_T esaio_completion_send(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataSend* opDataP, + int error) +{ + ErlNifEnv* env = dataP->env; + ESockRequestor req; + ERL_NIF_TERM reason; + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_send(%d) -> entry with" + "\r\n error: %T" + "\r\n", descP->sock, ENO2T(env, error)) ); + + switch (error) { + case NO_ERROR: + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_send(%d) -> no error" + "\r\n", descP->sock) ); + MLOCK(descP->writeMtx); + if (esock_writer_get(env, descP, + &opDataP->sendRef, + opCaller, + &req)) { + if (IS_OPEN(descP->writeState)) { + esaio_completion_send_completed(env, descP, + ovl, + opEnv, + opCaller, + opDataP->sockRef, + opDataP->sendRef, + opDataP->wbuf.len, + &req); + } else { + /* A completed (active) request for a socket that is not open. + * Is this even possible? + * A race (completed just as the socket was closed). + */ + esaio_completion_send_not_active(descP); + } + + } else { + /* Request was actually completed directly + * (and was therefor not put into the "queue") + * => Nothing to do here, other than cleanup (see below). + */ + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->writersQ.first == NULL) { + descP->writeState &= ~ESOCK_STATE_SELECTED; + } + + MUNLOCK(descP->writeMtx); + break; + + case WSA_OPERATION_ABORTED: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_send(%d) -> operation aborted" + "\r\n", descP->sock) ); + /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */ + MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); + /* The only thing *we* do that could cause an abort is the + * 'CancelIoEx' call, which we do when closing the socket + * (or cancel a request). + * But if we have done that; + * - Socket state will not be 'open' and + * - we have also set closer (pid and ref). + */ + + if (esock_writer_get(env, descP, + &opDataP->sendRef, + opCaller, + &req)) { + + reason = esock_atom_closed, + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + + /* The socket not being open (assumed closing), + * means we are in the closing phase... + */ + if (! IS_OPEN(descP->writeState)) { + + /* We can only send the 'close' message to the closer + * when all requests has been processed! + */ + + /* Check "our" queue */ + if (descP->writersQ.first == NULL) { + + /* Check "other" queue(s) and if there is a closer pid */ + if ((descP->readersQ.first == NULL) && + (descP->acceptorsQ.first == NULL)) { + + esaio_stop(env, descP); + + } + } + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->writersQ.first == NULL) { + descP->writeState &= ~ESOCK_STATE_SELECTED; + } + + } + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); + break; + + default: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_send(%d) -> operation unknown failure" + "\r\n", descP->sock) ); + MLOCK(descP->writeMtx); + /* We do not know what this is + * but we can "assume" that the request failed so we need to + * remove it from the "queue" if its still there... + * And cleanup... + */ + if (esock_writer_get(env, descP, + &opDataP->sendRef, + opCaller, + &req)) { + + reason = MKT2(env, + esock_atom_get_overlapped_result, + ENO2T(env, error)); + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + esaio_completion_send_fail(env, descP, error, FALSE); + + } else { + esaio_completion_send_fail(env, descP, error, TRUE); + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->writersQ.first == NULL) { + descP->writeState &= ~ESOCK_STATE_SELECTED; + } + + MUNLOCK(descP->writeMtx); + break; + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_send(%d) -> cleanup\r\n", descP->sock) ); + + FREE( opDataP->wbuf.buf ); + + /* No need for this "stuff" anymore */ + esock_clear_env("esaio_completion_send - op cleanup", opEnv); + esock_free_env("esaio_completion_send - op cleanup", opEnv); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_send(%d) -> done\r\n", + descP->sock) ); + + return FALSE; + +} + + +/* *** esaio_completion_send_completed *** + * The send request has completed. + */ +static +void esaio_completion_send_completed(ErlNifEnv* env, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* sender, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + DWORD toWrite, + ESockRequestor* reqP) +{ + ERL_NIF_TERM completionStatus, completionInfo; + DWORD written; + + ESOCK_ASSERT( DEMONP("esaio_completion_send_completed - sender", + env, descP, &reqP->mon) == 0); + + /* Success, but we need to check how much we actually got. + * Also the 'flags' (which we currentöy ignore) + * + * CompletionStatus = ok | {ok, RestData} + * CompletionInfo = {ConnRef, CompletionStatus} + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_send_completed ->" + "success - try get overlapped result\r\n") ); + + if (get_send_ovl_result(descP->sock, ovl, &written)) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_send_completed -> overlapped result: " + "\r\n written: %d" + "\r\n buffer size: %d" + "\r\n", written, toWrite) ); + + if (written == toWrite) { + + /* Sent it all => done */ + + completionStatus = esaio_completion_send_done(env, + descP, sockRef, + written); + + } else { + + /* Only send part of the data => + * needs splitting and (maybe) retry (its up to the caller)! + */ + + completionStatus = esaio_completion_send_partial(env, + descP, + sockRef, + written); + } + + } else { + + int save_errno = sock_errno(); + + /* Now what? + * We know we wrote "something" but we cannot figure out + * how much... + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_send_completed -> " + "overlapped result failure: %d\r\n", save_errno) ); + + completionStatus = + esaio_completion_get_ovl_result_fail(env, descP, save_errno); + } + + completionInfo = MKT2(env, CP_TERM(env, sendRef), completionStatus); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_send_completed -> " + "send completion message to %T with" + "\r\n CompletionInfo: %T" + "\r\n", MKPID(env, sender), completionInfo) ); + + /* Send a 'send' completion message */ + esaio_send_completion_msg(env, // Send env + descP, // Descriptor + sender, // Msg destination + opEnv, // Msg env + sockRef, // Dest socket + completionInfo); // Info + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_send_completed -> done\r\n") ); +} + + + +/* *** esaio_completion_send_done *** + * + * A complete write (the entire buffer was sent). + * + */ +static +ERL_NIF_TERM esaio_completion_send_done(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + DWORD written) +{ + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_pkg, &descP->writePkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_byte, &descP->writeByteCnt, written); + + if (written > descP->writePkgMax) + descP->writePkgMax = written; + + return esock_atom_ok; +} + + + +/* *** esaio_completion_send_partial *** + * + * A partial send, that is only part of the buffer was used. + * + */ +static +ERL_NIF_TERM esaio_completion_send_partial(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + DWORD written) +{ + if (written > 0) { + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_pkg, &descP->writePkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_byte, &descP->writeByteCnt, written); + + if (written > descP->writePkgMax) + descP->writePkgMax = written; + + } + + return esock_make_ok2(env, MKI64(env, written)); + +} + + + +/* *** esaio_completion_send_not_active *** + * A send request has completed but the request is no longer valid. + */ +static +void esaio_completion_send_not_active(ESockDescriptor* descP) +{ + /* This send request is *not* "active"! + * The send (send, sendto, sendmsg) operation + * has been (most likely) cancelled => cleanup. + * If the op failed, its safe to assume that the error is + * the result of the cancellation, and we do not actually + * need to do anything here. + * If however, the send succeeded, we need to do "some + * cleanup". + * But what can we do here? + * Send an abort message to the sender or/and owner? + * Increment a counter (unexpected acceptssends)? + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_send_not_active(%d) -> " + "success for not active send request\r\n", + descP->sock) ); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.unexpectedWrites, 1); + + MUNLOCK(ctrl.cntMtx); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_send_not_active(%d) -> done\r\n", + descP->sock) ); + +} + + + +/* *** esaio_completion_send_fail *** + * Unknown operation failure. + */ +static +void esaio_completion_send_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform) +{ + esaio_completion_fail(env, descP, "send", error, inform); +} + + + +/* *** esaio_completion_sendto *** + * + * Handle a completed 'sendto' (completion) request. + * Send a 'completion' message (to requestor) with the request status. + * + * Completion message: + * {'socket tag', socket(), completion, CompletionInfo} + * + * CompletionInfo: {CompletionHandle, CompletionStatus} + * CompletionHandle: reference() + * Result: ok | {error, Reason} + * + * + * There is a possibillity of a race here. That is, if the user + * calls socket:sendto(Socket, ..., nowait), the send is scheduled, + * and then just as it has completed, but before this + * thread has been activated to handle the 'send completed' + * the user calls socket:close(Socket) (or exits). + * Then when this function is called, the socket is closed. + * What to do? + * + * We need to use 'WSAGetOverlappedResult' to actually figure out the + * "transfer result" (how much was sent). + */ +static +BOOLEAN_T esaio_completion_sendto(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataSendTo* opDataP, + int error) +{ + ErlNifEnv* env = dataP->env; + ESockRequestor req; + ERL_NIF_TERM reason; + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_sendto(%d) -> entry" + "\r\n error: %T" + "\r\n", descP->sock, ENO2T(env, error)) ); + + switch (error) { + case NO_ERROR: + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_sendto(%d) -> no error" + "\r\n", descP->sock) ); + MLOCK(descP->writeMtx); + if (esock_writer_get(env, descP, + &opDataP->sendRef, + opCaller, + &req)) { + if (IS_OPEN(descP->writeState)) { + esaio_completion_send_completed(env, descP, + ovl, + opEnv, + opCaller, + opDataP->sockRef, + opDataP->sendRef, + opDataP->wbuf.len, + &req); + } else { + /* A completed (active) request for a socket that is not open. + * Is this even possible? + * A race (completed just as the socket was closed). + */ + esaio_completion_send_not_active(descP); + } + + } else { + /* Request was actually completed directly + * (and was therefor not put into the "queue") + * => Nothing to do here, other than cleanup (see below). + */ + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->writersQ.first == NULL) { + descP->writeState &= ~ESOCK_STATE_SELECTED; + } + + MUNLOCK(descP->writeMtx); + break; + + case WSA_OPERATION_ABORTED: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_sendto(%d) -> operation aborted" + "\r\n", descP->sock) ); + /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */ + MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); + /* The only thing *we* do that could cause an abort is the + * 'CancelIoEx' call, which we do when closing the socket + * (or cancel a request). + * But if we have done that; + * - Socket state will not be 'open' and + * - we have also set closer (pid and ref). + */ + + if (esock_writer_get(env, descP, + &opDataP->sendRef, + opCaller, + &req)) { + + reason = esock_atom_closed, + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + + /* The socket not being open (assumed closing), + * means we are in the closing phase... + */ + if (! IS_OPEN(descP->writeState)) { + + /* We can only send the 'close' message to the closer + * when all requests has been processed! + */ + + /* Check "our" queue */ + if (descP->writersQ.first == NULL) { + + /* Check "other" queue(s) and if there is a closer pid */ + if ((descP->readersQ.first == NULL) && + (descP->acceptorsQ.first == NULL)) { + + esaio_stop(env, descP); + + } + } + } + + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->writersQ.first == NULL) { + descP->writeState &= ~ESOCK_STATE_SELECTED; + } + + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); + break; + + default: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_sendto(%d) -> operation unknown failure" + "\r\n", descP->sock) ); + MLOCK(descP->writeMtx); + /* We do not know what this is + * but we can "assume" that the request failed so we need to + * remove it from the "queue" if its still there... + * And cleanup... + */ + if (esock_writer_get(env, descP, + &opDataP->sendRef, + opCaller, + &req)) { + + reason = MKT2(env, + esock_atom_get_overlapped_result, + ENO2T(env, error)); + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + esaio_completion_sendto_fail(env, descP, error, FALSE); + + } else { + esaio_completion_sendto_fail(env, descP, error, TRUE); + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->writersQ.first == NULL) { + descP->writeState &= ~ESOCK_STATE_SELECTED; + } + + MUNLOCK(descP->writeMtx); + break; + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_sendto(%d) -> cleanup\r\n", + descP->sock) ); + + FREE( opDataP->wbuf.buf ); + + /* No need for this "stuff" anymore */ + esock_clear_env("esaio_completion_sendto - op cleanup", opEnv); + esock_free_env("esaio_completion_sendto - op cleanup", opEnv); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_sendto(%d) -> done\r\n", + descP->sock) ); + + return FALSE; +} + + + +/* *** esaio_completion_sendto_fail *** + * Unknown operation failure. + */ +static +void esaio_completion_sendto_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform) +{ + esaio_completion_fail(env, descP, "sendto", error, inform); +} + + + +/* *** esaio_completion_sendmsg *** + * + * Handle a completed 'sendmsg' (completion) request. + * Send a 'completion' message (to requestor) with the request status. + * + * Completion message: + * {'socket tag', socket(), completion, CompletionInfo} + * + * CompletionInfo: {CompletionHandle, CompletionStatus} + * CompletionHandle: reference() + * Result: ok | {error, Reason} + * + * + * There is a possibillity of a race here. That is, if the user + * calls socket:sendto(Socket, ..., nowait), the send is scheduled, + * and then just as it has completed, but before this + * thread has been activated to handle the 'send completed' + * the user calls socket:close(Socket) (or exits). + * Then when this function is called, the socket is closed. + * What to do? + * + * We need to use 'WSAGetOverlappedResult' to actually figure out the + * "transfer result" (how much was sent). + */ +static +BOOLEAN_T esaio_completion_sendmsg(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataSendMsg* opDataP, + int error) +{ + ErlNifEnv* env = dataP->env; + ESockRequestor req; + ERL_NIF_TERM reason; + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_sendmsg(%d) -> entry with" + "\r\n error: %T" + "\r\n", descP->sock, ENO2T(env, error)) ); + + switch (error) { + case NO_ERROR: + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_sendmsg(%d) -> no error" + "\r\n", descP->sock) ); + MLOCK(descP->writeMtx); + if (esock_writer_get(env, descP, + &opDataP->sendRef, + opCaller, + &req)) { + if (IS_OPEN(descP->writeState)) { + + DWORD toWrite = 0; + + /* Calculate how much data *in total* + * we was supposed to write */ + for (int i = 0; i < opDataP->iovec->iovcnt; i++) { + toWrite += opDataP->iovec->iov[i].iov_len; + } + + esaio_completion_send_completed(env, descP, + ovl, + opEnv, + opCaller, + opDataP->sockRef, + opDataP->sendRef, + toWrite, + &req); + + } else { + /* A completed (active) request for a socket that is not open. + * Is this even possible? + * A race (completed just as the socket was closed). + */ + esaio_completion_send_not_active(descP); + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->writersQ.first == NULL) { + descP->writeState &= ~ESOCK_STATE_SELECTED; + } + + } else { + /* Request was actually completed directly + * (and was therefor not put into the "queue") + * => Nothing to do here, other than cleanup (see below). + */ + } + MUNLOCK(descP->writeMtx); + break; + + case WSA_OPERATION_ABORTED: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_sendmsg(%d) -> operation aborted" + "\r\n", descP->sock) ); + /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */ + MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); + /* The only thing *we* do that could cause an abort is the + * 'CancelIoEx' call, which we do when closing the socket + * (or cancel a request). + * But if we have done that; + * - Socket state will not be 'open' and + * - we have also set closer (pid and ref). + */ + + if (esock_writer_get(env, descP, + &opDataP->sendRef, + opCaller, + &req)) { + + reason = esock_atom_closed, + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + + /* The socket not being open (assumed closing), + * means we are in the closing phase... + */ + if (! IS_OPEN(descP->writeState)) { + + /* We can only send the 'close' message to the closer + * when all requests has been processed! + */ + + /* Check "our" queue */ + if (descP->writersQ.first == NULL) { + + /* Check "other" queue(s) and if there is a closer pid */ + if ((descP->readersQ.first == NULL) && + (descP->acceptorsQ.first == NULL)) { + + esaio_stop(env, descP); + + } + } + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->writersQ.first == NULL) { + descP->writeState &= ~ESOCK_STATE_SELECTED; + } + + } + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); + break; + + default: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_sendmsg(%d) -> operation unknown failure" + "\r\n", descP->sock) ); + MLOCK(descP->writeMtx); + /* We do not know what this is + * but we can "assume" that the request failed so we need to + * remove it from the "queue" if its still there... + * And cleanup... + */ + if (esock_writer_get(env, descP, + &opDataP->sendRef, + opCaller, + &req)) { + + reason = MKT2(env, + esock_atom_get_overlapped_result, + ENO2T(env, error)); + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + esaio_completion_sendmsg_fail(env, descP, error, FALSE); + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->writersQ.first == NULL) { + descP->writeState &= ~ESOCK_STATE_SELECTED; + } + + } else { + esaio_completion_sendmsg_fail(env, descP, error, TRUE); + } + MUNLOCK(descP->writeMtx); + break; + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_sendmsg(%d) -> cleanup\r\n", descP->sock) ); + + /* "Manually" allocated buffers */ + FREE( opDataP->msg.lpBuffers ); + if (opDataP->ctrlBuf != NULL) + FREE( opDataP->ctrlBuf ); + + /* No need for this "stuff" anymore */ + esock_clear_env("esaio_completion_sendmsg - op cleanup", opEnv); + esock_free_env("esaio_completion_sendmsg - op cleanup", opEnv); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_sendmsg(%d) -> done\r\n", + descP->sock) ); + + return FALSE; + +} + + + +/* *** esaio_completion_sendmsg_fail *** + * Unknown operation failure. + */ +static +void esaio_completion_sendmsg_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform) +{ + esaio_completion_fail(env, descP, "sendmsg", error, inform); +} + + + +/* === receive 'stuff' === */ + +/* *** esaio_completion_recv *** + * + * Handle a completed 'recv' (completion) request. + * Send a 'completion' message (to requestor) with the request status. + * + * Completion message: + * {'socket tag', socket(), completion, CompletionInfo} + * + * CompletionInfo: {CompletionHandle, CompletionStatus} + * CompletionHandle: reference() + * Result: ok | {error, Reason} + * + * + * There is a possibillity of a race here. That is, if the user + * calls socket:recv(Socket, ..., nowait), the recv is scheduled, + * and then just as it has completed, but before this + * thread has been activated to handle the 'recv completed' + * the user calls socket:close(Socket) (or exits). + * Then when this function is called, the socket is closed. + * What to do? + */ +static +BOOLEAN_T esaio_completion_recv(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecv* opDataP, + int error) +{ + ErlNifEnv* env = dataP->env; + ESockRequestor req; + ERL_NIF_TERM reason; + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recv(%d) -> entry with" + "\r\n error: %s (%d)" + "\r\n", descP->sock, erl_errno_id(error), error) ); + + switch (error) { + case NO_ERROR: + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recv(%d) -> no error" + "\r\n", descP->sock) ); + MLOCK(descP->readMtx); + if (esock_reader_get(env, descP, + &opDataP->recvRef, + opCaller, + &req)) { + if (IS_OPEN(descP->readState)) { + esaio_completion_recv_completed(env, descP, + ovl, + opEnv, opCaller, opDataP, + &req); + } else { + /* A completed (active) request for a socket that is not open. + * Is this even possible? + * A race (completed just as the socket was closed). + */ + esaio_completion_recv_not_active(descP); + FREE_BIN( &opDataP->buf ); + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->readersQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + + } else { + /* Request was actually completed directly + * (and was therefor not put into the "queue") + * => Nothing to do here, other than cleanup (see below). + * => But we do not free the "buffer" since it was "used up" + * when we (as assumed) got the result (directly)... + */ + } + MUNLOCK(descP->readMtx); + break; + + case WSA_OPERATION_ABORTED: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv(%d) -> operation aborted" + "\r\n", descP->sock) ); + /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */ + MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); + /* The only thing *we* do that could cause an abort is the + * 'CancelIoEx' call, which we do when closing the socket + * (or cancel a request). + * But if we have done that; + * - Socket state will not be 'open' and + * - we have also set closer (pid and ref). + */ + + if (esock_reader_get(env, descP, + &opDataP->recvRef, + opCaller, + &req)) { + + reason = esock_atom_closed, + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + + /* The socket not being open (assumed closing), + * means we are in the closing phase... + */ + if (! IS_OPEN(descP->readState)) { + + /* We can only send the 'close' message to the closer + * when all requests has been processed! + */ + + /* Check "our" queue */ + if (descP->readersQ.first == NULL) { + + /* Check "other" queue(s) and if there is a closer pid */ + if ((descP->writersQ.first == NULL) && + (descP->acceptorsQ.first == NULL)) { + + esaio_stop(env, descP); + + } + } + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->readersQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + } + FREE_BIN( &opDataP->buf ); + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); + break; + + default: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv(%d) -> operation unknown failure" + "\r\n", descP->sock) ); + MLOCK(descP->readMtx); + /* We do not know what this is + * but we can "assume" that the request failed so we need to + * remove it from the "queue" if its still there... + * And cleanup... + */ + if (esock_reader_get(env, descP, + &opDataP->recvRef, + opCaller, + &req)) { + /* Figure out the reason */ + reason = MKT2(env, + esock_atom_get_overlapped_result, + ENO2T(env, error)); + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + esaio_completion_recv_fail(env, descP, error, FALSE); + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->readersQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + + } else { + esaio_completion_recv_fail(env, descP, error, TRUE); + } + FREE_BIN( &opDataP->buf ); + MUNLOCK(descP->readMtx); + break; + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv {%d} -> clear and delete op env\r\n", + descP->sock) ); + + /* No need for this "stuff" anymore */ + esock_clear_env("esaio_completion_recv - op cleanup", opEnv); + esock_free_env("esaio_completion_recv - op cleanup", opEnv); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recv {%d} -> done\r\n", + descP->sock) ); + + return FALSE; +} + + + +/* *** esaio_completion_recv_completed *** + * The recv request has completed. + */ +static +void esaio_completion_recv_completed(ErlNifEnv* env, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecv* opDataP, + ESockRequestor* reqP) +{ + ERL_NIF_TERM completionStatus, completionInfo; + DWORD read, flags; + + ESOCK_ASSERT( DEMONP("esaio_completion_recv_completed - sender", + env, descP, &reqP->mon) == 0); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_completed ->" + "success - try get overlapped result\r\n") ); + + if (get_recv_ovl_result(descP->sock, ovl, &read, &flags)) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_completed -> overlapped result: " + "\r\n read: %d" + "\r\n buffer size: %d" + "\r\n flags: %d" + "\r\n", read, opDataP->buf.size, flags) ); + + /* *** Success! *** + * CompletionStatus = {ok, {Flags, Bin}} (should be) + * CompletionInfo = {ConnRef, CompletionStatus} + */ + + if ((read == 0) && (descP->type == SOCK_STREAM)) { + + /* + * When a stream socket peer has performed an orderly + * shutdown, the return value will be 0 (the traditional + * "end-of-file" return). + * + * *We* do never actually try to read 0 bytes! + */ + + ESOCK_CNT_INC(env, descP, opDataP->sockRef, + esock_atom_read_fails, &descP->readFails, 1); + + completionStatus = esock_make_error(opEnv, esock_atom_closed); + + } else { + + if (read == opDataP->buf.size) { + /* We filled the buffer => done */ + + completionStatus = + esaio_completion_recv_done(env, descP, + opEnv, opDataP, + flags); + + } else { + + /* Only used a part of the buffer => + * needs splitting and (maybe) retry (its up to the caller)! + */ + + completionStatus = + esaio_completion_recv_partial(env, descP, + opEnv, opDataP, + reqP, read, flags); + } + + } + + } else { + + int save_errno = sock_errno(); + + /* Now what? + * We know we read "something" but we cannot figure out + * how much... + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_completed -> " + "overlapped result failure: %d\r\n", save_errno) ); + + completionStatus = + esaio_completion_get_ovl_result_fail(opEnv, descP, save_errno); + } + + completionInfo = MKT2(opEnv, opDataP->recvRef, completionStatus); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_completed -> " + "send completion message to %T with" + "\r\n CompletionInfo: %T" + "\r\n", MKPID(env, opCaller), completionInfo) ); + + /* Send a 'send' completion message */ + esaio_send_completion_msg(env, // Send env + descP, // Descriptor + opCaller, // Msg destination + opEnv, // Msg env + opDataP->sockRef, // Socket + completionInfo); // Info + + /* *** Finalize *** */ + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recv_completed -> finalize\r\n") ); + + /* Request cleanup (demonitor already done above) */ + esock_clear_env("esaio_completion_recv_completed -> req cleanup", + reqP->env); + esock_free_env("esaio_completion_recv_completed -> req cleanup", + reqP->env); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recv_completed -> done\r\n") ); +} + + + +/* *** esaio_completion_recv_done *** + * + * A complete read (filled the provided buffer). + * + */ +static +ERL_NIF_TERM esaio_completion_recv_done(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecv* opDataP, + DWORD flags) +{ + ERL_NIF_TERM data; + ERL_NIF_TERM sockRef = opDataP->sockRef; + ERL_NIF_TERM recvRef = opDataP->recvRef; + DWORD read = opDataP->buf.size; + + (void) flags; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_done(%T) {%d} -> entry with" + "\r\n recvRef: %T" + "\r\n flags: 0x%X" + "\r\n", sockRef, descP->sock, recvRef, flags) ); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_byte, &descP->readByteCnt, read); + + if (read > descP->readPkgMax) + descP->readPkgMax = read; + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(opEnv, &opDataP->buf); + + /* We ignore the flags *for now*. + * Needs to be passed up eventually! + * + * This should eventually be something like: + * + * {ok, {Flags, Bin}} + * + * But for now we skip the 'flags' part: + * + * {ok, Bin} + */ + return esock_make_ok2(opEnv, data); +} + + +/* *** esaio_completion_recv_partial *** + * + * A partial read, that is only part of the buffer was used. + * + */ +static +ERL_NIF_TERM esaio_completion_recv_partial(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecv* opDataP, + ESockRequestor* reqP, + DWORD read, + DWORD flags) +{ + ERL_NIF_TERM res; + ERL_NIF_TERM sockRef = opDataP->sockRef; + ERL_NIF_TERM recvRef = opDataP->recvRef; + DWORD toRead = opDataP->toRead; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_partial(%T) {%d} -> entry with" + "\r\n toRead: %ld" + "\r\n recvRef: %T" + "\r\n read: %ld" + "\r\n flags: 0x%X" + "\r\n", sockRef, descP->sock, + (long) toRead, recvRef, (long) read, flags) ); + + /* We only got part of what we wanted, but since we let the user + * (or possibly the read loop in socket) decide what to do, + * do we actually need this check? Why not just call a + * 'esaio_completion_recv_partial' function (that splits the + * binary and deliver what we got) and let the user sort it out? + */ + + if ((toRead == 0) || + (descP->type != SOCK_STREAM)) { + + /* +++ We only got a partial, *** + * *** but we should not wait for more. +++ + * +++ Must split it into a sub-binary. +++ + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_partial(%T) {%d} -> done reading\r\n", + sockRef, descP->sock) ); + + res = esaio_completion_recv_partial_done(env, descP, + opEnv, opDataP, + read, flags); + + } else { + + /* A stream socket with specified read size + * and not a polling read, we got a partial read + * - return a result to initiate a retry + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_partial(%T) {%d} ->" + " only part of data - expected more" + "\r\n", sockRef, descP->sock) ); + + res = esaio_completion_recv_partial_part(env, descP, + opEnv, opDataP, + read, flags); + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_partial(%T) {%d} -> done\r\n", + sockRef, descP->sock) ); + + return res; +} + + + +/* *** esaio_completion_recv_partial_done *** + * + * A successful but only partial recv, which fulfilled the required read. + */ + +static +ERL_NIF_TERM esaio_completion_recv_partial_done(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecv* opDataP, + ssize_t read, + DWORD flags) +{ + ERL_NIF_TERM sockRef = opDataP->sockRef; + ERL_NIF_TERM data; + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_byte, &descP->readByteCnt, read); + + if (read > descP->readPkgMax) + descP->readPkgMax = read; + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(opEnv, &opDataP->buf); + data = MKSBIN(opEnv, data, 0, read); + + (void) flags; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_partial_done(%T) {%d} -> done\r\n", + sockRef, descP->sock) ); + + return esock_make_ok2(opEnv, data); +} + + + +/* *** esaio_completion_recv_partial_part *** + * + * A successful but only partial recv, which only partly fulfilled + * the required read. + * We do *not* want to risk ending up in a "never ending" read loop + * here (by trying to read more data (and yet again getting partial)). + * [worst case, we could up with all our worker threads busy trying + * to read more data, and no one ready to respond to new requests]. + * So we simply return what we got to the user and let the user + * decide what to do. + * + * What shall we send? {ok, Bin} | {more, Bin} + * Presumably the user knows how much to expect, so is therefor + * able to check: + * + * "Expected > byte_size(Bin)" -> read again + * "Expected =:= byte_size(Bin)" -> done + */ + +static +ERL_NIF_TERM esaio_completion_recv_partial_part(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecv* opDataP, + ssize_t read, + DWORD flags) +{ + /* This is just a "placeholder". Is this really all we need to do? */ + return esaio_completion_recv_partial_done(env, descP, + opEnv, opDataP, + read, flags); +} + + + +/* *** esaio_completion_recv_not_active *** + * A recv request has completed but the request is no longer valid. + */ +static +void esaio_completion_recv_not_active(ESockDescriptor* descP) +{ + /* This receive request is *not* "active"! + * The receive (recv,recvfrom,recvmsg) operation + * has been (most likely) cancelled => cleanup. + * If the op failed, its safe to assume that the error is + * the result of the cancellation, and we do not actually + * need to do anything here. + * If however, the recv succeeded, we need to do "some + * cleanup". + * But what can we do here? + * Send an abort message to the reader or/and owner? + * Increment a counter (unexpected readsa)? + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_not_active {%d} -> " + "success for not active read request\r\n", descP->sock) ); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.unexpectedReads, 1); + + MUNLOCK(ctrl.cntMtx); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_not_active {%d} -> done\r\n", + descP->sock) ); + +} + + + +/* *** esaio_completion_recv_closed *** + * A recv request has completed but the socket is closed. + * When the socket is closed, all outstanding requests + * are "flushed", so we do not actually need to "do" anything + * here (other then maybe count unexpected writes), maybe read bytes?. + */ +static +void esaio_completion_recv_closed(ESockDescriptor* descP, + int error) +{ + if (error == NO_ERROR) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recv_closed -> " + "success for closed socket (%d)\r\n", + descP->sock) ); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.unexpectedReads, 1); + + MUNLOCK(ctrl.cntMtx); + + } +} + + + +/* *** esaio_completion_recv_fail *** + * Unknown operation failure. + */ +static +void esaio_completion_recv_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform) +{ + esaio_completion_fail(env, descP, "recv", error, inform); +} + + + +/* *** esaio_completion_recvfrom *** + * + * Handle a completed 'recvfrom' (completion) request. + * Send a 'completion' message (to requestor) with the request status. + * + * Completion message: + * {'socket tag', socket(), completion, CompletionInfo} + * + * CompletionInfo: {CompletionHandle, CompletionStatus} + * CompletionHandle: reference() + * Result: ok | {error, Reason} + * + * + * There is a possibillity of a race here. That is, if the user + * calls socket:recvfrom(Socket, ..., nowait), the receive is scheduled, + * and then just as it has completed, but before this + * thread has been activated to handle the 'recv completed' + * the user calls socket:close(Socket) (or exits). + * Then when this function is called, the socket is closed. + * What to do? + */ +static +BOOLEAN_T esaio_completion_recvfrom(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecvFrom* opDataP, + int error) +{ + ErlNifEnv* env = dataP->env; + ESockRequestor req; + ERL_NIF_TERM reason; + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recvfrom(%d) -> entry with" + "\r\n error: %T, %s (%d)" + "\r\n", + descP->sock, ENO2T(env, error), + erl_errno_id(error), error) ); + + switch (error) { + case NO_ERROR: + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recvfrom(%d) -> no error" + "\r\n", descP->sock) ); + MLOCK(descP->readMtx); + if (esock_reader_get(env, descP, + &opDataP->recvRef, + opCaller, + &req)) { + if (IS_OPEN(descP->readState)) { + esaio_completion_recvfrom_completed(env, descP, + ovl, opEnv, opCaller, + opDataP, &req); + } else { + /* A completed (active) request for a socket that is not open. + * Is this even possible? + * A race (completed just as the socket was closed). + */ + esaio_completion_recv_not_active(descP); + FREE_BIN( &opDataP->buf ); + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->readersQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + + } else { + /* Request was actually completed directly + * (and was therefor not put into the "queue") + * => Nothing to do here, other than cleanup (see below). + * => But we do not free the "buffer" since it was "used up" + * when we (as assumed) got the result (directly)... + */ + } + MUNLOCK(descP->readMtx); + break; + + case WSA_OPERATION_ABORTED: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom(%d) -> operation aborted" + "\r\n", descP->sock) ); + /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */ + MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); + /* The only thing *we* do that could cause an abort is the + * 'CancelIoEx' call, which we do when closing the socket + * (or cancel a request). + * But if we have done that; + * - Socket state will not be 'open' and + * - we have also set closer (pid and ref). + */ + + if (esock_reader_get(env, descP, + &opDataP->recvRef, + opCaller, + &req)) { + + reason = esock_atom_closed, + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + + /* The socket not being open (assumed closing), + * means we are in the closing phase... + */ + if (! IS_OPEN(descP->readState)) { + + /* We can only send the 'close' message to the closer + * when all requests has been processed! + */ + + /* Check "our" queue */ + if (descP->readersQ.first == NULL) { + + /* Check "other" queue(s) and if there is a closer pid */ + if ((descP->writersQ.first == NULL) && + (descP->acceptorsQ.first == NULL)) { + + esaio_stop(env, descP); + + } + } + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->readersQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + + } + FREE_BIN( &opDataP->buf ); + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); + break; + + default: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom(%d) -> operation unknown failure" + "\r\n", descP->sock) ); + MLOCK(descP->readMtx); + /* We do not know what this is + * but we can "assume" that the request failed so we need to + * remove it from the "queue" if its still there... + * And cleanup... + */ + if (esock_reader_get(env, descP, + &opDataP->recvRef, + opCaller, + &req)) { + + reason = MKT2(env, + esock_atom_get_overlapped_result, + ENO2T(env, error)); + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + esaio_completion_recvfrom_fail(env, descP, error, FALSE); + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->readersQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + + } else { + esaio_completion_recvfrom_fail(env, descP, error, TRUE); + } + FREE_BIN( &opDataP->buf ); + MUNLOCK(descP->readMtx); + break; + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom {%d} -> clear and delete op env\r\n") ); + + /* No need for this "stuff" anymore */ + esock_clear_env("esaio_completion_recvfrom - op cleanup", opEnv); + esock_free_env("esaio_completion_recvfrom - op cleanup", opEnv); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recvfrom {%d} -> done\r\n") ); + + return FALSE; +} + + + +/* *** esaio_completion_recvfrom_completed *** + * The recvfrom request has completed. + */ +static +void esaio_completion_recvfrom_completed(ErlNifEnv* env, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecvFrom* opDataP, + ESockRequestor* reqP) +{ + ERL_NIF_TERM completionStatus, completionInfo; + DWORD read, flags; + + ESOCK_ASSERT( DEMONP("esaio_completion_recvfrom_completed - sender", + env, descP, &reqP->mon) == 0); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom_completed ->" + "success - try get overlapped result\r\n") ); + + if (get_recv_ovl_result(descP->sock, ovl, &read, &flags)) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom_completed -> overlapped result: " + "\r\n read: %d" + "\r\n buffer size: %d" + "\r\n flags: %d" + "\r\n", read, opDataP->buf.size, flags) ); + + /* *** Success! *** + * CompletionStatus = {ok, {Flags, Bin}} (should be) + * CompletionInfo = {ConnRef, CompletionStatus} + */ + + if (read == opDataP->buf.size) { + /* We filled the buffer => done */ + + completionStatus = + esaio_completion_recvfrom_done(env, descP, + opEnv, opDataP, + flags); + + } else { + + /* Only used a part of the buffer => + * needs splitting and (maybe) retry (its up to the caller)! + */ + + completionStatus = + esaio_completion_recvfrom_partial(env, descP, + opEnv, opDataP, + reqP, read, flags); + } + + } else { + + int save_errno = sock_errno(); + + /* Now what? + * We know we read "something" but we cannot figure out + * how much... + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom_completed -> " + "overlapped result failure: %d\r\n", save_errno) ); + + completionStatus = + esaio_completion_get_ovl_result_fail(opEnv, descP, save_errno); + + FREE_BIN( &opDataP->buf ); + } + + completionInfo = MKT2(opEnv, opDataP->recvRef, completionStatus); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom_completed -> " + "send completion message to %T with" + "\r\n CompletionInfo: %T" + "\r\n", MKPID(env, opCaller), completionInfo) ); + + /* Send a 'send' completion message */ + esaio_send_completion_msg(env, // Send env + descP, // Descriptor + opCaller, // Msg destination + opEnv, // Msg env + opDataP->sockRef, // Dest socket + completionInfo); // Info + + /* *** Finalize *** */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom_completed -> finalize\r\n") ); + + /* Request cleanup (demonitor already done above) */ + esock_clear_env("esaio_completion_recvfrom_completed -> req cleanup", + reqP->env); + esock_free_env("esaio_completion_recvfrom_completed -> req cleanup", + reqP->env); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recvfrom_completed -> done\r\n") ); +} + + + +/* *** esaio_completion_recvfrom_done *** + * + * A complete read (filled the provided buffer). + * + */ +static +ERL_NIF_TERM esaio_completion_recvfrom_done(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecvFrom* opDataP, + DWORD flags) +{ + ERL_NIF_TERM res, data, eSockAddr; + ERL_NIF_TERM sockRef = opDataP->sockRef; + ERL_NIF_TERM recvRef = opDataP->recvRef; + DWORD read = opDataP->buf.size; + + (void) flags; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom_done(%T) {%d} -> entry with" + "\r\n recvRef: %T" + "\r\n flags: 0x%X" + "\r\n", sockRef, descP->sock, recvRef, flags) ); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_byte, &descP->readByteCnt, read); + + if (read > descP->readPkgMax) + descP->readPkgMax = read; + + esock_encode_sockaddr(opEnv, + &opDataP->fromAddr, + opDataP->addrLen, + &eSockAddr); + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(opEnv, &opDataP->buf); + + /* We ignore the flags *for now*. + * Needs to be passed up eventually! + * + * This should eventually be something like: + * + * {ok, {Source, Flags, Data}} + * + * But for now we skip the 'flags' part: + * + * {ok, {Source, Data}} + */ + res = esock_make_ok2(opEnv, MKT2(opEnv, eSockAddr, data)); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom_done(%T) {%d} -> done\r\n", + sockRef, descP->sock) ); + + return res; +} + + + +/* *** esaio_completion_recvfrom_partial *** + * + * A partial read, that is only part of the buffer was used. + * + */ +static +ERL_NIF_TERM esaio_completion_recvfrom_partial(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecvFrom* opDataP, + ESockRequestor* reqP, + DWORD read, + DWORD flags) +{ + ERL_NIF_TERM res, data, eSockAddr; + ERL_NIF_TERM sockRef = opDataP->sockRef; + ERL_NIF_TERM recvRef = opDataP->recvRef; + + (void) flags; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom_partial(%T) {%d} -> entry with" + "\r\n recvRef: %T" + "\r\n read: %ld" + "\r\n flags: 0x%X" + "\r\n", sockRef, descP->sock, recvRef, (long) read, flags) ); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_byte, &descP->readByteCnt, read); + + if (read > descP->readPkgMax) + descP->readPkgMax = read; + + esock_encode_sockaddr(opEnv, + &opDataP->fromAddr, + opDataP->addrLen, + &eSockAddr); + + /* This transfers "ownership" of the *allocated* binary to an + * erlang term (no need for an explicit free). + */ + data = MKBIN(opEnv, &opDataP->buf); + data = MKSBIN(opEnv, data, 0, read); + + /* We ignore the flags *for now*. + * Needs to be passed up eventually! + * + * This should eventually be something like: + * + * {ok, {Source, Flags, Data}} + * + * But for now we skip the 'flags' part: + * + * {ok, {Source, Data}} + */ + + res = esock_make_ok2(opEnv, MKT2(opEnv, eSockAddr, data)); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvfrom_partial(%T) {%d} -> done\r\n", + sockRef, descP->sock) ); + + return res; +} + + + +/* *** esaio_completion_recvfrom_fail *** + * Unknown operation failure. + */ +static +void esaio_completion_recvfrom_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform) +{ + esaio_completion_fail(env, descP, "recvfrom", error, inform); +} + + + +/* *** esaio_completion_recvmsg *** + * + * Handle a completed 'recvmsg' (completion) request. + * Send a 'completion' message (to requestor) with the request status. + * + * Completion message: + * {'socket tag', socket(), completion, CompletionInfo} + * + * CompletionInfo: {CompletionHandle, CompletionStatus} + * CompletionHandle: reference() + * Result: ok | {error, Reason} + * + * + * There is a possibillity of a race here. That is, if the user + * calls socket:recvmsg(Socket, ..., nowait), the receive is scheduled, + * and then just as it has completed, but before this + * thread has been activated to handle the 'recv completed' + * the user calls socket:close(Socket) (or exits). + * Then when this function is called, the socket is closed. + * What to do? + */ +static +BOOLEAN_T esaio_completion_recvmsg(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecvMsg* opDataP, + int error) +{ + ErlNifEnv* env = dataP->env; + ESockRequestor req; + ERL_NIF_TERM reason; + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recvmsg(%d) -> entry with" + "\r\n error: %T" + "\r\n", descP->sock, ENO2T(env, error)) ); + + switch (error) { + case NO_ERROR: + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recvmsg(%d) -> no error:" + "\r\n try get request %T from %T" + "\r\n", + descP->sock, + opDataP->recvRef, MKPID(env, opCaller)) ); + MLOCK(descP->readMtx); + if (esock_reader_get(env, descP, + &opDataP->recvRef, + opCaller, + &req)) { + if (IS_OPEN(descP->readState)) { + esaio_completion_recvmsg_completed(env, descP, + ovl, opEnv, opCaller, + opDataP, + &req); + } else { + /* A completed (active) request for a socket that is not open. + * Is this even possible? + * A race (completed just as the socket was closed). + */ + esaio_completion_recv_not_active(descP); + FREE_BIN( &opDataP->data[0] ); + FREE_BIN( &opDataP->ctrl ); + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->readersQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + + } else { + /* Request was actually completed directly + * (and was therefor not put into the "queue") + * => Nothing to do here, other than cleanup (see below). + * => But we do not free the "buffer" since it was "used up" + * when we (as assumed) got the result (directly)... + */ + } + MUNLOCK(descP->readMtx); + break; + + case WSA_OPERATION_ABORTED: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg(%d) -> operation aborted" + "\r\n", descP->sock) ); + /* *** SAME MTX LOCK ORDER FOR ALL OPs *** */ + MLOCK(descP->readMtx); + MLOCK(descP->writeMtx); + /* The only thing *we* do that could cause an abort is the + * 'CancelIoEx' call, which we do when closing the socket + * (or cancel a request). + * But if we have done that; + * - Socket state will not be 'open' and + * - we have also set closer (pid and ref). + */ + + if (esock_reader_get(env, descP, + &opDataP->recvRef, + opCaller, + &req)) { + + reason = esock_atom_closed, + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + + /* The socket not being open (assumed closing), + * means we are in the closing phase... + */ + if (! IS_OPEN(descP->readState)) { + + /* We can only send the 'close' message to the closer + * when all requests has been processed! + */ + + /* Check "our" queue */ + if (descP->readersQ.first == NULL) { + + /* Check "other" queue(s) and if there is a closer pid */ + if ((descP->writersQ.first == NULL) && + (descP->acceptorsQ.first == NULL)) { + + esaio_stop(env, descP); + + } + } + } + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->readersQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + + } + FREE_BIN( &opDataP->data[0] ); + FREE_BIN( &opDataP->ctrl ); + MUNLOCK(descP->writeMtx); + MUNLOCK(descP->readMtx); + break; + + default: + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg(%d) -> operation unknown failure" + "\r\n", descP->sock) ); + MLOCK(descP->readMtx); + /* We do not know what this is + * but we can "assume" that the request failed so we need to + * remove it from the "queue" if its still there... + * And cleanup... + */ + if (esock_reader_get(env, descP, + &opDataP->recvRef, + opCaller, + &req)) { + + reason = MKT2(env, + esock_atom_get_overlapped_result, + ENO2T(env, error)); + + /* Inform the user waiting for a reply */ + esock_send_abort_msg(env, descP, opDataP->sockRef, + &req, reason); + esaio_completion_recvmsg_fail(env, descP, error, FALSE); + + /* *Maybe* update socket (write) state + * (depends on if the queue is now empty) + */ + if (descP->readersQ.first == NULL) { + descP->readState &= ~ESOCK_STATE_SELECTED; + } + + } else { + esaio_completion_recvmsg_fail(env, descP, error, TRUE); + } + FREE_BIN( &opDataP->data[0] ); + FREE_BIN( &opDataP->ctrl ); + MUNLOCK(descP->readMtx); + break; + } + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg {%d} -> clear and delete op env\r\n", + descP->sock) ); + + /* No need for this "stuff" anymore */ + esock_clear_env("esaio_completion_recvmsg - op cleanup", opEnv); + esock_free_env("esaio_completion_recvmsg - op cleanup", opEnv); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recvmsg {%d} -> done\r\n", + descP->sock) ); + + return FALSE; +} + + + +/* *** esaio_completion_recvmsg_completed *** + * The recvmsg request has completed. + */ +static +void esaio_completion_recvmsg_completed(ErlNifEnv* env, + ESockDescriptor* descP, + OVERLAPPED* ovl, + ErlNifEnv* opEnv, + ErlNifPid* opCaller, + ESAIOOpDataRecvMsg* opDataP, + ESockRequestor* reqP) +{ + ERL_NIF_TERM completionStatus, completionInfo; + DWORD read, flags; + + ESOCK_ASSERT( DEMONP("esaio_completion_recvmsg_completed - sender", + env, descP, &reqP->mon) == 0); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg_completed ->" + "success - try get overlapped result\r\n") ); + + if (get_recv_ovl_result(descP->sock, ovl, &read, &flags)) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg_completed -> overlapped result: " + "\r\n read: %d" + "\r\n buffer size: %d" + "\r\n flags: %d" + "\r\n", read, opDataP->data[0].size, flags) ); + + /* *** Success! *** + * CompletionStatus = {ok, Msg} (should be) + * CompletionInfo = {ConnRef, CompletionStatus} + */ + + /* We should have "calculated" the entire size before, + * but since we have a vector of size one... + */ + if (read == opDataP->data[0].size) { + /* We filled the buffer => done */ + + completionStatus = + esaio_completion_recvmsg_done(env, descP, + opEnv, opDataP, + flags); + + } else { + + /* Only used a part of the buffer => + * needs splitting and (maybe) retry (its up to the caller)! + */ + + completionStatus = + esaio_completion_recvmsg_partial(env, descP, + opEnv, opDataP, + reqP, read, flags); + } + + } else { + + int save_errno = sock_errno(); + + /* Now what? + * We know we read "something" but we cannot figure out + * how much... + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg_completed -> " + "overlapped result failure: %d\r\n", save_errno) ); + + completionStatus = + esaio_completion_get_ovl_result_fail(opEnv, descP, save_errno); + + } + + completionInfo = MKT2(opEnv, opDataP->recvRef, completionStatus); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg_completed -> " + "send completion message to %T with" + "\r\n CompletionInfo: %T" + "\r\n", MKPID(env, opCaller), completionInfo) ); + + /* Send a 'send' completion message */ + esaio_send_completion_msg(env, // Send env + descP, // Descriptor + opCaller, // Msg destination + opEnv, // Msg env + opDataP->sockRef, // Dest socket + completionInfo); // Info + + /* *** Finalize *** */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg_completed -> finalize\r\n") ); + + /* Request cleanup (demonitor already done above) */ + esock_clear_env("esaio_completion_recvmsg_completed -> req cleanup", + reqP->env); + esock_free_env("esaio_completion_recvmsg_completed -> req cleanup", + reqP->env); + + SSDBG( descP, + ("WIN-ESAIO", "esaio_completion_recvmsg_completed -> done\r\n") ); +} + + + +/* *** esaio_completion_recvmsg_done *** + * + * A complete read (filled the provided buffer). + * + */ +static +ERL_NIF_TERM esaio_completion_recvmsg_done(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecvMsg* opDataP, + DWORD flags) +{ + ERL_NIF_TERM res, eMsg; + ERL_NIF_TERM sockRef = opDataP->sockRef; + ERL_NIF_TERM recvRef = opDataP->recvRef; + DWORD read = opDataP->data[0].size; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg_done(%T) {%d} -> entry with" + "\r\n recvRef: %T" + "\r\n flags: 0x%X" + "\r\n", sockRef, descP->sock, recvRef, flags) ); + + (void) flags; + + encode_msg(opEnv, descP, read, + &opDataP->msg, + opDataP->data, + &opDataP->ctrl, + &eMsg); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_byte, &descP->readByteCnt, read); + + if (read > descP->readPkgMax) + descP->readPkgMax = read; + + res = esock_make_ok2(opEnv, eMsg); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg_done(%T) {%d} -> done\r\n", + sockRef, descP->sock) ); + + return res; +} + + + +/* *** esaio_completion_recvmsg_partial *** + * + * A partial read, that is only part of the buffer was used. + * + */ +static +ERL_NIF_TERM esaio_completion_recvmsg_partial(ErlNifEnv* env, + ESockDescriptor* descP, + ErlNifEnv* opEnv, + ESAIOOpDataRecvMsg* opDataP, + ESockRequestor* reqP, + DWORD read, + DWORD flags) +{ + ERL_NIF_TERM res, eMsg; + ERL_NIF_TERM sockRef = opDataP->sockRef; + ERL_NIF_TERM recvRef = opDataP->recvRef; + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg_partial(%T) {%d} -> entry with" + "\r\n recvRef: %T" + "\r\n read: %ld" + "\r\n flags: 0x%X" + "\r\n", sockRef, descP->sock, recvRef, (long) read, flags) ); + + (void) flags; + + /* This function hansles splitting the binaries */ + encode_msg(opEnv, descP, read, + &opDataP->msg, + opDataP->data, + &opDataP->ctrl, + &eMsg); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_pkg, &descP->readPkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_read_byte, &descP->readByteCnt, read); + + if (read > descP->readPkgMax) + descP->readPkgMax = read; + + res = esock_make_ok2(opEnv, eMsg); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_recvmsg_partial(%T) {%d} -> done\r\n", + sockRef, descP->sock) ); + + return res; +} + + + +/* *** esaio_completion_recvmsg_fail *** + * Unknown operation failure. + */ +static +void esaio_completion_recvmsg_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error, + BOOLEAN_T inform) +{ + esaio_completion_fail(env, descP, "recvmsg", error, inform); +} + + + +/* *** esaio_completion_get_ovl_result_fail *** + * This function is called when the function 'WSAGetOverlappedResult' fails. + * It generates a result (returns) in the form of: + * + * {error, {get_overlapped_result, atom()}} + */ +static +ERL_NIF_TERM esaio_completion_get_ovl_result_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int error) +{ + ERL_NIF_TERM eerrno = ENO2T(env, error); + ERL_NIF_TERM reason = MKT2(env, esock_atom_get_overlapped_result, eerrno); + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_completion_get_ovl_result_fail{%d} -> entry with" + "\r\n Errno: %d (%T)" + "\r\n", descP->sock, error, eerrno) ); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.genErrs, 1); + + MUNLOCK(ctrl.cntMtx); + + return esock_make_error(env, reason); +} + + + + +/* === Unknown command 'stuff' === */ + +/* *** esaio_completion_unknown *** + * What shall we actually do here? + * Increment counters (bytes, number of unknown packages)? + * Send a messge with this info to "someone"? + * Write a (error) message to stdout/stderr? + */ +static +BOOLEAN_T esaio_completion_unknown(ESAIOThreadData* dataP, + ESockDescriptor* descP, + OVERLAPPED* ovl, + DWORD numBytes, + int error) +{ + (void) dataP; + (void) descP; + (void) ovl; + (void) numBytes; + (void) error; + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.unknownCmds, 1); + + MUNLOCK(ctrl.cntMtx); + + return FALSE; +} + + + +/* *** esaio_completion_fail *** + * Unknown operation failure (not 'unknown operation' failure, + * but an unknown 'operation failure'). + */ +static +void esaio_completion_fail(ErlNifEnv* env, + ESockDescriptor* descP, + const char* opStr, + int error, + BOOLEAN_T inform) +{ + if (inform) + esock_warning_msg("[WIN-ESAIO] Unknown (%s) operation failure: " + "\r\n Descriptor: %d" + "\r\n Errno: %T" + "\r\n", + opStr, descP->sock, ENO2T(env, error)); + + MLOCK(ctrl.cntMtx); + + esock_cnt_inc(&ctrl.genErrs, 1); + + MUNLOCK(ctrl.cntMtx); + +} + + + +static +void esaio_completion_inc(ESAIOThreadData* dataP) +{ + if (dataP->cnt == ESAIO_THREAD_CNT_MAX) { + dataP->cnt = 0; + } else { + dataP->cnt++; + } +} + + + +/* ==================================================================== + * + * NIF (I/O backend) Resource callback functions: dtor, stop and down + * + * ==================================================================== + */ + +extern +void esaio_dtor(ErlNifEnv* env, + ESockDescriptor* descP) +{ + ERL_NIF_TERM sockRef; + + SGDBG( ("WIN-ESAIO", "esaio_dtor -> entry\r\n") ); + + /* + ESOCK_PRINTF("esaio_dtor -> entry when" + "\r\n is selected: %s" + "\r\n read state: 0x%X" + "\r\n is read-closed: %s" + "\r\n write state: 0x%X" + "\r\n is write-closed: %s" + "\r\n sock: %d" + "\r\n", + B2S(IS_SELECTED(descP)), + descP->readState, + B2S(IS_CLOSED(descP->readState)), + descP->writeState, + B2S(IS_CLOSED(descP->writeState)), + descP->sock); + */ + + if (IS_SELECTED(descP)) { + /* We have used the socket in the "I/O Completion Port" machinery, + * so we must have closed it properly to get here + */ + if (! IS_CLOSED(descP->readState) ) + esock_warning_msg("Socket Read State not CLOSED at dtor\r\n"); + + if (! IS_CLOSED(descP->writeState) ) + esock_warning_msg("Socket Write State not CLOSED at dtor\r\n"); + + if ( descP->sock != INVALID_SOCKET ) + esock_warning_msg("Socket %d still valid\r\n", descP->sock); + + ESOCK_ASSERT( IS_CLOSED(descP->readState) ); + ESOCK_ASSERT( IS_CLOSED(descP->writeState) ); + ESOCK_ASSERT( descP->sock == INVALID_SOCKET ); + + } else { + /* The socket is only opened, should be safe to close nonblocking */ + (void) sock_close(descP->sock); + descP->sock = INVALID_SOCKET; + } + + SGDBG( ("WIN-ESAIO", "esaio_dtor -> set state and pattern\r\n") ); + descP->readState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED); + descP->writeState |= (ESOCK_STATE_DTOR | ESOCK_STATE_CLOSED); + descP->pattern = (ESOCK_DESC_PATTERN_DTOR | ESOCK_STATE_CLOSED); + + SGDBG( ("WIN-ESAIO", + "esaio_dtor -> try free readers request queue\r\n") ); + esock_free_request_queue(&descP->readersQ); + + SGDBG( ("WIN-ESAIO", + "esaio_dtor -> try free writers request queue\r\n") ); + esock_free_request_queue(&descP->writersQ); + + SGDBG( ("WIN-ESAIO", + "esaio_dtor -> try free acceptors request queue\r\n") ); + esock_free_request_queue(&descP->acceptorsQ); + + esock_free_env("esaio_dtor close env", descP->closeEnv); + descP->closeEnv = NULL; + + esock_free_env("esaio_dtor meta env", descP->meta.env); + descP->meta.env = NULL; + + SGDBG( ("WIN-ESAIO", "esaio_dtor -> done\r\n") ); +} + + + +extern +void esaio_stop(ErlNifEnv* env, + ESockDescriptor* descP) +{ + + SSDBG( descP, + ("WIN-ESAIO", "esaio_stop(%d) -> entry\r\n", descP->sock) ); + + /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + * + * Inform waiting Closer, or close socket + * + * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + */ + + if (! IS_PID_UNDEF(&descP->closerPid)) { + /* We have a waiting closer process after nif_close() + * - send message to trigger nif_finalize_close() + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_stop(%d) -> send close msg to %T\r\n", + descP->sock, MKPID(env, &descP->closerPid)) ); + + esock_send_close_msg(env, descP, &descP->closerPid); + /* Message send frees closeEnv */ + descP->closeEnv = NULL; + descP->closeRef = esock_atom_undefined; + + } else { + int err; + + /* We do not have a closer process + * - have to do an unclean (non blocking) close */ + + err = esock_close_socket(env, descP, FALSE); + + if (err != 0) + esock_warning_msg("[WIN-ESAIO] Failed closing socket without " + "closer process: " + "\r\n Controlling Process: %T" + "\r\n socket fd: %d" + "\r\n Errno: %T" + "\r\n", + descP->ctrlPid, descP->sock, ENO2T(env, err)); + } + + SSDBG( descP, + ("WIN-ESAIO", "esaio_stop(%d) -> done\r\n", descP->sock) ); + +} + + + + +/* A 'down' has occured. + * Check the possible processes we monitor in turn: + * closer, controlling process (owner), connector, reader, acceptor and writer. + * + */ +extern +void esaio_down(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP, + const ErlNifMonitor* monP) +{ + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down {%d} -> entry with:" + "\r\n Pid: %T" + "\r\n Mon: %T" + "\r\n", descP->sock, MKPID(env, pidP), MON2T(env, monP)) ); + + if (COMPARE_PIDS(&descP->closerPid, pidP) == 0) { + + /* The closer process went down + * - it will not call nif_finalize_close + */ + + enif_set_pid_undefined(&descP->closerPid); + + if (MON_EQ(&descP->closerMon, monP)) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down {%d} -> closer process exit\r\n", + descP->sock) ); + + MON_INIT(&descP->closerMon); + + } else { + // The owner is the closer so we used its monitor + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down {%d} -> closer controlling process exit\r\n", + descP->sock) ); + + ESOCK_ASSERT( MON_EQ(&descP->ctrlMon, monP) ); + MON_INIT(&descP->ctrlMon); + enif_set_pid_undefined(&descP->ctrlPid); + + } + + /* Since the closer went down there was one, + * hence esock_close() must have run or scheduled esock_stop(), + * or the socket has never been "selected" upon. + */ + + if (descP->closeEnv == NULL) { + int err; + + /* Since there is no closeEnv, + * esock_close() did not schedule esock_stop() + * and is about to call esock_finalize_close() but died, + * or esock_stop() has run, sent close_msg to the closer + * and cleared ->closeEnv but the closer died + * - we have to do an unclean (non blocking) socket close here + */ + + err = esock_close_socket(env, descP, FALSE); + if (err != 0) + esock_warning_msg("[WIN-ESAIO] " + "Failed closing socket for terminating " + "closer process: " + "\r\n Closer Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d (%T)" + "\r\n", + MKPID(env, pidP), descP->sock, + err, ENO2T(env, err)); + } else { + /* Since there is a closeEnv esock_stop() has not run yet + * - when it finds that there is no closer process + * it will close the socket and ignore the close_msg + * + * The 'stop' callback function will never be triggered on + * Windows...It may be explicitly called... + */ + esock_clear_env("esaio_down - close-env", descP->closeEnv); + esock_free_env("esaio_down - close-env", descP->closeEnv); + descP->closeEnv = NULL; + descP->closeRef = esock_atom_undefined; + } + + } else if (MON_EQ(&descP->ctrlMon, monP)) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down {%d} -> controller process exit\r\n", + descP->sock) ); + + MON_INIT(&descP->ctrlMon); + /* The owner went down */ + enif_set_pid_undefined(&descP->ctrlPid); + + if (IS_OPEN(descP->readState)) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down {%d} -> OPEN => initiate close\r\n", + descP->sock) ); + + esaio_down_ctrl(env, descP, pidP); + + descP->readState |= ESOCK_STATE_CLOSING; + descP->writeState |= ESOCK_STATE_CLOSING; + + } else { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down {%d} -> already closed or closing\r\n", + descP->sock) ); + + } + + } else if (descP->connectorP != NULL && + MON_EQ(&descP->connector.mon, monP)) { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down {%d} -> connector process exit\r\n", + descP->sock) ); + + MON_INIT(&descP->connector.mon); + + /* connectorP is only set during connection. + * Forget all about the ongoing connection. + * We might end up connected, but the process that initiated + * the connection has died and will never know + */ + + esock_requestor_release("esaio_down->connector", + env, descP, &descP->connector); + descP->connectorP = NULL; + descP->writeState &= ~ESOCK_STATE_CONNECTING; + + } else { + ERL_NIF_TERM sockRef = enif_make_resource(env, descP); + + /* check all operation queue(s): acceptor, writer and reader. + * + * Is it really any point in doing this if the socket is closed? + * + */ + + if (IS_CLOSED(descP->readState)) { + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down(%T) {%d} -> stray down: %T\r\n", + sockRef, descP->sock, pidP) ); + } else { + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down(%T) {%d} -> " + "other process - check readers, writers and acceptors\r\n", + sockRef, descP->sock) ); + + if (descP->readersQ.first != NULL) + esaio_down_reader(env, descP, sockRef, pidP, monP); + if (descP->acceptorsQ.first != NULL) + esaio_down_acceptor(env, descP, sockRef, pidP, monP); + if (descP->writersQ.first != NULL) + esaio_down_writer(env, descP, sockRef, pidP, monP); + } + } + + SSDBG( descP, ("WIN-ESAIO", "esaio_down {%d} -> done\r\n", descP->sock) ); + +} + + + +/* *** esaio_down_ctrl *** + * + * Stop after a downed controller (controlling process = owner process) + * + * This is 'extern' because its currently called from prim_socket_nif + * (esock_setopt_otp_ctrl_proc). + */ +extern +void esaio_down_ctrl(ErlNifEnv* env, + ESockDescriptor* descP, + const ErlNifPid* pidP) +{ + SSDBG( descP, + ("WIN-ESAIO", "esaio_down_ctrl {%d} -> entry with" + "\r\n Pid: %T" + "\r\n", descP->sock, MKPID(env, pidP)) ); + + if (do_stop(env, descP)) { + /* esock_stop() is scheduled + * - it has to close the socket + */ + SSDBG( descP, + ("WIN-ESAIO", "esaio_down_ctrl {%d} -> stop was scheduled\r\n", + descP->sock) ); + } else { + int err; + + /* Socket has no *active* requests in the I/O Completion Ports machinery + * so esock_stop() will not be called + * - we have to do an unclean (non blocking) socket close here + */ + + err = esock_close_socket(env, descP, FALSE); + if (err != 0) + esock_warning_msg("[WIN-ESAIO] " + "Failed closing socket for terminating " + "owner process: " + "\r\n Owner Process: %T" + "\r\n Descriptor: %d" + "\r\n Errno: %d (%T)" + "\r\n", + MKPID(env, pidP), descP->sock, + err, ENO2T(env, err)); + } + + SSDBG( descP, + ("WIN-ESAIO", "esaio_down_ctrl {%d} -> done\r\n", descP->sock) ); + +} + + + +/* *** esaio_down_acceptor *** + * + * Check and then handle a downed acceptor process. + * + */ +static +void esaio_down_acceptor(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP) +{ + + /* Maybe unqueue one of the waiting acceptors */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down_acceptor(%T) {%d} -> " + "maybe unqueue a waiting acceptor\r\n", + sockRef, descP->sock) ); + + esock_acceptor_unqueue(env, descP, NULL, pidP); + +} + + +/* *** esaio_down_writer *** + * + * Check and then handle a downed writer process. + * + */ + +static +void esaio_down_writer(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP) +{ + + /* Maybe unqueue one of the waiting writer(s) */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down_writer(%T) {%d} -> maybe unqueue a waiting writer\r\n", + sockRef, descP->sock) ); + + esock_writer_unqueue(env, descP, NULL, pidP); + +} + + +/* *** esaio_down_reader *** + * + * Check and then handle a downed reader process. + * + */ + +static +void esaio_down_reader(ErlNifEnv* env, + ESockDescriptor* descP, + ERL_NIF_TERM sockRef, + const ErlNifPid* pidP, + const ErlNifMonitor* monP) +{ + /* Maybe unqueue one of the waiting reader(s) */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_down_reader(%T) {%d} -> maybe unqueue a waiting reader\r\n", + sockRef, descP->sock) ); + + esock_reader_unqueue(env, descP, NULL, pidP); + +} + + +/* ==================================================================== * + * * + * Send Utility functions * + * * + * ==================================================================== * + */ + +/* *** send_check_result *** + * + * Check the result of a socket send (WSASend, WSASendTo and WSASendMsg) call. + * + */ +static +ERL_NIF_TERM send_check_result(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + int send_result, + ssize_t dataSize, + BOOLEAN_T dataInTail, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef, + BOOLEAN_T* cleanup) +{ + ERL_NIF_TERM res; + BOOLEAN_T send_error; + int err; + + if (send_result == 0) { + + /* Send success already! + * So, no need to store the data (request, in the "queue"). + * Note that the completion threads will use the + * precense or absence of this request 'record' to inform + * its actions. + */ + + *cleanup = FALSE; + + res = send_check_ok(env, descP, dataSize, sockRef); + + } else { + + /* send returned error, check which */ + + int save_errno = sock_errno(); + + /* There are basically two kinds of errors: + * 1) Pending: + * An overlapped operation was successfully initiated. + * Completion will be "indicated" at a later time. + * 2) An actual error + */ + + if (save_errno == WSA_IO_PENDING) { + + /* We need to store the data in the queue! */ + + *cleanup = FALSE; + + res = send_check_pending(env, descP, opP, caller, sockRef, sendRef); + + } else { + + *cleanup = TRUE; + + res = send_check_fail(env, descP, save_errno, sockRef); + + } + } + + SSDBG( descP, + ("WIN-ESAIO", + "send_check_result(%T) {%d} -> done:" + "\r\n res: %T" + "\r\n", sockRef, descP->sock, res) ); + + return res; +} + + + +/* *** send_check_ok *** + * + * Processing done upon successful send. + */ +static +ERL_NIF_TERM send_check_ok(ErlNifEnv* env, + ESockDescriptor* descP, + DWORD written, + ERL_NIF_TERM sockRef) +{ + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_pkg, &descP->writePkgCnt, 1); + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_byte, &descP->writeByteCnt, written); + + /* We can *never* have a partial successs: + * Either the entire buffer is sent, or the op is scheduled or we fail. + * But since we have a field (writePkgMaxCnt) in the descriptor + * we might as well use it. + */ + + descP->writePkgMaxCnt = written; + if (descP->writePkgMaxCnt > descP->writePkgMax) + descP->writePkgMax = descP->writePkgMaxCnt; + descP->writePkgMaxCnt = 0; + + SSDBG( descP, + ("WIN-ESAIO", "send_check_ok(%T) {%d} -> %ld written - done\r\n", + sockRef, descP->sock, written) ); + + return esock_atom_ok; +} + + +/* *** send_check_pending *** + * + * The send operation was scheduled, that is, its now in the handls + * of the I/O Completion Port framework. + */ +static +ERL_NIF_TERM send_check_pending(ErlNifEnv* env, + ESockDescriptor* descP, + ESAIOOperation* opP, + ErlNifPid caller, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM sendRef) +{ + SSDBG( descP, + ("WIN-ESAIO", + "send_check_pending(%T, %d) -> entry with" + "\r\n sendRef: %T" + "\r\n", sockRef, descP->sock, sendRef) ); + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_waits, &descP->writeWaits, 1); + + descP->writeState |= ESOCK_STATE_SELECTED; + + esock_writer_push(env, descP, caller, sendRef, opP); + + return esock_atom_completion; + +} + + + +/* *** send_check_fail *** + * + * Processing done upon failed send. + * An actual failure. + */ +static +ERL_NIF_TERM send_check_fail(ErlNifEnv* env, + ESockDescriptor* descP, + int saveErrno, + ERL_NIF_TERM sockRef) +{ + ERL_NIF_TERM reason; + + ESOCK_CNT_INC(env, descP, sockRef, + esock_atom_write_fails, &descP->writeFails, 1); + + reason = ENO2T(env, saveErrno); + + SSDBG( descP, + ("WIN-ESAIO", + "send_check_fail(%T, %d) -> error: " + "\r\n %d (%T)\r\n", + sockRef, descP->sock, saveErrno, reason) ); + + return esock_make_error(env, reason); +} + + + +/* ==================================================================== * + * * + * Utility functions * + * * + * ==================================================================== * + */ + +/* *** get_send_ovl_result *** + * + * Used for all send 'overlapped' operations; send, sendto and sendmsg. + */ +static +BOOL get_send_ovl_result(SOCKET sock, + OVERLAPPED* ovl, + DWORD* written) +{ + DWORD flags = 0; + BOOL result = get_ovl_result(sock, ovl, written, &flags); + + (void) flags; + + return result; +} + + +/* *** get_recv_ovl_result *** + * + * Used for recv *and* recvfrom 'overlapped' operations. + */ +static +BOOL get_recv_ovl_result(SOCKET sock, + OVERLAPPED* ovl, + DWORD* read, + DWORD* flags) +{ + return get_ovl_result(sock, ovl, read, flags); +} + + +/* *** get_recvmsg_ovl_result *** + * + * Used for all recvmsg 'overlapped' operations. + */ +static +BOOL get_recvmsg_ovl_result(SOCKET sock, + OVERLAPPED* ovl, + DWORD* read) +{ + DWORD flags = 0; + BOOL result = get_ovl_result(sock, ovl, read, &flags); + + (void) flags; + + return result; +} + + +/* *** get_ovl_result *** + * + * Simple wrapper function for WSAGetOverlappedResult. + */ +static +BOOL get_ovl_result(SOCKET sock, + OVERLAPPED* ovl, + DWORD* transfer, + DWORD* flags) +{ + return WSAGetOverlappedResult(sock, ovl, transfer, FALSE, flags); +} + + + +/* *** esaio_add_socket *** + * + * Add socket to I/O completion port. + */ +static +int esaio_add_socket(ESockDescriptor* descP) +{ + int res; + HANDLE tmp = CreateIoCompletionPort((HANDLE) descP->sock, ctrl.cport, + (ULONG_PTR) descP, 0); + + if (tmp != NULL) { + res = ESAIO_OK; + } else { + res = sock_errno(); + } + + return res; +} + + +static +void esaio_send_completion_msg(ErlNifEnv* sendEnv, + ESockDescriptor* descP, + ErlNifPid* pid, + ErlNifEnv* msgEnv, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM completionInfo) +{ + ERL_NIF_TERM msg = mk_completion_msg(msgEnv, sockRef, completionInfo); + + /* This can only fail if: + * - The recipient is dead. + * Our monitor should clear this up ... eventually. + * Possible race? + * - We (the sender) are "dead" (which we are clearly not) + */ + if (! esock_send_msg(sendEnv, pid, msg, NULL)) { + + /* + ESOCK_DBG_PRINTF( TRUE, ("IN-ESAIO", + "esaio_send_completion_msg(%T) {%d} failed ->" + "\r\n pid: %T" + "\r\n", + sockRef, descP->sock, MKPID(sendEnv, pid)) ); + */ + + SSDBG( descP, + ("WIN-ESAIO", + "esaio_send_completion_msg(%T) {%d} failed ->" + "\r\n pid: %T" + "\r\n", + sockRef, descP->sock, MKPID(sendEnv, pid)) ); + } +} + + +/* *** mk_completion_msg *** + * + * Construct a completion (socket) message. It has the form: + * + * {'$socket', Socket, completion, CompletionInfo} + * + */ + +static +ERL_NIF_TERM mk_completion_msg(ErlNifEnv* env, + ERL_NIF_TERM sockRef, + ERL_NIF_TERM info) +{ + return esock_mk_socket_msg(env, sockRef, + esock_atom_completion, info); +} diff --git a/erts/preloaded/ebin/prim_socket.beam b/erts/preloaded/ebin/prim_socket.beam Binary files differindex b58b2f4a22..ba50f42a01 100644 --- a/erts/preloaded/ebin/prim_socket.beam +++ b/erts/preloaded/ebin/prim_socket.beam diff --git a/erts/preloaded/src/prim_socket.erl b/erts/preloaded/src/prim_socket.erl index 68c950331a..fd4432cbc9 100644 --- a/erts/preloaded/src/prim_socket.erl +++ b/erts/preloaded/src/prim_socket.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2018-2022. All Rights Reserved. +%% Copyright Ericsson AB 2018-2023. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -512,6 +512,8 @@ send(SockRef, Bin, EFlags, SendRef) when is_integer(EFlags) -> {select, Written} -> <<_:Written/binary, RestBin/binary>> = Bin, {select, RestBin, EFlags}; + completion -> + completion; {error, _Reason} = Result -> Result end; @@ -621,6 +623,7 @@ sendmsg_result( sendmsg_result( SockRef, RestIOV, Cont, SendRef, true, nif_sendmsg(SockRef, EMsg, EFlags, SendRef, RestIOV)); + select -> if HasWritten -> @@ -631,6 +634,11 @@ sendmsg_result( {select, Written} -> RestIOV = rest_iov(Written, IOV), {select, RestIOV, Cont}; + + %% Either the message was written or not. No half ways... + completion = C -> + C; + {error, Reason} = Error-> if HasWritten -> |