diff --git a/Makefile b/Makefile index a375d3a..1314dfa 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ ipxconfig.exe: src/ipxconfig.cpp $(CXX) $(CXXFLAGS) -static-libgcc -static-libstdc++ -Wl,-s -D_WIN32_IE=0x0400 -mwindows -o ipxconfig.exe src/ipxconfig.cpp -liphlpapi dpwsockx.dll: src/directplay.o src/log.o src/dpwsockx_stubs.o src/common.o ipxwrapper.dll - $(CC) $(CFLAGS) -Wl,--enable-stdcall-fixup,-s -shared -o dpwsockx.dll src/directplay.o src/log.o src/common.o src/dpwsockx_stubs.o src/dpwsockx.def -L. -lipxwrapper -lwsock32 + $(CC) $(CFLAGS) -Wl,--enable-stdcall-fixup,-s -shared -o dpwsockx.dll src/directplay.o src/log.o src/common.o src/dpwsockx_stubs.o src/dpwsockx.def -L. -lipxwrapper -lwsock32 -liphlpapi src/ipxwrapper_stubs.s: src/ipxwrapper_stubs.txt perl mkstubs.pl src/ipxwrapper_stubs.txt src/ipxwrapper_stubs.s @@ -68,7 +68,7 @@ src/dpwsockx_stubs.s: src/dpwsockx_stubs.txt perl mkstubs.pl src/dpwsockx_stubs.txt src/dpwsockx_stubs.s dpwsockx.dll %.dll: src/stubdll.o src/%_stubs.o src/log.o src/common.o src/%.def - $(CC) $(CFLAGS) -Wl,--enable-stdcall-fixup,-s -shared -o $@ $^ + $(CC) $(CFLAGS) -Wl,--enable-stdcall-fixup,-s -shared -o $@ $^ -liphlpapi src/%_stubs.o: src/%_stubs.s nasm -f win32 -o $@ $< diff --git a/src/common.c b/src/common.c index c211f30..b4da98f 100644 --- a/src/common.c +++ b/src/common.c @@ -16,8 +16,10 @@ */ #include +#include #include "common.h" +#include "config.h" HKEY regkey = NULL; @@ -104,3 +106,117 @@ HMODULE load_sysdll(const char *name) { return dll; } + +/* Get virtual IPX interfaces + * Select a single interface by setting ifnum >= 0 +*/ +struct ipx_interface *get_interfaces(int ifnum) { + IP_ADAPTER_INFO *ifroot, tbuf; + ULONG bufsize = sizeof(IP_ADAPTER_INFO); + + int err = GetAdaptersInfo(&tbuf, &bufsize); + if(err == ERROR_NO_DATA) { + log_printf("No network interfaces detected!"); + return NULL; + }else if(err != ERROR_SUCCESS && err != ERROR_BUFFER_OVERFLOW) { + log_printf("Error fetching network interfaces: %s", w32_error(err)); + return NULL; + } + + if(!(ifroot = malloc(bufsize))) { + log_printf("Out of memory! (Tried to allocate %u bytes)", (unsigned int)bufsize); + return NULL; + } + + err = GetAdaptersInfo(ifroot, &bufsize); + if(err != ERROR_SUCCESS) { + log_printf("Error fetching network interfaces: %s", w32_error(err)); + + free(ifroot); + return NULL; + } + + struct ipx_interface *nics = NULL, *enic = NULL; + + IP_ADAPTER_INFO *ifptr = ifroot; + int this_ifnum = 0; + + while(ifptr) { + if(ifnum >= 0 && this_ifnum++ != ifnum) { + ifptr = ifptr->Next; + continue; + } + + struct reg_value rv; + int got_rv = 0; + + char vname[18]; + NODE_TO_STRING(vname, ifptr->Address); + + if(reg_get_bin(vname, &rv, sizeof(rv)) == sizeof(rv)) { + got_rv = 1; + } + + if(got_rv && !rv.enabled) { + /* Interface has been disabled, don't add it */ + ifptr = ifptr->Next; + continue; + } + + struct ipx_interface *nnic = malloc(sizeof(struct ipx_interface)); + if(!nnic) { + log_printf("Out of memory! (Tried to allocate %u bytes)", (unsigned int)sizeof(struct ipx_interface)); + + free_interfaces(nics); + return NULL; + } + + nnic->ipaddr = inet_addr(ifptr->IpAddressList.IpAddress.String); + nnic->netmask = inet_addr(ifptr->IpAddressList.IpMask.String); + nnic->bcast = nnic->ipaddr | ~nnic->netmask; + + memcpy(nnic->hwaddr, ifptr->Address, 6); + + if(got_rv) { + memcpy(nnic->ipx_net, rv.ipx_net, 4); + memcpy(nnic->ipx_node, rv.ipx_node, 6); + }else{ + unsigned char net[] = {0,0,0,1}; + + memcpy(nnic->ipx_net, net, 4); + memcpy(nnic->ipx_node, nnic->hwaddr, 6); + } + + nnic->next = NULL; + + if(got_rv && rv.primary) { + /* Force primary flag set, insert at start of NIC list */ + nnic->next = nics; + nics = nnic; + + if(!enic) { + enic = nnic; + } + }else if(enic) { + enic->next = nnic; + enic = nnic; + }else{ + enic = nics = nnic; + } + + ifptr = ifptr->Next; + } + + free(ifroot); + + return nics; +} + +void free_interfaces(struct ipx_interface *iface) { + while(iface) { + struct ipx_interface *del = iface; + iface = iface->next; + + free(del); + } +} diff --git a/src/common.h b/src/common.h index 7d404c8..ad88ce3 100644 --- a/src/common.h +++ b/src/common.h @@ -19,6 +19,41 @@ #define IPXWRAPPER_COMMON_H #include +#include +#include + +#define NET_TO_STRING(s, net) \ + sprintf( \ + s, "%02X:%02X:%02X:%02X", \ + (unsigned int)(unsigned char)(net[0]), \ + (unsigned int)(unsigned char)(net[1]), \ + (unsigned int)(unsigned char)(net[2]), \ + (unsigned int)(unsigned char)(net[3]) \ + ) + +#define NODE_TO_STRING(s, node) \ + sprintf( \ + s, "%02X:%02X:%02X:%02X:%02X:%02X", \ + (unsigned int)(unsigned char)(node[0]), \ + (unsigned int)(unsigned char)(node[1]), \ + (unsigned int)(unsigned char)(node[2]), \ + (unsigned int)(unsigned char)(node[3]), \ + (unsigned int)(unsigned char)(node[4]), \ + (unsigned int)(unsigned char)(node[5]) \ + ) + +struct ipx_interface { + uint32_t ipaddr; + uint32_t netmask; + uint32_t bcast; + + unsigned char hwaddr[6]; + + unsigned char ipx_net[4]; + unsigned char ipx_node[6]; + + struct ipx_interface *next; +}; extern HKEY regkey; @@ -34,4 +69,7 @@ DWORD reg_get_bin(const char *val_name, void *buf, DWORD size); HMODULE load_sysdll(const char *name); +struct ipx_interface *get_interfaces(int ifnum); +void free_interfaces(struct ipx_interface *iface); + #endif /* !IPXWRAPPER_COMMON_H */ diff --git a/src/ipxwrapper.h b/src/ipxwrapper.h index cc5fc68..48e4338 100644 --- a/src/ipxwrapper.h +++ b/src/ipxwrapper.h @@ -53,26 +53,6 @@ SetLastError(errnum);\ return __VA_ARGS__; -#define NET_TO_STRING(s, net) \ - sprintf( \ - s, "%02X:%02X:%02X:%02X", \ - (unsigned int)(unsigned char)(net[0]), \ - (unsigned int)(unsigned char)(net[1]), \ - (unsigned int)(unsigned char)(net[2]), \ - (unsigned int)(unsigned char)(net[3]) \ - ) - -#define NODE_TO_STRING(s, node) \ - sprintf( \ - s, "%02X:%02X:%02X:%02X:%02X:%02X", \ - (unsigned int)(unsigned char)(node[0]), \ - (unsigned int)(unsigned char)(node[1]), \ - (unsigned int)(unsigned char)(node[2]), \ - (unsigned int)(unsigned char)(node[3]), \ - (unsigned int)(unsigned char)(node[4]), \ - (unsigned int)(unsigned char)(node[5]) \ - ) - typedef struct ipx_socket ipx_socket; typedef struct ipx_packet ipx_packet; typedef struct ipx_nic ipx_nic; diff --git a/src/mswsock_stubs.txt b/src/mswsock_stubs.txt index 3f59c93..c8cff38 100644 --- a/src/mswsock_stubs.txt +++ b/src/mswsock_stubs.txt @@ -30,3 +30,4 @@ rexec rresvport s_perror sethostname +inet_addr diff --git a/src/router.c b/src/router.c new file mode 100644 index 0000000..0e33126 --- /dev/null +++ b/src/router.c @@ -0,0 +1,218 @@ +/* IPXWrapper - Router code + * Copyright (C) 2011 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 "router.h" +#include "common.h" + +/* Allocate router_vars structure and initialise all members + * Returns NULL on failure +*/ +struct router_vars *router_init(BOOL global) { + struct router_vars *router = malloc(sizeof(struct router_vars)); + if(!router) { + log_printf("Not enough memory to create router_vars!"); + return NULL; + } + + router->running = TRUE; + router->udp_sock = -1; + router->listner = -1; + router->wsa_event = WSA_INVALID_EVENT; + router->crit_sec_init = FALSE; + router->addrs = NULL; + + if(InitializeCriticalSectionAndSpinCount(&(router->crit_sec), 0x80000000)) { + router->crit_sec_init = TRUE; + }else{ + log_printf("Error creating critical section: %s", w32_error(GetLastError())); + + router_destroy(router); + return NULL; + } + + if((router->wsa_event = WSACreateEvent()) == WSA_INVALID_EVENT) { + log_printf("Error creating WSA event object: %s", w32_error(WSAGetLastError())); + + router_destroy(router); + return NULL; + } + + if((router->udp_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + log_printf("Error creating UDP socket: %s", w32_error(WSAGetLastError())); + + router_destroy(router); + return NULL; + } + + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + addr.sin_port = 9999; + + if(bind(router->udp_sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + router_destroy(router); + return NULL; + } + + if(global) { + /* TODO: Global (service) router support */ + } + + return router; +} + +/* Release all resources allocated by a router and free it */ +void router_destroy(struct router_vars *router) { + if(router->udp_sock != -1) { + closesocket(router->udp_sock); + } + + if(router->wsa_event != WSA_INVALID_EVENT) { + WSACloseEvent(router->wsa_event); + } + + if(router->crit_sec_init) { + DeleteCriticalSection(&(router->crit_sec)); + } + + free(router); +} + +DWORD router_main(void *arg) { + struct router_vars *router = arg; + + while(1) { + WaitForSingleObject(router->wsa_event, INFINITE); + + EnterCriticalSection(&(router->crit_sec)); + + if(!router->running) { + LeaveCriticalSection(&(router->crit_sec)); + return 0; + } + + /* TODO: Step through sockets, deal with timeouts/etc */ + + LeaveCriticalSection(&(router->crit_sec)); + } + + return 0; +} + +int router_bind(struct router_vars *router, SOCKET control, SOCKET sock, struct sockaddr_ipx *addr) { + struct ipx_interface *ifaces = get_interfaces(-1), *iface; + unsigned char z6[] = {0,0,0,0,0,0}; + + for(iface = ifaces; iface; iface = iface->next) { + if( + (memcmp(addr->sa_netnum, iface->ipx_net, 4) == 0 || memcmp(addr->sa_netnum, z6, 4) == 0) && + (memcmp(addr->sa_nodenum, iface->ipx_node, 6) == 0 || memcmp(addr->sa_nodenum, z6, 6) == 0) + ) { + break; + } + } + + if(!iface) { + log_printf("bind failed: no such address"); + + free_interfaces(ifaces); + + WSASetLastError(WSAEADDRNOTAVAIL); + return -1; + } + + memcpy(addr->sa_netnum, iface->ipx_net, 4); + memcpy(addr->sa_nodenum, iface->ipx_node, 6); + + free_interfaces(ifaces); + + EnterCriticalSection(&(router->crit_sec)); + + if(addr->sa_socket == 0) { + /* Automatic socket allocations start at 1024, I have no idea if + * this is normal IPX behaviour, but IP does it and it doesn't seem + * to interfere with any IPX software I've tested. + */ + + uint16_t s = 1024; + struct router_addr *a = router->addrs; + + while(a) { + if(ntohs(a->addr.sa_socket) == s) { + if(s == 65535) { + log_printf("bind failed: out of sockets?!"); + + LeaveCriticalSection(&(router->crit_sec)); + + WSASetLastError(WSAEADDRNOTAVAIL); + return -1; + } + + s++; + a = router->addrs; + + continue; + } + + a = a->next; + } + + addr->sa_socket = htons(s); + }else if(addr->sa_family != AF_IPX_SHARE) { + /* Test if any bound socket is using the requested socket number. */ + + struct router_addr *a = router->addrs; + + while(a) { + if(a->addr.sa_socket == addr->sa_socket) { + log_printf("bind failed: requested socket in use"); + + LeaveCriticalSection(&(router->crit_sec)); + + WSASetLastError(WSAEADDRINUSE); + return -1; + } + + a = a->next; + } + } + + struct router_addr *new_addr = malloc(sizeof(struct router_addr)); + if(!new_addr) { + LeaveCriticalSection(&(router->crit_sec)); + + WSASetLastError(ERROR_OUTOFMEMORY); + return -1; + } + + memcpy(&(new_addr->addr), addr, sizeof(struct sockaddr_ipx)); + + new_addr->local_port = 0; + new_addr->ws_socket = sock; + new_addr->control_socket = control; + new_addr->next = NULL; + + router->addrs = new_addr; + + LeaveCriticalSection(&(router->crit_sec)); + + return 0; +} diff --git a/src/router.h b/src/router.h new file mode 100644 index 0000000..ba480eb --- /dev/null +++ b/src/router.h @@ -0,0 +1,58 @@ +/* IPXWrapper - Router header + * Copyright (C) 2011 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. +*/ + +#ifndef IPXWRAPPER_ROUTER_H +#define IPXWRAPPER_ROUTER_H + +#include +#include +#include +#include + +/* Special address family for use when binding AF_IPX sockets, allows multiple + * sockets to share the same address. +*/ +#define AF_IPX_SHARE 42 + +struct router_addr { + struct sockaddr_ipx addr; + + uint16_t local_port; /* Local UDP port */ + SOCKET ws_socket; /* Application socket */ + SOCKET control_socket; /* Control socket */ + + struct router_addr *next; +}; + +struct router_vars { + BOOL running; + + SOCKET udp_sock; + SOCKET listner; + + WSAEVENT wsa_event; + + CRITICAL_SECTION crit_sec; + BOOL crit_sec_init; + + struct router_addr *addrs; +}; + +struct router_vars *router_init(BOOL global); +void router_destroy(struct router_vars *router); + +#endif /* !IPXWRAPPER_ROUTER_H */