mirror of
https://github.com/solemnwarning/ipxwrapper
synced 2024-12-30 16:45:37 +01:00
Merge branch 'fionread'
This commit is contained in:
commit
2f76f9177d
5
Makefile
5
Makefile
@ -46,7 +46,7 @@ BIN_FILES := $(shell cat manifest.bin.txt)
|
|||||||
SRC_FILES := $(shell cat manifest.src.txt)
|
SRC_FILES := $(shell cat manifest.src.txt)
|
||||||
|
|
||||||
# Tests to compile before running the test suite.
|
# Tests to compile before running the test suite.
|
||||||
TESTS := tests/addr.exe tests/addrcache.exe tests/ethernet.exe
|
TESTS := tests/addr.exe tests/addrcache.exe tests/ethernet.exe tools/fionread.exe
|
||||||
|
|
||||||
# Tools to compile before running the test suite.
|
# Tools to compile before running the test suite.
|
||||||
TOOLS := tools/socket.exe tools/list-interfaces.exe tools/bind.exe tools/ipx-send.exe \
|
TOOLS := tools/socket.exe tools/list-interfaces.exe tools/bind.exe tools/ipx-send.exe \
|
||||||
@ -178,6 +178,9 @@ tests/%.o: tests/%.c
|
|||||||
$(CC) $(CFLAGS) $(DEPFLAGS) -I./ -c -o $@ $<
|
$(CC) $(CFLAGS) $(DEPFLAGS) -I./ -c -o $@ $<
|
||||||
$(DEPPOST)
|
$(DEPPOST)
|
||||||
|
|
||||||
|
tools/fionread.exe: tests/fionread.o tests/tap/basic.o src/addr.o
|
||||||
|
$(CC) $(CFLAGS) -o $@ $^ -lwsock32
|
||||||
|
|
||||||
tools/%.exe: tools/%.o src/addr.o
|
tools/%.exe: tools/%.o src/addr.o
|
||||||
$(CC) $(CFLAGS) -o $@ $^ -lwsock32 -lole32 -lrpcrt4
|
$(CC) $(CFLAGS) -o $@ $^ -lwsock32 -lole32 -lrpcrt4
|
||||||
|
|
||||||
|
@ -1,3 +1,10 @@
|
|||||||
|
Version TBA:
|
||||||
|
Added support for DOSBox IPX servers.
|
||||||
|
|
||||||
|
Fixed implementation of FIONREAD ioctl (needed for Laser Arena).
|
||||||
|
|
||||||
|
Fixed a memory leak.
|
||||||
|
|
||||||
Version 0.6.1:
|
Version 0.6.1:
|
||||||
Added support for LLC and Novell "raw" 802.3 Ethernet frame formats.
|
Added support for LLC and Novell "raw" 802.3 Ethernet frame formats.
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ src/dpwsockx_stubs.txt
|
|||||||
src/firewall.c
|
src/firewall.c
|
||||||
src/interface.c
|
src/interface.c
|
||||||
src/interface.h
|
src/interface.h
|
||||||
|
src/interface2.c
|
||||||
|
src/interface2.h
|
||||||
src/ipxwrapper.c
|
src/ipxwrapper.c
|
||||||
src/ipxwrapper.def
|
src/ipxwrapper.def
|
||||||
src/ipxwrapper.h
|
src/ipxwrapper.h
|
||||||
@ -63,6 +65,8 @@ tests/07-ethernet.t
|
|||||||
tests/10-socket.t
|
tests/10-socket.t
|
||||||
tests/15-interfaces.t
|
tests/15-interfaces.t
|
||||||
tests/20-bind.t
|
tests/20-bind.t
|
||||||
|
tests/25-fionread.t
|
||||||
|
tests/30-dosbox-ipx.t
|
||||||
tests/30-eth-ipx.t
|
tests/30-eth-ipx.t
|
||||||
tests/30-ip-ipx.t
|
tests/30-ip-ipx.t
|
||||||
tests/40-ip-spx.t
|
tests/40-ip-spx.t
|
||||||
@ -71,12 +75,15 @@ tests/addr.c
|
|||||||
tests/addrcache.c
|
tests/addrcache.c
|
||||||
tests/config.pm
|
tests/config.pm
|
||||||
tests/ethernet.c
|
tests/ethernet.c
|
||||||
|
tests/fionread.c
|
||||||
tests/ptype.pm
|
tests/ptype.pm
|
||||||
|
|
||||||
tests/lib/IPXWrapper/Capture/IPX.pm
|
tests/lib/IPXWrapper/Capture/IPX.pm
|
||||||
tests/lib/IPXWrapper/Capture/IPXLLC.pm
|
tests/lib/IPXWrapper/Capture/IPXLLC.pm
|
||||||
tests/lib/IPXWrapper/Capture/IPXNovell.pm
|
tests/lib/IPXWrapper/Capture/IPXNovell.pm
|
||||||
tests/lib/IPXWrapper/Capture/IPXOverUDP.pm
|
tests/lib/IPXWrapper/Capture/IPXOverUDP.pm
|
||||||
|
tests/lib/IPXWrapper/DOSBoxClient.pm
|
||||||
|
tests/lib/IPXWrapper/DOSBoxServer.pm
|
||||||
tests/lib/IPXWrapper/SPX.pm
|
tests/lib/IPXWrapper/SPX.pm
|
||||||
tests/lib/IPXWrapper/Tool/Bind.pm
|
tests/lib/IPXWrapper/Tool/Bind.pm
|
||||||
tests/lib/IPXWrapper/Tool/DPTool.pm
|
tests/lib/IPXWrapper/Tool/DPTool.pm
|
||||||
@ -101,3 +108,20 @@ tools/socket.c
|
|||||||
tools/spx-client.c
|
tools/spx-client.c
|
||||||
tools/spx-server.c
|
tools/spx-server.c
|
||||||
tools/tools.h
|
tools/tools.h
|
||||||
|
|
||||||
|
winpcap/include/pcap-bpf.h
|
||||||
|
winpcap/include/Packet32.h
|
||||||
|
winpcap/include/pcap/vlan.h
|
||||||
|
winpcap/include/pcap/sll.h
|
||||||
|
winpcap/include/pcap/bluetooth.h
|
||||||
|
winpcap/include/pcap/bpf.h
|
||||||
|
winpcap/include/pcap/usb.h
|
||||||
|
winpcap/include/pcap/namedb.h
|
||||||
|
winpcap/include/pcap/pcap.h
|
||||||
|
winpcap/include/pcap-namedb.h
|
||||||
|
winpcap/include/pcap-stdinc.h
|
||||||
|
winpcap/include/Win32-Extensions.h
|
||||||
|
winpcap/include/bittypes.h
|
||||||
|
winpcap/include/ip6_misc.h
|
||||||
|
winpcap/include/remote-ext.h
|
||||||
|
winpcap/include/pcap.h
|
||||||
|
@ -62,6 +62,7 @@ The following have been reported to work:
|
|||||||
* Delta Force 2
|
* Delta Force 2
|
||||||
* Diablo
|
* Diablo
|
||||||
* Heroes of Might and Magic III
|
* Heroes of Might and Magic III
|
||||||
|
* Laser Arena
|
||||||
* Need For Speed III - Hot Pursuit
|
* Need For Speed III - Hot Pursuit
|
||||||
* Outlive
|
* Outlive
|
||||||
* Rising Lands
|
* Rising Lands
|
||||||
|
@ -21,3 +21,4 @@ EXPORTS
|
|||||||
listen
|
listen
|
||||||
accept
|
accept
|
||||||
WSAAsyncSelect
|
WSAAsyncSelect
|
||||||
|
select
|
||||||
|
@ -60,6 +60,55 @@
|
|||||||
typedef struct ipx_socket ipx_socket;
|
typedef struct ipx_socket ipx_socket;
|
||||||
typedef struct ipx_packet ipx_packet;
|
typedef struct ipx_packet ipx_packet;
|
||||||
|
|
||||||
|
#define RECV_QUEUE_MAX_PACKETS 32
|
||||||
|
|
||||||
|
#define IPX_RECV_QUEUE_FREE -1
|
||||||
|
#define IPX_RECV_QUEUE_LOCKED -2
|
||||||
|
|
||||||
|
/* Any AF_IPX IPX socket has an associated recv_queue.
|
||||||
|
*
|
||||||
|
* When a recv_pump() operation is running, the sockets lock has to be released
|
||||||
|
* in case the recv() blocks, which means the socket could be closed before it
|
||||||
|
* regains the lock.
|
||||||
|
*
|
||||||
|
* An ipx_recv_queue isn't destroyed until the refcount reaches zero. The
|
||||||
|
* ipx_socket holds one reference and each in-progress recv_pump() also holds a
|
||||||
|
* reference while the sockets lock isn't held.
|
||||||
|
*
|
||||||
|
* Access to the refcount is protected by refcount_lock.
|
||||||
|
*
|
||||||
|
* data[] holds an array of buffers for each queued packet, the status and size
|
||||||
|
* of which is indicated by the sizes[] array.
|
||||||
|
*
|
||||||
|
* If sizes[x] is IPX_RECV_QUEUE_FREE, the buffer is available to be claimed by
|
||||||
|
* a recv_pump() operation, which then sets it to IPX_RECV_QUEUE_LOCKED until
|
||||||
|
* it completes, which prevents a recv_pump() in another thread from trying to
|
||||||
|
* use the same receive buffer. Once a packet is read in, sizes[x] is set to
|
||||||
|
* the size of the packet and x is added to the end of the ready[] array.
|
||||||
|
*
|
||||||
|
* When a read is requested, the packet will be read from the data[] index
|
||||||
|
* stored in ready[0], and unless MSG_PEEK was used, that slot will then be
|
||||||
|
* released (sizes[x] set to IPX_RECV_QUEUE_FREE) and any subsequent slots in
|
||||||
|
* read[] will be advanced for the next read to pick up from ready[0].
|
||||||
|
*
|
||||||
|
* Access to the ready, n_ready and sizes members is only permitted when a
|
||||||
|
* thread holds the main sockets lock.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ipx_recv_queue
|
||||||
|
{
|
||||||
|
CRITICAL_SECTION refcount_lock;
|
||||||
|
int refcount;
|
||||||
|
|
||||||
|
int ready[RECV_QUEUE_MAX_PACKETS];
|
||||||
|
int n_ready;
|
||||||
|
|
||||||
|
unsigned char data[RECV_QUEUE_MAX_PACKETS][MAX_PKT_SIZE];
|
||||||
|
int sizes[RECV_QUEUE_MAX_PACKETS];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ipx_recv_queue ipx_recv_queue;
|
||||||
|
|
||||||
struct ipx_socket {
|
struct ipx_socket {
|
||||||
SOCKET fd;
|
SOCKET fd;
|
||||||
|
|
||||||
@ -79,6 +128,8 @@ struct ipx_socket {
|
|||||||
/* Address used with connect call, only set when IPX_CONNECTED is */
|
/* Address used with connect call, only set when IPX_CONNECTED is */
|
||||||
struct sockaddr_ipx remote_addr;
|
struct sockaddr_ipx remote_addr;
|
||||||
|
|
||||||
|
struct ipx_recv_queue *recv_queue;
|
||||||
|
|
||||||
UT_hash_handle hh;
|
UT_hash_handle hh;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -165,5 +216,6 @@ int PASCAL r_getpeername(SOCKET fd, struct sockaddr *addr, int *addrlen);
|
|||||||
int PASCAL r_listen(SOCKET s, int backlog);
|
int PASCAL r_listen(SOCKET s, int backlog);
|
||||||
SOCKET PASCAL r_accept(SOCKET s, struct sockaddr *addr, int *addrlen);
|
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_WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);
|
||||||
|
int WSAAPI r_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const PTIMEVAL timeout);
|
||||||
|
|
||||||
#endif /* !IPXWRAPPER_H */
|
#endif /* !IPXWRAPPER_H */
|
||||||
|
@ -7,7 +7,7 @@ htonl:4
|
|||||||
ntohl:4
|
ntohl:4
|
||||||
htons:4
|
htons:4
|
||||||
ntohs:4
|
ntohs:4
|
||||||
select:4
|
r_select:4
|
||||||
r_listen:4
|
r_listen:4
|
||||||
r_accept:4
|
r_accept:4
|
||||||
WSACreateEvent:4
|
WSACreateEvent:4
|
||||||
|
340
src/winsock.c
340
src/winsock.c
@ -303,6 +303,26 @@ INT WINAPI WSHEnumProtocols(LPINT protocols, LPWSTR ign, LPVOID buf, LPDWORD bsp
|
|||||||
return do_EnumProtocols(protocols, buf, bsptr, false);
|
return do_EnumProtocols(protocols, buf, bsptr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int recv_queue_adjust_refcount(ipx_recv_queue *recv_queue, int adj)
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&(recv_queue->refcount_lock));
|
||||||
|
int new_refcount = (recv_queue->refcount += adj);
|
||||||
|
LeaveCriticalSection(&(recv_queue->refcount_lock));
|
||||||
|
|
||||||
|
return new_refcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_recv_queue(ipx_recv_queue *recv_queue)
|
||||||
|
{
|
||||||
|
int new_refcount = recv_queue_adjust_refcount(recv_queue, -1);
|
||||||
|
|
||||||
|
if(new_refcount == 0)
|
||||||
|
{
|
||||||
|
DeleteCriticalSection(&(recv_queue->refcount_lock));
|
||||||
|
free(recv_queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SOCKET WSAAPI socket(int af, int type, int protocol)
|
SOCKET WSAAPI socket(int af, int type, int protocol)
|
||||||
{
|
{
|
||||||
log_printf(LOG_DEBUG, "socket(%d, %d, %d)", af, type, protocol);
|
log_printf(LOG_DEBUG, "socket(%d, %d, %d)", af, type, protocol);
|
||||||
@ -318,10 +338,38 @@ SOCKET WSAAPI socket(int af, int type, int protocol)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ipx_recv_queue *recv_queue = malloc(sizeof(ipx_recv_queue));
|
||||||
|
if(recv_queue == NULL)
|
||||||
|
{
|
||||||
|
free(nsock);
|
||||||
|
|
||||||
|
WSASetLastError(ERROR_OUTOFMEMORY);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!InitializeCriticalSectionAndSpinCount(&(recv_queue->refcount_lock), 0x80000000))
|
||||||
|
{
|
||||||
|
log_printf(LOG_ERROR, "Failed to initialise critical section: %s", w32_error(GetLastError()));
|
||||||
|
WSASetLastError(GetLastError());
|
||||||
|
|
||||||
|
free(recv_queue);
|
||||||
|
free(nsock);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
recv_queue->refcount = 1;
|
||||||
|
recv_queue->n_ready = 0;
|
||||||
|
|
||||||
|
for(int i = 0; i < RECV_QUEUE_MAX_PACKETS; ++i)
|
||||||
|
{
|
||||||
|
recv_queue->sizes[i] = IPX_RECV_QUEUE_FREE;
|
||||||
|
}
|
||||||
|
|
||||||
if((nsock->fd = r_socket(AF_INET, SOCK_DGRAM, 0)) == -1)
|
if((nsock->fd = r_socket(AF_INET, SOCK_DGRAM, 0)) == -1)
|
||||||
{
|
{
|
||||||
log_printf(LOG_ERROR, "Cannot create UDP socket: %s", w32_error(WSAGetLastError()));
|
log_printf(LOG_ERROR, "Cannot create UDP socket: %s", w32_error(WSAGetLastError()));
|
||||||
|
|
||||||
|
release_recv_queue(recv_queue);
|
||||||
free(nsock);
|
free(nsock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -329,6 +377,8 @@ SOCKET WSAAPI socket(int af, int type, int protocol)
|
|||||||
nsock->flags = IPX_SEND | IPX_RECV | IPX_RECV_BCAST;
|
nsock->flags = IPX_SEND | IPX_RECV | IPX_RECV_BCAST;
|
||||||
nsock->s_ptype = (protocol ? protocol - NSPROTO_IPX : 0);
|
nsock->s_ptype = (protocol ? protocol - NSPROTO_IPX : 0);
|
||||||
|
|
||||||
|
nsock->recv_queue = recv_queue;
|
||||||
|
|
||||||
log_printf(LOG_INFO, "IPX socket created (fd = %d)", nsock->fd);
|
log_printf(LOG_INFO, "IPX socket created (fd = %d)", nsock->fd);
|
||||||
|
|
||||||
lock_sockets();
|
lock_sockets();
|
||||||
@ -384,6 +434,8 @@ SOCKET WSAAPI socket(int af, int type, int protocol)
|
|||||||
nsock->flags |= IPX_IS_SPXII;
|
nsock->flags |= IPX_IS_SPXII;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsock->recv_queue = NULL;
|
||||||
|
|
||||||
log_printf(LOG_INFO, "SPX socket created (fd = %d)", nsock->fd);
|
log_printf(LOG_INFO, "SPX socket created (fd = %d)", nsock->fd);
|
||||||
|
|
||||||
lock_sockets();
|
lock_sockets();
|
||||||
@ -425,6 +477,11 @@ int WSAAPI closesocket(SOCKET sockfd)
|
|||||||
|
|
||||||
log_printf(LOG_INFO, "Socket %d (%s) closed", sockfd, (sock->flags & IPX_IS_SPX ? "SPX" : "IPX"));
|
log_printf(LOG_INFO, "Socket %d (%s) closed", sockfd, (sock->flags & IPX_IS_SPX ? "SPX" : "IPX"));
|
||||||
|
|
||||||
|
if(sock->recv_queue != NULL)
|
||||||
|
{
|
||||||
|
release_recv_queue(sock->recv_queue);
|
||||||
|
}
|
||||||
|
|
||||||
if(sock->flags & IPX_BOUND)
|
if(sock->flags & IPX_BOUND)
|
||||||
{
|
{
|
||||||
CloseHandle(sock->sock_mut);
|
CloseHandle(sock->sock_mut);
|
||||||
@ -687,6 +744,121 @@ int WSAAPI getsockname(SOCKET fd, struct sockaddr *addr, int *addrlen)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOL reclaim_socket(ipx_socket *sockptr, int lookup_fd)
|
||||||
|
{
|
||||||
|
/* Reclaim the lock, ensure the socket hasn't been
|
||||||
|
* closed by the application (naughty!) while we were
|
||||||
|
* waiting.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ipx_socket *reclaim_sock = get_socket(lookup_fd);
|
||||||
|
if(sockptr != reclaim_sock)
|
||||||
|
{
|
||||||
|
log_printf(LOG_DEBUG, "Application closed socket while inside a WinSock call!");
|
||||||
|
|
||||||
|
if(reclaim_sock)
|
||||||
|
{
|
||||||
|
unlock_sockets();
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int recv_pump(ipx_socket *sockptr, BOOL block)
|
||||||
|
{
|
||||||
|
int fd = sockptr->fd;
|
||||||
|
|
||||||
|
if(!block)
|
||||||
|
{
|
||||||
|
fd_set read_fds;
|
||||||
|
FD_ZERO(&read_fds);
|
||||||
|
|
||||||
|
FD_SET(fd, &read_fds);
|
||||||
|
|
||||||
|
struct timeval timeout = { 0, 0 };
|
||||||
|
|
||||||
|
int r = r_select(-1, &read_fds, NULL, NULL, &timeout);
|
||||||
|
if(r == -1)
|
||||||
|
{
|
||||||
|
unlock_sockets();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
else if(r == 0)
|
||||||
|
{
|
||||||
|
/* No packet waiting in underlying recv buffer. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ipx_recv_queue *queue = sockptr->recv_queue;
|
||||||
|
|
||||||
|
int recv_slot = -1;
|
||||||
|
|
||||||
|
for(int i = 0; i < RECV_QUEUE_MAX_PACKETS; ++i)
|
||||||
|
{
|
||||||
|
if(queue->sizes[i] == IPX_RECV_QUEUE_FREE)
|
||||||
|
{
|
||||||
|
queue->sizes[i] = IPX_RECV_QUEUE_LOCKED;
|
||||||
|
recv_queue_adjust_refcount(queue, 1);
|
||||||
|
|
||||||
|
recv_slot = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(recv_slot < 0)
|
||||||
|
{
|
||||||
|
/* No free recv_queue slots. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_sockets();
|
||||||
|
|
||||||
|
int r = r_recv(fd, (char*)(queue->data[recv_slot]), MAX_PKT_SIZE, 0);
|
||||||
|
|
||||||
|
if(!reclaim_socket(sockptr, fd))
|
||||||
|
{
|
||||||
|
/* The application closed the socket while we were in the recv() call.
|
||||||
|
* Just discard our handle, let the queue be destroyed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
release_recv_queue(queue);
|
||||||
|
WSASetLastError(WSAENOTSOCK);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(r == -1)
|
||||||
|
{
|
||||||
|
queue->sizes[recv_slot] = IPX_RECV_QUEUE_FREE;
|
||||||
|
release_recv_queue(queue);
|
||||||
|
unlock_sockets();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ipx_packet *packet = (struct ipx_packet*)(queue->data[recv_slot]);
|
||||||
|
|
||||||
|
if(r < sizeof(ipx_packet) - 1 || r != packet->size + sizeof(ipx_packet) - 1)
|
||||||
|
{
|
||||||
|
log_printf(LOG_ERROR, "Invalid packet received on loopback port!");
|
||||||
|
|
||||||
|
queue->sizes[recv_slot] = IPX_RECV_QUEUE_FREE;
|
||||||
|
release_recv_queue(queue);
|
||||||
|
|
||||||
|
WSASetLastError(WSAEWOULDBLOCK);
|
||||||
|
unlock_sockets();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue->sizes[recv_slot] = r;
|
||||||
|
queue->ready[queue->n_ready] = recv_slot;
|
||||||
|
++(queue->n_ready);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Recieve a packet from an IPX socket
|
/* Recieve a packet from an IPX socket
|
||||||
* addr must be NULL or a region of memory big enough for a sockaddr_ipx
|
* addr must be NULL or a region of memory big enough for a sockaddr_ipx
|
||||||
*
|
*
|
||||||
@ -694,40 +866,31 @@ int WSAAPI getsockname(SOCKET fd, struct sockaddr *addr, int *addrlen)
|
|||||||
* The size of the packet will be returned on success, even if it was truncated
|
* The size of the packet will be returned on success, even if it was truncated
|
||||||
*/
|
*/
|
||||||
static int recv_packet(ipx_socket *sockptr, char *buf, int bufsize, int flags, struct sockaddr_ipx_ext *addr, int addrlen) {
|
static int recv_packet(ipx_socket *sockptr, char *buf, int bufsize, int flags, struct sockaddr_ipx_ext *addr, int addrlen) {
|
||||||
SOCKET fd = sockptr->fd;
|
if(!(sockptr->flags & IPX_BOUND))
|
||||||
int is_bound = sockptr->flags & IPX_BOUND;
|
{
|
||||||
int extended_addr = sockptr->flags & IPX_EXT_ADDR;
|
unlock_sockets();
|
||||||
|
|
||||||
unlock_sockets();
|
|
||||||
|
|
||||||
if(!is_bound) {
|
|
||||||
WSASetLastError(WSAEINVAL);
|
WSASetLastError(WSAEINVAL);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *recvbuf = malloc(MAX_PKT_SIZE);
|
/* Loop here in case some crazy application does concurrent recv() calls
|
||||||
if(!recvbuf) {
|
* and they race between putting packets on the queue and handling them.
|
||||||
WSASetLastError(ERROR_OUTOFMEMORY);
|
*/
|
||||||
return -1;
|
while(sockptr->recv_queue->n_ready < 1)
|
||||||
}
|
|
||||||
|
|
||||||
struct ipx_packet *packet = (struct ipx_packet*)(recvbuf);
|
|
||||||
|
|
||||||
int rval = r_recv(fd, recvbuf, MAX_PKT_SIZE, flags);
|
|
||||||
if(rval == -1) {
|
|
||||||
free(recvbuf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rval < sizeof(ipx_packet) - 1 || rval != packet->size + sizeof(ipx_packet) - 1)
|
|
||||||
{
|
{
|
||||||
log_printf(LOG_ERROR, "Invalid packet received on loopback port!");
|
if(recv_pump(sockptr, TRUE) < 0)
|
||||||
|
{
|
||||||
free(recvbuf);
|
/* Socket closed or recv() error. */
|
||||||
WSASetLastError(WSAEWOULDBLOCK);
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int slot = sockptr->recv_queue->ready[0];
|
||||||
|
|
||||||
|
struct ipx_packet *packet = (struct ipx_packet*)(sockptr->recv_queue->data[slot]);
|
||||||
|
assert(sockptr->recv_queue->sizes[slot] >= 0);
|
||||||
|
|
||||||
if(min_log_level <= LOG_DEBUG)
|
if(min_log_level <= LOG_DEBUG)
|
||||||
{
|
{
|
||||||
IPX_STRING_ADDR(addr_s, addr32_in(packet->src_net), addr48_in(packet->src_node), packet->src_socket);
|
IPX_STRING_ADDR(addr_s, addr32_in(packet->src_net), addr48_in(packet->src_node), packet->src_socket);
|
||||||
@ -741,7 +904,7 @@ static int recv_packet(ipx_socket *sockptr, char *buf, int bufsize, int flags, s
|
|||||||
memcpy(addr->sa_nodenum, packet->src_node, 6);
|
memcpy(addr->sa_nodenum, packet->src_node, 6);
|
||||||
addr->sa_socket = packet->src_socket;
|
addr->sa_socket = packet->src_socket;
|
||||||
|
|
||||||
if(extended_addr) {
|
if(sockptr->flags & IPX_EXT_ADDR) {
|
||||||
if(addrlen >= sizeof(struct sockaddr_ipx_ext)) {
|
if(addrlen >= sizeof(struct sockaddr_ipx_ext)) {
|
||||||
addr->sa_ptype = packet->ptype;
|
addr->sa_ptype = packet->ptype;
|
||||||
addr->sa_flags = 0;
|
addr->sa_flags = 0;
|
||||||
@ -774,8 +937,17 @@ static int recv_packet(ipx_socket *sockptr, char *buf, int bufsize, int flags, s
|
|||||||
}
|
}
|
||||||
|
|
||||||
memcpy(buf, packet->data, packet->size <= bufsize ? packet->size : bufsize);
|
memcpy(buf, packet->data, packet->size <= bufsize ? packet->size : bufsize);
|
||||||
rval = packet->size;
|
int rval = packet->size;
|
||||||
free(recvbuf);
|
|
||||||
|
if((flags & MSG_PEEK) == 0)
|
||||||
|
{
|
||||||
|
sockptr->recv_queue->sizes[slot] = IPX_RECV_QUEUE_FREE;
|
||||||
|
|
||||||
|
--(sockptr->recv_queue->n_ready);
|
||||||
|
memmove(&(sockptr->recv_queue->ready[0]), &(sockptr->recv_queue->ready[1]), (sockptr->recv_queue->n_ready * sizeof(int)));
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_sockets();
|
||||||
|
|
||||||
return rval;
|
return rval;
|
||||||
}
|
}
|
||||||
@ -1625,39 +1797,33 @@ int PASCAL ioctlsocket(SOCKET fd, long cmd, u_long *argp)
|
|||||||
|
|
||||||
if(cmd == FIONREAD && !(sock->flags & IPX_IS_SPX))
|
if(cmd == FIONREAD && !(sock->flags & IPX_IS_SPX))
|
||||||
{
|
{
|
||||||
/* Test to see if data is waiting. */
|
while(1)
|
||||||
|
|
||||||
fd_set fdset;
|
|
||||||
struct timeval tv = {0,0};
|
|
||||||
|
|
||||||
FD_ZERO(&fdset);
|
|
||||||
FD_SET(sock->fd, &fdset);
|
|
||||||
|
|
||||||
int r = select(1, &fdset, NULL, NULL, &tv);
|
|
||||||
|
|
||||||
if(r == -1)
|
|
||||||
{
|
{
|
||||||
unlock_sockets();
|
int r = recv_pump(sock, FALSE);
|
||||||
return -1;
|
if(r < 0)
|
||||||
}
|
{
|
||||||
else if(r == 0)
|
/* Error in recv_pump() */
|
||||||
{
|
return -1;
|
||||||
*(unsigned long*)(argp) = 0;
|
}
|
||||||
|
|
||||||
unlock_sockets();
|
if(r == 0)
|
||||||
return -1;
|
{
|
||||||
|
/* No more packets ready to read from underlying socket. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the size of the packet. */
|
unsigned long accumulated_packet_data = 0;
|
||||||
|
|
||||||
char tmp_buf;
|
for(int i = 0; i < sock->recv_queue->n_ready; ++i)
|
||||||
|
|
||||||
if((r = recv_packet(sock, &tmp_buf, 1, MSG_PEEK, NULL, 0)) == -1)
|
|
||||||
{
|
{
|
||||||
return -1;
|
const ipx_packet *packet = (const ipx_packet*)(sock->recv_queue->data[ sock->recv_queue->ready[i] ]);
|
||||||
|
accumulated_packet_data += packet->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(unsigned long*)(argp) = r;
|
unlock_sockets();
|
||||||
|
|
||||||
|
*(unsigned long*)(argp) = accumulated_packet_data;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1898,7 +2064,7 @@ static int _connect_spx(ipx_socket *sock, struct sockaddr_ipx *ipxaddr)
|
|||||||
.tv_usec = ((wait_until - now) % 1000) * 1000
|
.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);
|
closesocket(lookup_fd);
|
||||||
free(packet);
|
free(packet);
|
||||||
@ -2011,7 +2177,7 @@ static int _connect_spx(ipx_socket *sock, struct sockaddr_ipx *ipxaddr)
|
|||||||
FD_ZERO(&e_fdset);
|
FD_ZERO(&e_fdset);
|
||||||
FD_SET(sock->fd, &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;
|
goto CONNECTED;
|
||||||
}
|
}
|
||||||
@ -2493,3 +2659,63 @@ int PASCAL WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent)
|
|||||||
|
|
||||||
return r_WSAAsyncSelect(s, hWnd, wMsg, lEvent);
|
return r_WSAAsyncSelect(s, hWnd, wMsg, lEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int WSAAPI select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const PTIMEVAL timeout)
|
||||||
|
{
|
||||||
|
const struct timeval TIMEOUT_IMMEDIATE = { 0, 0 };
|
||||||
|
const struct timeval *use_timeout = timeout;
|
||||||
|
|
||||||
|
fd_set force_read_fds;
|
||||||
|
FD_ZERO(&force_read_fds);
|
||||||
|
|
||||||
|
if(readfds != NULL)
|
||||||
|
{
|
||||||
|
for(unsigned int i = 0; i < readfds->fd_count; ++i)
|
||||||
|
{
|
||||||
|
int fd = readfds->fd_array[i];
|
||||||
|
|
||||||
|
ipx_socket *sockptr = get_socket(fd);
|
||||||
|
if(sockptr != NULL)
|
||||||
|
{
|
||||||
|
if(sockptr->flags & IPX_IS_SPX)
|
||||||
|
{
|
||||||
|
unlock_sockets();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sockptr->recv_queue->n_ready > 0)
|
||||||
|
{
|
||||||
|
/* There is data in the receive queue for this socket, but
|
||||||
|
* the underlying socket isn't necessarily readable, so we
|
||||||
|
* reduce the select() timeout to zero to ensure it returns
|
||||||
|
* immediately and inject this fd back into readfds at the
|
||||||
|
* end if necessary.
|
||||||
|
*/
|
||||||
|
|
||||||
|
FD_SET(fd, &force_read_fds);
|
||||||
|
use_timeout = &TIMEOUT_IMMEDIATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_sockets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int r = r_select(nfds, readfds, writefds, exceptfds, (const PTIMEVAL)(use_timeout));
|
||||||
|
|
||||||
|
if(r >= 0)
|
||||||
|
{
|
||||||
|
for(unsigned int i = 0; i < force_read_fds.fd_count; ++i)
|
||||||
|
{
|
||||||
|
int fd = force_read_fds.fd_array[i];
|
||||||
|
|
||||||
|
if(!FD_ISSET(fd, readfds))
|
||||||
|
{
|
||||||
|
FD_SET(fd, readfds);
|
||||||
|
++r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
@ -15,7 +15,7 @@ ntohl
|
|||||||
ntohs
|
ntohs
|
||||||
recv:0
|
recv:0
|
||||||
recvfrom:0
|
recvfrom:0
|
||||||
select
|
select:0
|
||||||
send:0
|
send:0
|
||||||
sendto:0
|
sendto:0
|
||||||
setsockopt:0
|
setsockopt:0
|
||||||
|
34
tests/25-fionread.t
Normal file
34
tests/25-fionread.t
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# IPXWrapper test suite
|
||||||
|
# Copyright (C) 2023 Daniel Collins <solemnwarning@solemnwarning.net>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License version 2 as published by
|
||||||
|
# the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
# more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# this program; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use FindBin;
|
||||||
|
use lib "$FindBin::Bin/lib/";
|
||||||
|
|
||||||
|
use IPXWrapper::Util;
|
||||||
|
|
||||||
|
require "$FindBin::Bin/config.pm";
|
||||||
|
our ($remote_mac_a, $remote_ip_a);
|
||||||
|
|
||||||
|
reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper");
|
||||||
|
|
||||||
|
# Unit tests implemented by fionread.exe, so run it on the test system and pass
|
||||||
|
# the (TAP) output/exit status to our parent.
|
||||||
|
|
||||||
|
system("ssh", $remote_ip_a, "Z:\\tools\\fionread.exe", "00:00:00:01", $remote_mac_a);
|
||||||
|
exit($? >> 8);
|
134
tests/fionread.c
Normal file
134
tests/fionread.c
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/* IPXWrapper test suite
|
||||||
|
* Copyright (C) 2023 Daniel Collins <solemnwarning@solemnwarning.net>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License version 2 as published by
|
||||||
|
* the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||||
|
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <wsipx.h>
|
||||||
|
|
||||||
|
#include "tap/basic.h"
|
||||||
|
#include "../tools/tools.h"
|
||||||
|
|
||||||
|
static char buf[4096];
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
if(argc != 3)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: %s <netnum> <nodenum>\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
plan_lazy();
|
||||||
|
|
||||||
|
struct sockaddr_ipx addr1 = read_sockaddr(argv[1], argv[2], "1234");
|
||||||
|
struct sockaddr_ipx addr2 = read_sockaddr(argv[1], argv[2], "1235");
|
||||||
|
|
||||||
|
WSADATA data;
|
||||||
|
WSAStartup(MAKEWORD(1, 1), &data);
|
||||||
|
|
||||||
|
int sock1 = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
|
||||||
|
assert(sock1 != SOCKET_ERROR);
|
||||||
|
|
||||||
|
assert(bind(sock1, (struct sockaddr*)(&addr1), sizeof(addr1)) == 0);
|
||||||
|
|
||||||
|
int sock2 = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
|
||||||
|
assert(sock2 != SOCKET_ERROR);
|
||||||
|
|
||||||
|
assert(bind(sock2, (struct sockaddr*)(&addr2), sizeof(addr2)) == 0);
|
||||||
|
|
||||||
|
fd_set readfds;
|
||||||
|
struct timeval timeout = { 0, 0 };
|
||||||
|
|
||||||
|
unsigned long x = 123456789;
|
||||||
|
int r = ioctlsocket(sock1, FIONREAD, &x);
|
||||||
|
|
||||||
|
is_int(0, r, "ioctlsocket(FIONREAD) succeeds when no packets are waiting");
|
||||||
|
is_int(0, x, "ioctlsocket(FIONREAD) returns zero bytes when no packets are waiting");
|
||||||
|
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(sock1, &readfds);
|
||||||
|
is_int(select(-1, &readfds, NULL, NULL, &timeout), 0, "select() initially indicates socket is not ready to read");
|
||||||
|
ok(!FD_ISSET(sock1, &readfds), "select() initially indicates socket is not ready to read");
|
||||||
|
|
||||||
|
assert(sendto(sock2, buf, 128, 0, (struct sockaddr*)(&addr1), sizeof(addr1)) == 128);
|
||||||
|
|
||||||
|
/* Just in case there's any async going on */
|
||||||
|
Sleep(100);
|
||||||
|
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(sock1, &readfds);
|
||||||
|
is_int(select(-1, &readfds, NULL, NULL, &timeout), 1, "select() indicates socket is ready to read before call to ioctlsocket(FIONREAD)");
|
||||||
|
ok(FD_ISSET(sock1, &readfds), "select() indicates socket is ready to read before call to ioctlsocket(FIONREAD)");
|
||||||
|
|
||||||
|
x = 123456789;
|
||||||
|
r = ioctlsocket(sock1, FIONREAD, &x);
|
||||||
|
|
||||||
|
is_int(0, r, "ioctlsocket(FIONREAD) succeeds when packets are waiting");
|
||||||
|
is_int(128, x, "ioctlsocket(FIONREAD) returns payload size when one packet is waiting");
|
||||||
|
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(sock1, &readfds);
|
||||||
|
is_int(select(-1, &readfds, NULL, NULL, &timeout), 1, "select() indicates socket is ready to read after call to ioctlsocket(FIONREAD)");
|
||||||
|
ok(FD_ISSET(sock1, &readfds), "select() indicates socket is ready to read after call to ioctlsocket(FIONREAD)");
|
||||||
|
|
||||||
|
assert(sendto(sock2, buf, 256, 0, (struct sockaddr*)(&addr1), sizeof(addr1)) == 256);
|
||||||
|
|
||||||
|
/* Just in case there's any async going on */
|
||||||
|
Sleep(100);
|
||||||
|
|
||||||
|
x = 123456789;
|
||||||
|
r = ioctlsocket(sock1, FIONREAD, &x);
|
||||||
|
|
||||||
|
is_int(0, r, "ioctlsocket(FIONREAD) succeeds when packets are waiting");
|
||||||
|
is_int(384, x, "ioctlsocket(FIONREAD) returns combined payload sizes when multiple packets are waiting");
|
||||||
|
|
||||||
|
assert(recv(sock1, buf, 4096, 0) == 128);
|
||||||
|
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(sock1, &readfds);
|
||||||
|
is_int(select(-1, &readfds, NULL, NULL, &timeout), 1, "select() indicates socket is ready to read after reading first packet");
|
||||||
|
ok(FD_ISSET(sock1, &readfds), "select() indicates socket is ready to read after reading first packet");
|
||||||
|
|
||||||
|
x = 123456789;
|
||||||
|
r = ioctlsocket(sock1, FIONREAD, &x);
|
||||||
|
|
||||||
|
is_int(0, r, "ioctlsocket(FIONREAD) succeeds when packets are waiting");
|
||||||
|
is_int(256, x, "ioctlsocket(FIONREAD) returns payload sizes when one packet is waiting");
|
||||||
|
|
||||||
|
assert(recv(sock1, buf, 4096, 0) == 256);
|
||||||
|
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_SET(sock1, &readfds);
|
||||||
|
is_int(select(-1, &readfds, NULL, NULL, &timeout), 0, "select() indicates socket is not ready to read after reading second packet");
|
||||||
|
ok(!FD_ISSET(sock1, &readfds), "select() indicates socket is ready to read after reading second packet");
|
||||||
|
|
||||||
|
x = 123456789;
|
||||||
|
r = ioctlsocket(sock1, FIONREAD, &x);
|
||||||
|
|
||||||
|
is_int(0, r, "ioctlsocket(FIONREAD) succeeds when no packets are waiting");
|
||||||
|
is_int(0, x, "ioctlsocket(FIONREAD) returns zero bytes when no packets are waiting");
|
||||||
|
|
||||||
|
closesocket(sock2);
|
||||||
|
closesocket(sock1);
|
||||||
|
|
||||||
|
WSACleanup();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -8,7 +8,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "addr.h"
|
#include "../src/addr.h"
|
||||||
|
|
||||||
static struct sockaddr_ipx read_sockaddr(const char *net_s, const char *node_s, const char *socket_s)
|
static struct sockaddr_ipx read_sockaddr(const char *net_s, const char *node_s, const char *socket_s)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user