From 493bc9358b59c52c63296e28975e9ef63b5ae779 Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Sat, 13 Feb 2016 23:37:46 +0000 Subject: [PATCH] Properly handle IPX socket descriptors used in select() writefds. If any IPX sockets are passed to select() in the writefds set, substitute them for the private UDP socket before calling the real select(), if it remains in writefds upon return, remove it and restore the previously removed IPX sockets. In short: Allow detecting when an IPX socket is ready to send. --- src/ipxwrapper.def | 1 + src/ipxwrapper.h | 1 + src/ipxwrapper_stubs.txt | 2 +- src/winsock.c | 79 ++++++++++++++++++++++++++++++++++++++-- src/wsock32_stubs.txt | 2 +- 5 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/ipxwrapper.def b/src/ipxwrapper.def index c3f10ab..934c3d8 100644 --- a/src/ipxwrapper.def +++ b/src/ipxwrapper.def @@ -21,3 +21,4 @@ EXPORTS listen accept WSAAsyncSelect + select diff --git a/src/ipxwrapper.h b/src/ipxwrapper.h index a63c2a7..4ba3a7f 100644 --- a/src/ipxwrapper.h +++ b/src/ipxwrapper.h @@ -188,5 +188,6 @@ int PASCAL r_getpeername(SOCKET fd, struct sockaddr *addr, int *addrlen); int PASCAL r_listen(SOCKET s, int backlog); SOCKET PASCAL r_accept(SOCKET s, struct sockaddr *addr, int *addrlen); int PASCAL r_WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent); +int PASCAL r_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout); #endif /* !IPXWRAPPER_H */ diff --git a/src/ipxwrapper_stubs.txt b/src/ipxwrapper_stubs.txt index d2b308f..b465783 100644 --- a/src/ipxwrapper_stubs.txt +++ b/src/ipxwrapper_stubs.txt @@ -7,7 +7,7 @@ htonl:4 ntohl:4 htons:4 ntohs:4 -select:4 +r_select:4 r_listen:4 r_accept:4 WSACreateEvent:4 diff --git a/src/winsock.c b/src/winsock.c index 6b3d9b2..ce1f172 100644 --- a/src/winsock.c +++ b/src/winsock.c @@ -1514,7 +1514,7 @@ int PASCAL ioctlsocket(SOCKET fd, long cmd, u_long *argp) FD_ZERO(&fdset); FD_SET(sock->fd, &fdset); - int r = select(1, &fdset, NULL, NULL, &tv); + int r = r_select(1, &fdset, NULL, NULL, &tv); if(r == -1) { @@ -1779,7 +1779,7 @@ static int _connect_spx(ipx_socket *sock, struct sockaddr_ipx *ipxaddr) .tv_usec = ((wait_until - now) % 1000) * 1000 }; - if(select(1, &fdset, NULL, NULL, &tv) == -1) + if(r_select(1, &fdset, NULL, NULL, &tv) == -1) { closesocket(lookup_fd); free(packet); @@ -1892,7 +1892,7 @@ static int _connect_spx(ipx_socket *sock, struct sockaddr_ipx *ipxaddr) FD_ZERO(&e_fdset); FD_SET(sock->fd, &e_fdset); - if(select(1, NULL, &w_fdset, &e_fdset, NULL) == 1 && FD_ISSET(sock->fd, &w_fdset)) + if(r_select(1, NULL, &w_fdset, &e_fdset, NULL) == 1 && FD_ISSET(sock->fd, &w_fdset)) { goto CONNECTED; } @@ -2374,3 +2374,76 @@ int PASCAL WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent) return r_WSAAsyncSelect(s, hWnd, wMsg, lEvent); } + +int PASCAL select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timeval *timeout) +{ + if(ipx_use_pcap) + { + /* Can't do anything when using pcap since there isn't a socket + * to wait on for sending. + */ + return r_select(nfds, readfds, writefds, exceptfds, timeout); + } + + bool writefds_bodge = false; + + fd_set writefds_pulled; + FD_ZERO(&writefds_pulled); + + if(writefds != NULL) + { + /* Search for any IPX sockets being referenced in writefds... */ + lock_sockets(); + + ipx_socket *sock, *tmp; + HASH_ITER(hh, sockets, sock, tmp) + { + if(!(sock->flags & IPX_IS_SPX) && FD_ISSET(sock->fd, writefds)) + { + /* Found one! Remove it and stash it for later. */ + writefds_bodge = true; + FD_SET(sock->fd, &writefds_pulled); + FD_CLR(sock->fd, writefds); + } + } + + unlock_sockets(); + } + + if(writefds_bodge) + { + /* At least one IPX socket was removed from writefds, put the + * private UDP socket there instead since that is what actually + * matters for sending. + */ + FD_SET(private_socket, writefds); + } + + int sret = r_select(nfds, readfds, writefds, exceptfds, timeout); + + if(sret > 0 && writefds_bodge && FD_ISSET(private_socket, writefds)) + { + /* We munged writefds and private_socket is still there, take it + * out and re-add any of the IPX sockets we removed earlier. + */ + + FD_CLR(private_socket, writefds); + --sret; + + lock_sockets(); + + ipx_socket *sock, *tmp; + HASH_ITER(hh, sockets, sock, tmp) + { + if(FD_ISSET(sock->fd, &writefds_pulled)) + { + FD_SET(sock->fd, writefds); + ++sret; + } + } + + unlock_sockets(); + } + + return sret; +} diff --git a/src/wsock32_stubs.txt b/src/wsock32_stubs.txt index 75450c4..6131143 100644 --- a/src/wsock32_stubs.txt +++ b/src/wsock32_stubs.txt @@ -15,7 +15,7 @@ ntohl ntohs recv:0 recvfrom:0 -select +select:0 send:0 sendto:0 setsockopt:0