diff --git a/Makefile b/Makefile index 2c3f0fb..47801a4 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,7 @@ dist: all zip -r ipxwrapper-$(VERSION)-src.zip ipxwrapper-$(VERSION)-src/ rm -r ipxwrapper-$(VERSION)-src/ -test: $(TEST_DLLS) tests/addr.exe tests/socket.exe tests/bind.exe tests/ipx-sendrecv.exe +test: $(TEST_DLLS) tests/addr.exe tests/socket.exe tests/bind.exe tests/ipx-sendrecv.exe tests/spx-connect.exe cp $(TEST_DLLS) tests/ ./tests/addr.exe @@ -72,11 +72,14 @@ test: $(TEST_DLLS) tests/addr.exe tests/socket.exe tests/bind.exe tests/ipx-send cd tests/; prove bind.t ./tests/ipx-sendrecv.exe + + ./tests/spx-connect.exe tests/addr.exe: tests/addr.c src/addr.o tests/bind.exe: tests/bind.c tests/ipx-sendrecv.exe: tests/ipx-sendrecv.c tests/test.h tests/socket.exe: tests/socket.c +tests/spx-connect.exe: tests/spx-connect.c tests/test.h tests/%.exe: $(CC) $(CFLAGS) -I./src/ -o $@ $^ -lwsock32 diff --git a/manifest.src.txt b/manifest.src.txt index 101fefe..10a787b 100644 --- a/manifest.src.txt +++ b/manifest.src.txt @@ -60,4 +60,5 @@ tests/bind.c tests/bind.t tests/ipx-sendrecv.c tests/socket.c +tests/spx-connect.c tests/test.h diff --git a/tests/spx-connect.c b/tests/spx-connect.c new file mode 100644 index 0000000..85cdc8d --- /dev/null +++ b/tests/spx-connect.c @@ -0,0 +1,156 @@ +/* IPXWrapper - Unit tests + * Copyright (C) 2014 Daniel Collins + * + * 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 +#include +#include +#include +#include + +#include "test.h" + +static struct sockaddr_ipx get_iface_addr(int ifnum) +{ + int sock = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX); + assert(sock != -1); + + IPX_ADDRESS_DATA iface_info; + iface_info.adapternum = ifnum; + + int optlen = sizeof(iface_info); + assert(getsockopt(sock, NSPROTO_IPX, IPX_ADDRESS, (char*)(&iface_info), &optlen) == 0); + + closesocket(sock); + + struct sockaddr_ipx addr; + + addr.sa_family = AF_IPX; + memcpy(addr.sa_netnum, iface_info.netnum, 4); + memcpy(addr.sa_nodenum, iface_info.nodenum, 6); + + return addr; +} + +int main() +{ + { + WSADATA wsaData; + assert(WSAStartup(MAKEWORD(1,1), &wsaData) == 0); + } + + struct sockaddr_ipx iface1_addr = get_iface_addr(0); + struct sockaddr_ipx iface2_addr = get_iface_addr(1); + + /* Bind lsock1 to socket 2000 on the first interface */ + + int lsock1 = socket(AF_IPX, SOCK_STREAM, NSPROTO_SPX); + assert(lsock1 != -1); + + { + unsigned long nonblock = 1; + assert(ioctlsocket(lsock1, FIONBIO, &nonblock) == 0); + } + + struct sockaddr_ipx ls1_addr = iface1_addr; + ls1_addr.sa_socket = htons(2000); + + assert(bind(lsock1, (struct sockaddr*)(&ls1_addr), sizeof(ls1_addr)) == 0); + + assert(listen(lsock1, 8) == 0); + + /* Bind lsock2 to a random socket on the second interface */ + + int lsock2 = socket(AF_IPX, SOCK_STREAM, NSPROTO_SPX); + assert(lsock2 != -1); + + { + unsigned long nonblock = 1; + assert(ioctlsocket(lsock2, FIONBIO, &nonblock) == 0); + } + + struct sockaddr_ipx ls2_addr = iface2_addr; + ls2_addr.sa_socket = 0; + + assert(bind(lsock2, (struct sockaddr*)(&ls2_addr), sizeof(ls2_addr)) == 0); + assert(listen(lsock2, 8) == 0); + + { + int addrlen = sizeof(ls2_addr); + assert(getsockname(lsock2, (struct sockaddr*)(&ls2_addr), &addrlen) == 0); + } + + /* Unbound client should implicitly be bound to a random address. */ + + { + EXPECT_NO_ACCEPT(lsock1); + + int sock = socket(AF_IPX, SOCK_STREAM, NSPROTO_SPX); + assert(sock != -1); + + struct sockaddr_ipx my_addr = iface1_addr; + my_addr.sa_socket = htons(84); + + assert(bind(sock, (struct sockaddr*)(&my_addr), sizeof(my_addr)) == 0); + EXPECT_LOCAL_ADDR(sock, my_addr); + + MUST_CONNECT_TO(sock, ls1_addr); + + /* Work around race condition; connect returns before listening + * socket has necessarily been notified. + */ + + Sleep(100); + + closesocket(EXPECT_ACCEPT(lsock1)); + + closesocket(sock); + } + + /* Explicitly bound client should retain address. */ + + { + EXPECT_NO_ACCEPT(lsock1); + + int sock = socket(AF_IPX, SOCK_STREAM, NSPROTO_SPX); + assert(sock != -1); + + struct sockaddr_ipx my_addr = iface1_addr; + my_addr.sa_socket = htons(84); + + assert(bind(sock, (struct sockaddr*)(&my_addr), sizeof(my_addr)) == 0); + EXPECT_LOCAL_ADDR(sock, my_addr); + + MUST_CONNECT_TO(sock, ls1_addr); + + /* Work around race condition; connect returns before listening + * socket has necessarily been notified. + */ + + Sleep(100); + + closesocket(EXPECT_ACCEPT_FROM(lsock1, my_addr)); + + closesocket(sock); + } + + closesocket(lsock2); + closesocket(lsock1); + + WSACleanup(); + + return 0; +} diff --git a/tests/test.h b/tests/test.h index e578510..2bd8303 100644 --- a/tests/test.h +++ b/tests/test.h @@ -87,6 +87,55 @@ } \ } +#define MUST_CONNECT_TO(sock, addr) \ +{ \ + if(connect(sock, (struct sockaddr*)(&addr), sizeof(addr)) != 0) \ + { \ + FAIL("Couldn't connect socket, WSAGetLastError = %d", (int)(WSAGetLastError())); \ + } \ + struct sockaddr_ipx got_addr; \ + int addrlen = sizeof(got_addr); \ + if(getpeername(sock, (struct sockaddr*)(&got_addr), &addrlen) != 0) \ + { \ + FAIL("Couldn't get peer address of connected socket, WSAGetLastError = %d", (int)(WSAGetLastError())); \ + } \ + if(memcmp(&addr, &got_addr, sizeof(got_addr)) != 0) \ + { \ + FAIL("Connected socket, but getpeername returns wrong address"); \ + } \ +} + +#define EXPECT_LOCAL_ADDR(sock, expect_addr) \ +{ \ + struct sockaddr_ipx got_addr; \ + int addrlen = sizeof(got_addr); \ + assert(getsockname(sock, (struct sockaddr*)(&got_addr), &addrlen) == 0); \ + assert(memcmp(&got_addr, &expect_addr, sizeof(got_addr)) == 0); \ +} + +#define EXPECT_NO_ACCEPT(sock) \ +{ \ + assert(accept(sock, NULL, NULL) == -1); \ + assert(WSAGetLastError() == WSAEWOULDBLOCK); \ +} + +#define EXPECT_ACCEPT(sock) \ +({ \ + int newfd = accept(sock, NULL, NULL); \ + assert(newfd != -1); \ + newfd; \ +}) + +#define EXPECT_ACCEPT_FROM(sock, expect_addr) \ +({ \ + struct sockaddr_ipx got_addr; \ + int addrlen = sizeof(got_addr); \ + int newfd = accept(sock, (struct sockaddr*)(&got_addr), &addrlen); \ + assert(newfd != -1); \ + assert(memcmp(&expect_addr, &got_addr, sizeof(got_addr)) == 0); \ + newfd; \ +}) + static const char test_data_1[] = { 0x57, 0x65, 0x27, 0x72, 0x65, 0x20, 0x4b, 0x6e, 0x69, 0x67, 0x68, 0x74, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x6f, 0x75,