diff --git a/Makefile b/Makefile index 8c125a0..48772d5 100644 --- a/Makefile +++ b/Makefile @@ -33,7 +33,8 @@ export CXXFLAGS VERSION := DEVELOPMENT BUILD IPXWRAPPER_DEPS := src/ipxwrapper.o src/winsock.o src/ipxwrapper_stubs.o src/log.o src/common.o \ - src/interface.o src/router.o src/ipxwrapper.def src/addrcache.o src/config.o src/addr.o + src/interface.o src/router.o src/ipxwrapper.def src/addrcache.o src/config.o src/addr.o \ + src/addrtable.o BIN_FILES := changes.txt license.txt readme.txt ipxwrapper.dll mswsock.dll wsock32.dll ipxconfig.exe \ ipxrouter.exe dpwsockx.dll directplay-win32.reg directplay-win64.reg diff --git a/src/addrtable.c b/src/addrtable.c new file mode 100644 index 0000000..c6f7040 --- /dev/null +++ b/src/addrtable.c @@ -0,0 +1,416 @@ +/* IPXWrapper - Address table + * Copyright (C) 2008-2012 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. +*/ + +/* The address table is used to co-ordinate the IPX addresses in use accross all + * IPXWrapper instances. +*/ + +#include +#include + +#include "addrtable.h" +#include "ipxwrapper.h" + +#define ADDR_TABLE_SIZE (sizeof(addr_table_header_t) + (ADDR_TABLE_MAX_ENTRIES * sizeof(addr_table_entry_t))) + +static HANDLE addr_table_mutex = NULL; + +static HANDLE addr_table_h = NULL; + +static addr_table_header_t *addr_table_header = NULL; +static addr_table_entry_t *addr_table_base = NULL; + +static void _init_fail(void) +{ + if(addr_table_mutex) + { + ReleaseMutex(addr_table_mutex); + } + + log_printf(LOG_WARNING, "Multiple processes may have address conflicts!"); + addr_table_cleanup(); +} + +void addr_table_init(void) +{ + /* Mutex used to protect the address table. */ + if(!(addr_table_mutex = CreateMutex(NULL, FALSE, ADDR_TABLE_MUTEX))) + { + log_printf(LOG_ERROR, "Failed to create/open mutex: %s", w32_error(GetLastError())); + _init_fail(); + + return; + } + + addr_table_lock(); + + /* Allocate the address table "file" (shared memory). */ + + if(!(addr_table_h = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, ADDR_TABLE_SIZE, ADDR_TABLE_NAME))) + { + log_printf(LOG_ERROR, "Failed to create/open address table: %s", w32_error(GetLastError())); + _init_fail(); + + return; + } + + /* True if the table didn't exist before we created it. */ + bool new_table = (GetLastError() != ERROR_ALREADY_EXISTS); + + /* Map the address table. */ + if(!(addr_table_header = MapViewOfFile(addr_table_h, FILE_MAP_WRITE, 0, 0, ADDR_TABLE_SIZE))) + { + log_printf(LOG_ERROR, "Failed to map address table: %s", w32_error(GetLastError())); + _init_fail(); + + return; + } + + if(new_table) + { + /* Initialise the address table. */ + + memset(addr_table_header, 0, ADDR_TABLE_SIZE); + + addr_table_header->version = ADDR_TABLE_VERSION; + } + else if(addr_table_header->version != ADDR_TABLE_VERSION) + { + log_printf(LOG_ERROR, "Address table from incompatible IPXWrapper version present"); + _init_fail(); + + return; + } +} + +/* Release all handles to the address table and associated objects. */ +void addr_table_cleanup(void) +{ + if(addr_table_header) + { + UnmapViewOfFile(addr_table_header); + } + + addr_table_header = NULL; + addr_table_base = NULL; + + if(addr_table_h) + { + CloseHandle(addr_table_h); + addr_table_h = NULL; + } + + if(addr_table_mutex) + { + CloseHandle(addr_table_mutex); + addr_table_mutex = NULL; + } +} + +void addr_table_lock(void) +{ + if(addr_table_mutex) + { + WaitForSingleObject(addr_table_mutex, INFINITE); + } +} + +void addr_table_unlock(void) +{ + if(addr_table_mutex) + { + ReleaseMutex(addr_table_mutex); + } +} + +/* Search the address table for any conflicting binds. Falls back to searching + * the sockets table if the address table is unavailable. + * + * Returns false if a conflict was confirmed, true otherwise. +*/ +bool addr_table_check(const struct sockaddr_ipx *addr, bool reuse) +{ + if(addr_table_base) + { + addr_table_lock(); + + addr_table_entry_t *entry = addr_table_base; + addr_table_entry_t *end = addr_table_base + ADDR_TABLE_MAX_ENTRIES; + + while(entry < end && (entry->flags & ADDR_TABLE_ENTRY_VALID)) + { + if(addr->sa_socket == entry->socket && (!(entry->flags & ADDR_TABLE_ENTRY_REUSE) || !reuse)) + { + /* A socket is already bound to this address and either + * it or this one doesn't have SO_REUSEADDR set. + */ + + addr_table_unlock(); + return false; + } + + entry++; + } + + addr_table_unlock(); + } + else{ + /* Address table is unavailable, check the sockets table + * instead. This will not maintain address uniqueness between + * multiple processes! + */ + + lock_sockets(); + + ipx_socket *s = sockets; + + for(; s; s = s->next) + { + if(memcmp(&(s->addr), addr, sizeof(struct sockaddr_ipx)) == 0 && (!(s->flags & IPX_REUSE) || !reuse)) + { + unlock_sockets(); + return true; + } + } + + unlock_sockets(); + } + + return true; +} + +/* Return an unused socket number in network byte order for automatic allocation, + * zero if none are available. +*/ +uint16_t addr_table_auto_socket(void) +{ + /* 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 sock = 1024; + + if(addr_table_base) + { + addr_table_lock(); + + addr_table_entry_t *entry = addr_table_base; + addr_table_entry_t *end = addr_table_base + ADDR_TABLE_MAX_ENTRIES; + + while(entry < end) + { + if(ntohs(sock) == entry->socket) + { + if(sock == 65535) + { + addr_table_unlock(); + return 0; + } + + sock++; + + entry = addr_table_base; + continue; + } + + entry++; + } + + addr_table_unlock(); + } + else{ + lock_sockets(); + + ipx_socket *s = sockets; + + while(s) + { + if((s->flags & IPX_BOUND) && ntohs(sock) == s->addr.sa_socket) + { + if(sock == 65535) + { + unlock_sockets(); + return 0; + } + + sock++; + + s = sockets; + continue; + } + + s = s->next; + } + + unlock_sockets(); + } + + return htons(sock); +} + +/* Insert an entry into the address table. + * + * Performs no conflict checking. Take the address table lock in the caller and + * use addr_table_check() before calling this. +*/ +void addr_table_add(const struct sockaddr_ipx *addr, uint16_t port, bool reuse) +{ + if(!addr_table_base) + { + return; + } + + addr_table_lock(); + + /* Iterate over the address table to find the last entry. */ + + addr_table_entry_t *entry = addr_table_base; + addr_table_entry_t *end = addr_table_base + ADDR_TABLE_MAX_ENTRIES; + + while(entry < end && (entry->flags & ADDR_TABLE_ENTRY_VALID)) + { + entry++; + } + + /* Append the new address to the address table. */ + + if(entry < end) + { + entry->netnum = addr32_in(addr->sa_netnum); + entry->nodenum = addr48_in(addr->sa_nodenum); + entry->socket = addr->sa_socket; + + entry->flags = ADDR_TABLE_ENTRY_VALID; + + if(reuse) + { + entry->flags |= ADDR_TABLE_ENTRY_REUSE; + } + + entry->port = port; + entry->time = time(NULL); + } + else{ + log_printf(LOG_ERROR, "Out of address table slots, not appending!"); + } + + addr_table_unlock(); +} + +/* Remove an entry from the address table. */ +void addr_table_remove(uint16_t port) +{ + if(!addr_table_base) + { + return; + } + + addr_table_lock(); + + /* Iterate over the address table until we find the correct entry... */ + + addr_table_entry_t *entry = addr_table_base; + addr_table_entry_t *end = addr_table_base + ADDR_TABLE_MAX_ENTRIES; + + while(entry < end && (entry->flags & ADDR_TABLE_ENTRY_VALID) && entry->port != port) + { + entry++; + } + + /* Continue iteration until we find the last entry... */ + + addr_table_entry_t *last = entry; + + while(last < end && (last->flags & ADDR_TABLE_ENTRY_VALID)) + { + last++; + } + + /* Replace entry with the last entry and mark the latter as invalid. */ + + if(entry < end) + { + *entry = *last; + + last->flags &= ~ADDR_TABLE_ENTRY_VALID; + } + + addr_table_unlock(); +} + +/* Update the time field for any of our entries in the address table and remove + * any that have expired (most likely a crashed process). +*/ +void addr_table_update(void) +{ + if(!addr_table_base) + { + return; + } + + lock_sockets(); + + addr_table_lock(); + + ipx_socket *sock = sockets; + + /* Remove any expired entries. */ + + addr_table_entry_t *entry = addr_table_base; + addr_table_entry_t *last = addr_table_base; + addr_table_entry_t *end = addr_table_base + ADDR_TABLE_MAX_ENTRIES; + + while(last < end && (last->flags & ADDR_TABLE_ENTRY_VALID)) + { + last++; + } + + for(; entry < end && (entry->flags & ADDR_TABLE_ENTRY_VALID); entry++) + { + if(entry->time + ADDR_TABLE_ENTRY_TIMEOUT <= time(NULL)) + { + *end = *last; + + last->flags &= ~ADDR_TABLE_ENTRY_VALID; + last--; + } + } + + /* This is really, really efficient. */ + + for(; sock; sock = sock->next) + { + if(sock->flags & IPX_BOUND) + { + /* Search the address table... */ + + for(entry = addr_table_base; entry < end && (entry->flags & ADDR_TABLE_ENTRY_VALID); entry++) + { + if(entry->port == sock->port) + { + entry->time = time(NULL); + break; + } + } + } + } + + addr_table_unlock(); + + unlock_sockets(); +} diff --git a/src/addrtable.h b/src/addrtable.h new file mode 100644 index 0000000..a35be26 --- /dev/null +++ b/src/addrtable.h @@ -0,0 +1,72 @@ +/* IPXWrapper - Address table + * Copyright (C) 2008-2012 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 ADDRTABLE_H +#define ADDRTABLE_H + +#include +#include +#include + +#include "addr.h" + +#define ADDR_TABLE_MAX_ENTRIES 512 +#define ADDR_TABLE_ENTRY_TIMEOUT 10 + +#define ADDR_TABLE_MUTEX "IPXWrapper_addr_table_mutex" +#define ADDR_TABLE_NAME "IPXWrapper_addr_table" + +#define ADDR_TABLE_VERSION 1 + +typedef struct addr_table_header addr_table_header_t; + +struct addr_table_header +{ + int version; +}; + +#define ADDR_TABLE_ENTRY_VALID ((int)(1<<0)) +#define ADDR_TABLE_ENTRY_REUSE ((int)(1<<1)) + +typedef struct addr_table_entry addr_table_entry_t; + +struct addr_table_entry +{ + addr32_t netnum; + addr48_t nodenum; + uint16_t socket; + + int flags; + uint16_t port; + time_t time; +}; + +void addr_table_init(void); +void addr_table_cleanup(void); + +void addr_table_lock(void); +void addr_table_unlock(void); + +bool addr_table_check(const struct sockaddr_ipx *addr, bool reuse); +uint16_t addr_table_auto_socket(void); + +void addr_table_add(const struct sockaddr_ipx *addr, uint16_t port, bool reuse); +void addr_table_remove(uint16_t port); + +void addr_table_update(void); + +#endif /* !ADDRTABLE_H */ diff --git a/src/common.c b/src/common.c index d5de5f1..d29fbd3 100644 --- a/src/common.c +++ b/src/common.c @@ -68,7 +68,11 @@ HKEY reg_open_subkey(HKEY parent, const char *path, bool readwrite) if(err != ERROR_SUCCESS) { - log_printf(LOG_ERROR, "Could not open registry: %s", w32_error(err)); + if(err != ERROR_FILE_NOT_FOUND) + { + log_printf(LOG_ERROR, "Could not open registry: %s", w32_error(err)); + } + return NULL; } diff --git a/src/ipxwrapper.c b/src/ipxwrapper.c index f452f12..55c34f1 100644 --- a/src/ipxwrapper.c +++ b/src/ipxwrapper.c @@ -41,11 +41,8 @@ struct ipaddr_list { }; ipx_socket *sockets = NULL; -SOCKET send_fd = -1; main_config_t main_config; -struct rclient g_rclient; - static CRITICAL_SECTION sockets_cs; static void init_cs(CRITICAL_SECTION *cs) @@ -57,14 +54,17 @@ static void init_cs(CRITICAL_SECTION *cs) } } -BOOL WINAPI DllMain(HINSTANCE me, DWORD why, LPVOID res) { - if(why == DLL_PROCESS_ATTACH) { +BOOL WINAPI DllMain(HINSTANCE me, DWORD why, LPVOID res) +{ + if(why == DLL_PROCESS_ATTACH) + { log_open("ipxwrapper.log"); log_printf(LOG_INFO, "IPXWrapper %s", version_string); log_printf(LOG_INFO, "Compiled at %s", compile_time); - if(!getenv("SystemRoot")) { + if(!getenv("SystemRoot")) + { log_printf(LOG_WARNING, "SystemRoot is not set in the environment"); char env[268] = "SystemRoot="; @@ -81,51 +81,21 @@ BOOL WINAPI DllMain(HINSTANCE me, DWORD why, LPVOID res) { ipx_interfaces_init(); - if(!rclient_init(&g_rclient)) { - return FALSE; - } - init_cs(&sockets_cs); WSADATA wsdata; int err = WSAStartup(MAKEWORD(1,1), &wsdata); - if(err) { + if(err) + { log_printf(LOG_ERROR, "Failed to initialize winsock: %s", w32_error(err)); return FALSE; } - if(!rclient_start(&g_rclient)) { - return FALSE; - } - - if(g_rclient.router) { - send_fd = g_rclient.router->udp_sock; - }else{ - /* Create UDP socket for sending packets if not using a private router */ - - if((send_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { - log_printf(LOG_ERROR, "Failed to create UDP socket: %s", w32_error(WSAGetLastError())); - return FALSE; - } - - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = 0; - - if(bind(send_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { - log_printf(LOG_ERROR, "Failed to bind UDP socket (send_fd): %s", w32_error(WSAGetLastError())); - return FALSE; - } - } - }else if(why == DLL_PROCESS_DETACH) { - if(send_fd != -1 && !g_rclient.router) { - closesocket(send_fd); - } - - send_fd = -1; - - rclient_stop(&g_rclient); + router_init(); + } + else if(why == DLL_PROCESS_DETACH) + { + router_cleanup(); WSACleanup(); diff --git a/src/ipxwrapper.h b/src/ipxwrapper.h index 962c440..3412e3f 100644 --- a/src/ipxwrapper.h +++ b/src/ipxwrapper.h @@ -52,11 +52,15 @@ typedef struct ipx_socket ipx_socket; typedef struct ipx_packet ipx_packet; -typedef struct ipx_host ipx_host; struct ipx_socket { SOCKET fd; + /* Locally bound UDP port number (Network byte order). + * Undefined before IPX bind() call. + */ + uint16_t port; + int flags; uint8_t s_ptype; uint8_t f_ptype; /* Undefined when IPX_FILTER isn't set */ @@ -85,20 +89,8 @@ struct ipx_packet { char data[1]; } __attribute__((__packed__)); -struct ipx_host { - unsigned char ipx_net[4]; - unsigned char ipx_node[6]; - - uint32_t ipaddr; - time_t last_packet; - - ipx_host *next; -}; - extern ipx_socket *sockets; -extern SOCKET send_fd; extern main_config_t main_config; -extern struct rclient g_rclient; ipx_socket *get_socket(SOCKET fd); void lock_sockets(void); diff --git a/src/router.c b/src/router.c index c63fe60..f404d68 100644 --- a/src/router.c +++ b/src/router.c @@ -22,969 +22,309 @@ #include "common.h" #include "ipxwrapper.h" #include "interface.h" +#include "addrcache.h" -static struct router_addr *router_get(struct router_vars *router, SOCKET control, SOCKET sock); -static BOOL router_handle_call(struct router_vars *router, int sock, struct router_call *call); -static void router_drop_client(struct router_vars *router, int coff); +static bool router_running = false; +static WSAEVENT router_event = WSA_INVALID_EVENT; +static HANDLE router_thread = NULL; +static char *router_buf = NULL; -static BOOL rclient_do(struct rclient *rclient, struct router_call *call, struct router_ret *ret); - -/* Allocate router_vars structure and initialise all members - * Returns NULL on failure +/* The shared socket uses the UDP port number specified in the configuration, + * every IPXWrapper instance will share it and use it to receive broadcast + * packets. + * + * The private socket uses a randomly allocated UDP port number and is used to + * send packets and receive unicast, it is unique to a single IPXWrapper + * instance. */ -struct router_vars *router_init(BOOL global) { - struct router_vars *router = malloc(sizeof(struct router_vars)); - if(!router) { - log_printf(LOG_ERROR, "Not enough memory to create router_vars!"); - return NULL; - } + +SOCKET shared_socket = -1; +SOCKET private_socket = -1; + +static DWORD router_main(void *arg); + +/* Initialise a UDP socket. */ +static void _init_socket(SOCKET *sock, uint16_t port, BOOL reuseaddr) +{ + /* Socket used for sending and receiving packets on the network. */ - router->running = TRUE; - router->udp_sock = -1; - router->listener = -1; - router->client_count = 0; - router->wsa_event = WSA_INVALID_EVENT; - router->crit_sec_init = FALSE; - router->addrs = NULL; - router->recvbuf = NULL; - - if(InitializeCriticalSectionAndSpinCount(&(router->crit_sec), 0x80000000)) { - router->crit_sec_init = TRUE; - }else{ - log_printf(LOG_ERROR, "Error creating critical section: %s", w32_error(GetLastError())); - - router_destroy(router); - return NULL; - } - - if((router->wsa_event = WSACreateEvent()) == WSA_INVALID_EVENT) { - log_printf(LOG_ERROR, "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) { + if((*sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) + { log_printf(LOG_ERROR, "Error creating UDP socket: %s", w32_error(WSAGetLastError())); - - router_destroy(router); - return NULL; + abort(); } + /* Enable broadcast and address reuse. */ + + BOOL broadcast = TRUE; + + setsockopt(*sock, SOL_SOCKET, SO_BROADCAST, (char*)&broadcast, sizeof(BOOL)); + setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseaddr, sizeof(BOOL)); + + /* Set send/receive buffer size to 512KiB. */ + + int bufsize = 524288; + + setsockopt(*sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, sizeof(int)); + setsockopt(*sock, SOL_SOCKET, SO_SNDBUF, (char*)&bufsize, sizeof(int)); + struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = INADDR_ANY; - addr.sin_port = htons(main_config.udp_port); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(port); - if(bind(router->udp_sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) { + if(bind(*sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) + { log_printf(LOG_ERROR, "Error binding UDP socket: %s", w32_error(WSAGetLastError())); - - router_destroy(router); - return NULL; + abort(); } - BOOL broadcast = TRUE; - int bufsize = 524288; /* 512KiB */ - - setsockopt(router->udp_sock, SOL_SOCKET, SO_BROADCAST, (char*)&broadcast, sizeof(BOOL)); - setsockopt(router->udp_sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufsize, sizeof(int)); - setsockopt(router->udp_sock, SOL_SOCKET, SO_SNDBUF, (char*)&bufsize, sizeof(int)); - - if(WSAEventSelect(router->udp_sock, router->wsa_event, FD_READ) == -1) { + if(WSAEventSelect(*sock, router_event, FD_READ) == -1) + { log_printf(LOG_ERROR, "WSAEventSelect error: %s", w32_error(WSAGetLastError())); - - router_destroy(router); - return NULL; + abort(); } - - if(!(router->recvbuf = malloc(sizeof(struct rpacket_header) + MAX_PKT_SIZE))) { - log_printf(LOG_ERROR, "Out of memory! Cannot allocate recv buffer"); - - router_destroy(router); - return NULL; - } - - if(global) { - if((router->listener = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - log_printf(LOG_ERROR, "Failed to create TCP socket: %s", w32_error(WSAGetLastError())); - - router_destroy(router); - return NULL; - } - - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr.sin_port = htons(main_config.router_port); - - if(bind(router->listener, (struct sockaddr*)&addr, sizeof(addr)) == -1) { - log_printf(LOG_ERROR, "Failed to bind TCP socket: %s", w32_error(WSAGetLastError())); - - router_destroy(router); - return NULL; - } - - if(listen(router->listener, 8) == -1) { - log_printf(LOG_ERROR, "Failed to listen for connections: %s", w32_error(WSAGetLastError())); - - router_destroy(router); - return NULL; - } - - if(WSAEventSelect(router->listener, router->wsa_event, FD_ACCEPT) == -1) { - log_printf(LOG_ERROR, "WSAEventSelect error: %s", w32_error(WSAGetLastError())); - - router_destroy(router); - return NULL; - } - } - - return router; } -/* Release all resources allocated by a router and free it */ -void router_destroy(struct router_vars *router) { - struct router_addr *addr = router->addrs; - int i; - - while(addr) { - struct router_addr *del = addr; - addr = addr->next; - - free(del); +/* Initialise the UDP socket and router worker thread. + * Aborts on failure. +*/ +void router_init(void) +{ + if(!(router_buf = malloc(MAX_PKT_SIZE))) + { + log_printf(LOG_ERROR, "Out of memory! Cannot allocate router buffer"); + abort(); } - for(i = 0; i < router->client_count; i++) { - closesocket(router->clients[i].sock); + /* Event object used for notification of new packets and exit signal. */ + + if((router_event = WSACreateEvent()) == WSA_INVALID_EVENT) + { + log_printf(LOG_ERROR, "Error creating WSA event object: %s", w32_error(WSAGetLastError())); + abort(); } - if(router->listener != -1) { - closesocket(router->listener); + _init_socket(&shared_socket, main_config.udp_port, TRUE); + _init_socket(&private_socket, 0, FALSE); + + router_running = true; + + if(!(router_thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(&router_main), NULL, 0, NULL))) + { + log_printf(LOG_ERROR, "Cannot create router worker thread: %s", w32_error(GetLastError())); + abort(); } - - free(router->recvbuf); - - 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; +void router_cleanup(void) +{ + /* Signal the router thread to exit. */ - const unsigned char f6[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + router_running = false; + SetEvent(router_event); - while(1) { - WaitForSingleObject(router->wsa_event, INFINITE); + /* Wait for it to exit, kill if it takes too long. */ + + if(WaitForSingleObject(router_thread, 3000) == WAIT_TIMEOUT) + { + log_printf(LOG_WARNING, "Router thread didn't exit in 3 seconds, killing"); + TerminateThread(router_thread, 0); + } + + CloseHandle(router_thread); + router_thread = NULL; + + /* Release resources. */ + + if(private_socket != -1) + { + closesocket(private_socket); + private_socket = -1; + } + + if(shared_socket != -1) + { + closesocket(shared_socket); + shared_socket = -1; + } + + if(router_event != WSA_INVALID_EVENT) + { + WSACloseEvent(router_event); + router_event = WSA_INVALID_EVENT; + } + + free(router_buf); + router_buf = NULL; +} + +/* Recieve and process a packet from one of the UDP sockets. */ +static bool handle_recv(int fd) +{ + const unsigned char f6[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + struct sockaddr_in addr; + int addrlen = sizeof(addr); + + ipx_packet *packet = (ipx_packet*)(router_buf); + + int len = recvfrom(fd, (char*)packet, MAX_PKT_SIZE, 0, (struct sockaddr*)(&addr), &addrlen); + if(len == -1) + { + if(WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAECONNRESET) + { + return true; + } - EnterCriticalSection(&(router->crit_sec)); + return false; + } + + packet->size = ntohs(packet->size); + + if(len < sizeof(ipx_packet) - 1 || packet->size > MAX_DATA_SIZE || packet->size + sizeof(ipx_packet) - 1 != len) + { + /* Packet size incorrect. */ - WSAResetEvent(router->wsa_event); + return true; + } + + if(min_log_level <= LOG_DEBUG) + { + IPX_STRING_ADDR(src_addr, addr32_in(packet->src_net), addr48_in(packet->src_node), packet->src_socket); + IPX_STRING_ADDR(dest_addr, addr32_in(packet->dest_net), addr48_in(packet->dest_node), packet->dest_socket); - if(!router->running) { - LeaveCriticalSection(&(router->crit_sec)); + log_printf(LOG_DEBUG, "Recieved packet from %s (%s) for %s", src_addr, inet_ntoa(addr.sin_addr), dest_addr); + } + + addr_cache_set( + (struct sockaddr*)(&addr), sizeof(addr), + addr32_in(packet->src_net), addr48_in(packet->src_node), packet->src_socket + ); + + lock_sockets(); + + ipx_socket *sock = sockets; + + for(; sock; sock = sock->next) + { + if( + /* Socket is bound and not shutdown for recv. */ + + (sock->flags & IPX_BOUND) && (sock->flags & IPX_RECV) + + /* Packet type filtering is off, or packet type matches + * filter. + */ + + && (!(sock->flags & IPX_FILTER) || sock->f_ptype == packet->ptype) + + /* Packet destination address is correct. */ + + && (memcmp(packet->dest_net, sock->addr.sa_netnum, 4) == 0 || (memcmp(packet->dest_net, f6, 4) == 0 && (sock->flags & IPX_BROADCAST || !main_config.w95_bug) && sock->flags & IPX_RECV_BCAST)) + && (memcmp(packet->dest_node, sock->addr.sa_nodenum, 6) == 0 || (memcmp(packet->dest_node, f6, 6) == 0 && (sock->flags & IPX_BROADCAST || !main_config.w95_bug) && sock->flags & IPX_RECV_BCAST)) + && packet->dest_socket == sock->addr.sa_socket + + /* Socket has not been connected, or the IPX source + * address is correct. + */ + + && ( + !(sock->flags & IPX_CONNECTED) + + || ( + memcmp(sock->remote_addr.sa_netnum, packet->src_net, 4) == 0 + && memcmp(sock->remote_addr.sa_nodenum, packet->src_node, 6) == 0 + && sock->remote_addr.sa_socket == packet->src_socket + ) + ) + ) { + addr32_t iface_net = addr32_in(sock->addr.sa_netnum); + addr48_t iface_node = addr48_in(sock->addr.sa_nodenum); + + /* Fetch the interface this socket is bound to. */ + + ipx_interface_t *iface = ipx_interface_by_addr(iface_net, iface_node); + + if(!iface) + { + char net_s[ADDR32_STRING_SIZE], node_s[ADDR48_STRING_SIZE]; + + addr32_string(net_s, iface_net); + addr48_string(node_s, iface_node); + + log_printf(LOG_WARNING, "No iface for %s/%s! Stale bind?", net_s, node_s); + + continue; + } + + /* Iterate over the subnets and compare to the packet + * source IP address. + */ + + ipx_interface_ip_t *ip; + + int source_ok = 0; + + DL_FOREACH(iface->ipaddr, ip) + { + if((ip->ipaddr & ip->netmask) == (addr.sin_addr.s_addr & ip->netmask)) + { + source_ok = 1; + break; + } + } + + free_ipx_interface(iface); + + if(!source_ok) + { + /* Packet did not originate from an associated + * network. + */ + + continue; + } + + log_printf(LOG_DEBUG, "Relaying packet to local port %hu", ntohs(sock->port)); + + struct sockaddr_in send_addr; + + send_addr.sin_family = AF_INET; + send_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + send_addr.sin_port = sock->port; + + if(sendto(private_socket, router_buf, len, 0, (struct sockaddr*)(&send_addr), sizeof(send_addr)) == -1) + { + log_printf(LOG_ERROR, "Error relaying packet: %s", w32_error(WSAGetLastError())); + } + } + } + + unlock_sockets(); + + return true; +} + +static DWORD router_main(void *arg) +{ + while(1) + { + WaitForSingleObject(router_event, INFINITE); + + WSAResetEvent(router_event); + + if(!router_running) + { return 0; } - LeaveCriticalSection(&(router->crit_sec)); - - if(router->listener != -1) { - int newfd = accept(router->listener, NULL, NULL); - if(newfd != -1) { - if(router->client_count == MAX_ROUTER_CLIENTS) { - log_printf(LOG_WARNING, "Too many clients, dropping new connection!"); - goto DROP_NEWFD; - } - - if(WSAEventSelect(newfd, router->wsa_event, FD_READ | FD_CLOSE) == -1) { - log_printf(LOG_ERROR, "WSAEventSelect error: %s", w32_error(WSAGetLastError())); - goto DROP_NEWFD; - } - - router->clients[router->client_count].sock = newfd; - router->clients[router->client_count++].recvbuf_len = 0; - - if(0) { - DROP_NEWFD: - closesocket(newfd); - } - }else if(WSAGetLastError() != WSAEWOULDBLOCK) { - log_printf(LOG_ERROR, "Failed to accept client connection: %s", w32_error(WSAGetLastError())); - } - } - - int i; - for(i = 0; i < router->client_count; i++) { - char *bstart = ((char*)&(router->clients[i].recvbuf)) + router->clients[i].recvbuf_len; - int len = sizeof(struct router_call) - router->clients[i].recvbuf_len; - - if((len = recv(router->clients[i].sock, bstart, len, 0)) == -1) { - if(WSAGetLastError() == WSAEWOULDBLOCK) { - continue; - }else if(WSAGetLastError() == WSAECONNRESET) { - /* Treat connection reset as regular close */ - len = 0; - }else{ - log_printf(LOG_ERROR, "Error reading from client socket: %s", w32_error(WSAGetLastError())); - } - } - - if(len == -1 || len == 0) { - router_drop_client(router, i--); - continue; - } - - if((router->clients[i].recvbuf_len += len) == sizeof(struct router_call)) { - if(router_handle_call(router, router->clients[i].sock, &(router->clients[i].recvbuf))) { - router->clients[i].recvbuf_len = 0; - }else{ - router_drop_client(router, i--); - } - } - } - - struct sockaddr_in addr; - int addrlen = sizeof(addr); - - struct rpacket_header *rp_header = (struct rpacket_header*)router->recvbuf; - ipx_packet *packet = (ipx_packet*)(router->recvbuf + sizeof(*rp_header)); - - int len = recvfrom(router->udp_sock, (char*)packet, MAX_PKT_SIZE, 0, (struct sockaddr*)&addr, &addrlen); - if(len == -1) { - if(WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAECONNRESET) { - continue; - } - - log_printf(LOG_ERROR, "Error reading from UDP socket: %s", w32_error(WSAGetLastError())); + if(!handle_recv(shared_socket) || !handle_recv(private_socket)) + { return 1; } - - EnterCriticalSection(&(router->crit_sec)); - - packet->size = ntohs(packet->size); - - if(len < sizeof(ipx_packet) - 1 || packet->size > MAX_DATA_SIZE || packet->size + sizeof(ipx_packet) - 1 != len) { - LeaveCriticalSection(&(router->crit_sec)); - continue; - } - - if(min_log_level <= LOG_DEBUG) { - IPX_STRING_ADDR(src_addr, addr32_in(packet->src_net), addr48_in(packet->src_node), packet->src_socket); - IPX_STRING_ADDR(dest_addr, addr32_in(packet->dest_net), addr48_in(packet->dest_node), packet->dest_socket); - - log_printf(LOG_DEBUG, "Recieved packet from %s (%s) for %s", src_addr, inet_ntoa(addr.sin_addr), dest_addr); - } - - memset(rp_header, 0, sizeof(*rp_header)); - rp_header->src_ipaddr = addr.sin_addr.s_addr; - - struct router_addr *ra = router->addrs; - - for(; ra; ra = ra->next) - { - if( - ra->local_port && - (ra->filter_ptype < 0 || ra->filter_ptype == packet->ptype) && - (memcmp(packet->dest_net, ra->addr.sa_netnum, 4) == 0 || (memcmp(packet->dest_net, f6, 4) == 0 && (ra->flags & IPX_BROADCAST || !main_config.w95_bug) && ra->flags & IPX_RECV_BCAST)) && - (memcmp(packet->dest_node, ra->addr.sa_nodenum, 6) == 0 || (memcmp(packet->dest_node, f6, 6) == 0 && (ra->flags & IPX_BROADCAST || !main_config.w95_bug) && ra->flags & IPX_RECV_BCAST)) && - packet->dest_socket == ra->addr.sa_socket && - - /* Check source address matches remote_addr if set */ - (ra->remote_addr.sa_family == AF_UNSPEC || (memcmp(ra->remote_addr.sa_netnum, packet->src_net, 4) == 0 && memcmp(ra->remote_addr.sa_nodenum, packet->src_node, 6) == 0)) - ) { - addr32_t ra_net = addr32_in(ra->addr.sa_netnum); - addr48_t ra_node = addr48_in(ra->addr.sa_nodenum); - - /* Fetch the interface this socket is bound to. */ - - ipx_interface_t *iface = ipx_interface_by_addr(ra_net, ra_node); - - if(!iface) - { - char net_s[ADDR32_STRING_SIZE], node_s[ADDR48_STRING_SIZE]; - - addr32_string(net_s, ra_net); - addr48_string(node_s, ra_node); - - log_printf(LOG_WARNING, "No iface for %s/%s! Stale bind?", net_s, node_s); - - continue; - } - - /* Iterate over the subnets and compare to the - * packet source IP address. - */ - - ipx_interface_ip_t *ip; - - int source_ok = 0; - - DL_FOREACH(iface->ipaddr, ip) - { - if((ip->ipaddr & ip->netmask) == (addr.sin_addr.s_addr & ip->netmask)) - { - source_ok = 1; - break; - } - } - - free_ipx_interface(iface); - - if(!source_ok) - { - /* Packet did not originate from an - * associated network. - */ - - continue; - } - - log_printf(LOG_DEBUG, "Relaying packet to local port %hu", ntohs(ra->local_port)); - - struct sockaddr_in send_addr; - - send_addr.sin_family = AF_INET; - send_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - send_addr.sin_port = ra->local_port; - - if(sendto(router->udp_sock, (char*)rp_header, sizeof(*rp_header) + len, 0, (struct sockaddr*)&send_addr, sizeof(send_addr)) == -1) - { - log_printf(LOG_ERROR, "Error relaying packet: %s", w32_error(WSAGetLastError())); - } - } - } - - LeaveCriticalSection(&(router->crit_sec)); } return 0; } - -static int router_bind(struct router_vars *router, SOCKET control, SOCKET sock, struct sockaddr_ipx *addr, int flags) { - /* Network number 00:00:00:00 is specified as the "current" network, this code - * treats it as a wildcard when used for the network OR node numbers. - * - * According to MSDN 6, IPX socket numbers are unique to systems rather than - * interfaces and as such, the same socket number cannot be bound to more than - * one interface, my code lacks any "catch all" address like INADDR_ANY as I have - * not found any mentions of an equivalent address for IPX. This means that a - * given socket number may only be used on one interface. - * - * If you know the above information about IPX socket numbers to be incorrect, - * PLEASE email me with corrections! - */ - - struct ipx_interface *ifaces = get_ipx_interfaces(), *iface; - - addr32_t sa_netnum = addr32_in(addr->sa_netnum); - addr48_t sa_nodenum = addr48_in(addr->sa_nodenum); - - for(iface = ifaces; iface; iface = iface->next) { - if( - (sa_netnum == iface->ipx_net || sa_netnum == 0) - && (sa_nodenum == iface->ipx_node || sa_nodenum == 0) - ) { - break; - } - } - - if(!iface) { - log_printf(LOG_ERROR, "bind failed: no such address"); - - free_ipx_interface_list(&ifaces); - - WSASetLastError(WSAEADDRNOTAVAIL); - return -1; - } - - addr32_out(addr->sa_netnum, iface->ipx_net); - addr48_out(addr->sa_nodenum, iface->ipx_node); - - free_ipx_interface_list(&ifaces); - - EnterCriticalSection(&(router->crit_sec)); - - if(router_get(router, control, sock)) { - log_printf(LOG_ERROR, "bind failed: socket already bound"); - - LeaveCriticalSection(&(router->crit_sec)); - - WSASetLastError(WSAEINVAL); - return -1; - } - - 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(LOG_ERROR, "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{ - /* 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 && (!(a->flags & IPX_REUSE) || !(flags & IPX_REUSE))) { - log_printf(LOG_ERROR, "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->filter_ptype = -1; - new_addr->flags = flags; - new_addr->remote_addr.sa_family = AF_UNSPEC; - new_addr->next = router->addrs; - - router->addrs = new_addr; - - LeaveCriticalSection(&(router->crit_sec)); - - return 0; -} - -/* Set loopback UDP port of emulation socket in NETWORK BYTE ORDER - * Disable recv by setting to zero -*/ -static void router_set_port(struct router_vars *router, SOCKET control, SOCKET sock, uint16_t port) { - EnterCriticalSection(&(router->crit_sec)); - - struct router_addr *addr = router_get(router, control, sock); - if(addr) { - addr->local_port = port; - } - - LeaveCriticalSection(&(router->crit_sec)); -} - -static void router_unbind(struct router_vars *router, SOCKET control, SOCKET sock) { - EnterCriticalSection(&(router->crit_sec)); - - struct router_addr *addr = router->addrs, *prev = NULL; - - while(addr) { - if(addr->control_socket == control && addr->ws_socket == sock) { - if(prev) { - prev->next = addr->next; - }else{ - router->addrs = addr->next; - } - - free(addr); - break; - } - - prev = addr; - addr = addr->next; - } - - LeaveCriticalSection(&(router->crit_sec)); -} - -/* Return the address a given socket is bound to, NULL if unbound */ -static struct router_addr *router_get(struct router_vars *router, SOCKET control, SOCKET sock) { - EnterCriticalSection(&(router->crit_sec)); - - struct router_addr *addr = router->addrs; - - while(addr && (addr->control_socket != control || addr->ws_socket != sock)) { - addr = addr->next; - } - - LeaveCriticalSection(&(router->crit_sec)); - - return addr; -} - -/* Set packet type filter for a socket - * Disable filter by setting to negative value -*/ -static void router_set_filter(struct router_vars *router, SOCKET control, SOCKET sock, int ptype) { - EnterCriticalSection(&(router->crit_sec)); - - struct router_addr *addr = router_get(router, control, sock); - if(addr) { - addr->filter_ptype = ptype; - } - - LeaveCriticalSection(&(router->crit_sec)); -} - -static int router_set_flags(struct router_vars *router, SOCKET control, SOCKET sock, int flags) { - EnterCriticalSection(&(router->crit_sec)); - - struct router_addr *addr = router_get(router, control, sock); - if(addr) { - if(addr->flags & IPX_REUSE && !(flags & IPX_REUSE)) { - struct router_addr *test = router->addrs; - - while(test) { - if(addr != test && memcmp(&(addr->addr), &(test->addr), sizeof(struct sockaddr_ipx)) == 0) { - /* Refuse to disable SO_REUSEADDR when another binding for the same address exists */ - LeaveCriticalSection(&(router->crit_sec)); - return WSAEINVAL; - } - - test = test->next; - } - } - - addr->flags = flags; - } - - LeaveCriticalSection(&(router->crit_sec)); - return ERROR_SUCCESS; -} - -static BOOL router_set_remote(struct router_vars *router, SOCKET control, SOCKET sock, const struct sockaddr_ipx *addr) { - EnterCriticalSection(&(router->crit_sec)); - - struct router_addr *ra = router_get(router, control, sock); - if(ra) { - ra->remote_addr = *addr; - } - - LeaveCriticalSection(&(router->crit_sec)); - return TRUE; -} - -static BOOL router_handle_call(struct router_vars *router, int sock, struct router_call *call) { - struct router_ret ret; - - ret.err_code = ERROR_SUCCESS; - - switch(call->call) { - case rc_bind: { - ret.ret_addr = call->arg_addr; - - if(router_bind(router, sock, call->sock, &(ret.ret_addr), call->arg_int) == -1) { - ret.err_code = WSAGetLastError(); - } - - break; - } - - case rc_unbind: { - router_unbind(router, sock, call->sock); - break; - } - - case rc_port: { - router_set_port(router, sock, call->sock, call->arg_int); - break; - } - - case rc_filter: { - router_set_filter(router, sock, call->sock, call->arg_int); - break; - } - - case rc_flags: { - ret.err_code = router_set_flags(router, sock, call->sock, call->arg_int); - break; - } - - case rc_remote: { - router_set_remote(router, sock, call->sock, &(call->arg_addr)); - break; - } - - default: { - log_printf(LOG_ERROR, "Recieved unknown call, dropping client"); - return FALSE; - } - } - - int sent = 0, sr; - - while(sent < sizeof(ret)) { - char *sbuf = ((char*)&ret) + sent; - - if((sr = send(sock, sbuf, sizeof(ret) - sent, 0)) == -1) { - log_printf(LOG_ERROR, "Send error: %s, dropping client", w32_error(WSAGetLastError())); - return FALSE; - } - - sent += sr; - } - - return TRUE; -} - -static void router_drop_client(struct router_vars *router, int coff) { - EnterCriticalSection(&(router->crit_sec)); - - struct router_addr *addr = router->addrs, *dp = NULL; - - while(addr) { - dp = addr->control_socket == router->clients[coff].sock ? addr : NULL; - addr = addr->next; - - if(dp) { - router_unbind(router, dp->control_socket, dp->ws_socket); - } - } - - LeaveCriticalSection(&(router->crit_sec)); - - closesocket(router->clients[coff].sock); - router->clients[coff] = router->clients[--router->client_count]; -} - -BOOL rclient_init(struct rclient *rclient) { - rclient->cs_init = FALSE; - rclient->sock = -1; - rclient->router = NULL; - rclient->thread = NULL; - - if(InitializeCriticalSectionAndSpinCount(&(rclient->cs), 0x80000000)) { - rclient->cs_init = TRUE; - }else{ - log_printf(LOG_ERROR, "Failed to initialise critical section: %s", w32_error(GetLastError())); - return FALSE; - } - - return TRUE; -} - -/* Connect to the remote router process, spawns local (private) router thread if - * it the remote one isn't running. - * - * Calling when a router is already running is a no-op. -*/ -BOOL rclient_start(struct rclient *rclient) { - if(rclient->sock != -1 || rclient->router) { - return TRUE; - } - - if((rclient->sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - log_printf(LOG_ERROR, "Cannot create TCP socket: %s", w32_error(WSAGetLastError())); - return FALSE; - } - - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr.sin_port = htons(main_config.router_port); - - if(connect(rclient->sock, (struct sockaddr*)&addr, sizeof(addr)) == 0) { - return TRUE; - } - - log_printf(LOG_ERROR, "Cannot connect to router process: %s", w32_error(WSAGetLastError())); - - closesocket(rclient->sock); - rclient->sock = -1; - - log_printf(LOG_INFO, "Creating private router thread..."); - - if(!(rclient->router = router_init(FALSE))) { - return FALSE; - } - - rclient->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&router_main, rclient->router, 0, NULL); - if(!rclient->thread) { - log_printf(LOG_ERROR, "Failed to create router thread: %s", w32_error(GetLastError())); - - router_destroy(rclient->router); - rclient->router = NULL; - - return FALSE; - } - - return TRUE; -} - -/* Disconnect from the router process or stop the private router thread. - * - * Do not attempt to call rclient_start() again without calling rclient_init() - * as the critical section object is deleted. -*/ -void rclient_stop(struct rclient *rclient) { - if(rclient->sock != -1) { - closesocket(rclient->sock); - rclient->sock = -1; - } - - if(rclient->router) { - EnterCriticalSection(&(rclient->router->crit_sec)); - - rclient->router->running = FALSE; - WSASetEvent(rclient->router->wsa_event); - - LeaveCriticalSection(&(rclient->router->crit_sec)); - - if(WaitForSingleObject(rclient->thread, 3000) == WAIT_TIMEOUT) { - log_printf(LOG_WARNING, "Router thread didn't exit in 3 seconds, terminating"); - TerminateThread(rclient->thread, 0); - } - - CloseHandle(rclient->thread); - rclient->thread = NULL; - - router_destroy(rclient->router); - rclient->router = NULL; - } - - if(rclient->cs_init) { - DeleteCriticalSection(&(rclient->cs)); - rclient->cs_init = FALSE; - } -} - -static BOOL rclient_do(struct rclient *rclient, struct router_call *call, struct router_ret *ret) { - EnterCriticalSection(&(rclient->cs)); - - int done, r; - - for(done = 0; done < sizeof(*call);) { - if((r = send(rclient->sock, ((char*)call) + done, sizeof(*call) - done, 0)) == -1) { - log_printf(LOG_ERROR, "rclient_do: send error: %s", w32_error(WSAGetLastError())); - - LeaveCriticalSection(&(rclient->cs)); - return FALSE; - } - - done += r; - } - - for(done = 0; done < sizeof(*ret);) { - if((r = recv(rclient->sock, ((char*)ret) + done, sizeof(*ret) - done, 0)) == -1) { - log_printf(LOG_ERROR, "rclient_do: recv error: %s", w32_error(WSAGetLastError())); - - LeaveCriticalSection(&(rclient->cs)); - return FALSE; - }else if(r == 0) { - log_printf(LOG_ERROR, "rclient_do: Lost connection"); - WSASetLastError(WSAECONNRESET); - - LeaveCriticalSection(&(rclient->cs)); - return FALSE; - } - - done += r; - } - - LeaveCriticalSection(&(rclient->cs)); - return TRUE; -} - -BOOL rclient_bind(struct rclient *rclient, SOCKET sock, struct sockaddr_ipx *addr, BOOL reuse) { - if(rclient->sock != -1) { - struct router_call call; - struct router_ret ret; - - call.call = rc_bind; - call.sock = sock; - call.arg_addr = *addr; - call.arg_int = reuse; - - if(!rclient_do(rclient, &call, &ret)) { - return FALSE; - } - - if(ret.err_code == ERROR_SUCCESS) { - *addr = ret.ret_addr; - - return TRUE; - }else{ - WSASetLastError(ret.err_code); - return FALSE; - } - }else if(rclient->router) { - return router_bind(rclient->router, 0, sock, addr, reuse) == 0 ? TRUE: FALSE; - } - - log_printf(LOG_ERROR, "rclient_bind: No router?!"); - - WSASetLastError(WSAENETDOWN); - return FALSE; -} - -BOOL rclient_unbind(struct rclient *rclient, SOCKET sock) { - if(rclient->sock != -1) { - struct router_call call; - struct router_ret ret; - - call.call = rc_unbind; - call.sock = sock; - - if(!rclient_do(rclient, &call, &ret)) { - return FALSE; - } - - return TRUE; - }else if(rclient->router) { - router_unbind(rclient->router, 0, sock); - return TRUE; - } - - log_printf(LOG_ERROR, "rclient_unbind: No router?!"); - - WSASetLastError(WSAENETDOWN); - return FALSE; -} - -BOOL rclient_set_port(struct rclient *rclient, SOCKET sock, uint16_t port) { - if(rclient->sock != -1) { - struct router_call call; - struct router_ret ret; - - call.call = rc_port; - call.sock = sock; - call.arg_int = port; - - if(!rclient_do(rclient, &call, &ret)) { - return FALSE; - } - - return TRUE; - }else if(rclient->router) { - router_set_port(rclient->router, 0, sock, port); - return TRUE; - } - - log_printf(LOG_ERROR, "rclient_set_port: No router?!"); - - WSASetLastError(WSAENETDOWN); - return FALSE; -} - -BOOL rclient_set_filter(struct rclient *rclient, SOCKET sock, int ptype) { - if(rclient->sock != -1) { - struct router_call call; - struct router_ret ret; - - call.call = rc_filter; - call.sock = sock; - call.arg_int = ptype; - - if(!rclient_do(rclient, &call, &ret)) { - return FALSE; - } - - return TRUE; - }else if(rclient->router) { - router_set_filter(rclient->router, 0, sock, ptype); - return TRUE; - } - - log_printf(LOG_ERROR, "rclient_set_filter: No router?!"); - - WSASetLastError(WSAENETDOWN); - return FALSE; -} - -BOOL rclient_set_flags(struct rclient *rclient, SOCKET sock, int flags) { - if(rclient->sock != -1) { - struct router_call call; - struct router_ret ret; - - call.call = rc_flags; - call.sock = sock; - call.arg_int = flags; - - if(!rclient_do(rclient, &call, &ret)) { - return FALSE; - } - - if(ret.err_code == ERROR_SUCCESS) { - return TRUE; - }else{ - WSASetLastError(ret.err_code); - return FALSE; - } - }else if(rclient->router) { - int err = router_set_flags(rclient->router, 0, sock, flags); - if(err == ERROR_SUCCESS) { - return TRUE; - }else{ - WSASetLastError(err); - return FALSE; - } - - return TRUE; - } - - log_printf(LOG_ERROR, "rclient_set_reuse: No router?!"); - - WSASetLastError(WSAENETDOWN); - return FALSE; -} - -BOOL rclient_set_remote(struct rclient *rclient, SOCKET sock, const struct sockaddr_ipx *addr) { - if(rclient->sock != -1) { - struct router_call call; - struct router_ret ret; - - call.call = rc_remote; - call.sock = sock; - call.arg_addr = *addr; - - if(!rclient_do(rclient, &call, &ret)) { - return FALSE; - } - - return TRUE; - }else if(rclient->router) { - return router_set_remote(rclient->router, 0, sock, addr); - } - - log_printf(LOG_ERROR, "rclient_bind: No router?!"); - - WSASetLastError(WSAENETDOWN); - return FALSE; -} diff --git a/src/router.h b/src/router.h index 49ef7bb..eeedd68 100644 --- a/src/router.h +++ b/src/router.h @@ -23,102 +23,10 @@ #include #include -#define MAX_ROUTER_CLIENTS 128 +extern SOCKET shared_socket; +extern SOCKET private_socket; -struct router_call { - enum { - rc_bind, - rc_unbind, - rc_port, - rc_filter, - rc_flags, - rc_remote - } call; - - SOCKET sock; - - struct sockaddr_ipx arg_addr; - int arg_int; -}; - -struct router_ret { - int err_code; /* ERROR_SUCCESS on success */ - - struct sockaddr_ipx ret_addr; - uint32_t ret_u32; -}; - -/* Represents a bound IPX address */ -struct router_addr { - struct sockaddr_ipx addr; - - uint16_t local_port; /* Local UDP port (NETWORK BYTE ORDER) */ - SOCKET ws_socket; /* Application socket */ - SOCKET control_socket; /* Control socket */ - int filter_ptype; /* Packet type filter, negative to disable */ - int flags; - - /* Only accept packets from this address (any if AF_UNSPEC) */ - struct sockaddr_ipx remote_addr; - - struct router_addr *next; -}; - -struct router_client { - SOCKET sock; - - struct router_call recvbuf; - int recvbuf_len; -}; - -struct router_vars { - BOOL running; - - SOCKET udp_sock; - SOCKET listener; - - struct router_client clients[MAX_ROUTER_CLIENTS]; - int client_count; - - WSAEVENT wsa_event; - - CRITICAL_SECTION crit_sec; - BOOL crit_sec_init; - - struct router_addr *addrs; - - char *recvbuf; -}; - -struct rclient { - CRITICAL_SECTION cs; - BOOL cs_init; - - SOCKET sock; - - struct router_vars *router; - HANDLE thread; -}; - -struct rpacket_header { - uint32_t src_ipaddr; - char spare[20]; -} __attribute__((__packed__)); - -struct router_vars *router_init(BOOL global); -void router_destroy(struct router_vars *router); - -DWORD router_main(void *arg); - -BOOL rclient_init(struct rclient *rclient); -BOOL rclient_start(struct rclient *rclient); -void rclient_stop(struct rclient *rclient); - -BOOL rclient_bind(struct rclient *rclient, SOCKET sock, struct sockaddr_ipx *addr, int flags); -BOOL rclient_unbind(struct rclient *rclient, SOCKET sock); -BOOL rclient_set_port(struct rclient *rclient, SOCKET sock, uint16_t port); -BOOL rclient_set_filter(struct rclient *rclient, SOCKET sock, int ptype); -BOOL rclient_set_flags(struct rclient *rclient, SOCKET sock, int flags); -BOOL rclient_set_remote(struct rclient *rclient, SOCKET sock, const struct sockaddr_ipx *addr); +void router_init(void); +void router_cleanup(void); #endif /* !IPXWRAPPER_ROUTER_H */ diff --git a/src/winsock.c b/src/winsock.c index 38adf3d..c490746 100644 --- a/src/winsock.c +++ b/src/winsock.c @@ -27,6 +27,7 @@ #include "interface.h" #include "router.h" #include "addrcache.h" +#include "addrtable.h" typedef struct _PROTOCOL_INFO { DWORD dwServiceFlags ; @@ -205,8 +206,9 @@ int WSAAPI closesocket(SOCKET fd) { log_printf(LOG_INFO, "IPX socket closed (fd = %d)", fd); - if(ptr->flags & IPX_BOUND) { - rclient_unbind(&g_rclient, fd); + if(ptr->flags & IPX_BOUND) + { + addr_table_remove(ptr->port); } if(ptr == sockets) { @@ -228,13 +230,76 @@ int WSAAPI closesocket(SOCKET fd) { RETURN(0); } -int WSAAPI bind(SOCKET fd, const struct sockaddr *addr, int addrlen) { - ipx_socket *ptr = get_socket(fd); +static bool _complete_bind_address(struct sockaddr_ipx *addr) +{ + /* Network number 00:00:00:00 is specified as the "current" network, this code + * treats it as a wildcard when used for the network OR node numbers. + * + * According to MSDN 6, IPX socket numbers are unique to systems rather than + * interfaces and as such, the same socket number cannot be bound to more than + * one interface. + * + * If you know the above information about IPX socket numbers to be incorrect, + * PLEASE email me with corrections! + */ - if(ptr) { + /* Iterate over the interfaces list, stop at the first match. */ + + struct ipx_interface *ifaces = get_ipx_interfaces(), *iface; + + addr32_t netnum = addr32_in(addr->sa_netnum); + addr48_t nodenum = addr48_in(addr->sa_nodenum); + + for(iface = ifaces; iface; iface = iface->next) + { + if( + (netnum == iface->ipx_net || netnum == 0) + && (nodenum == iface->ipx_node || nodenum == 0) + ) { + break; + } + } + + if(!iface) + { + log_printf(LOG_ERROR, "bind failed: no such address"); + + free_ipx_interface_list(&ifaces); + + WSASetLastError(WSAEADDRNOTAVAIL); + return false; + } + + addr32_out(addr->sa_netnum, iface->ipx_net); + addr48_out(addr->sa_nodenum, iface->ipx_node); + + free_ipx_interface_list(&ifaces); + + /* Socket zero signifies automatic allocation. */ + + if(addr->sa_socket == 0 && (addr->sa_socket = addr_table_auto_socket()) == 0) + { + /* Hmmm. We appear to have ran out of sockets?! */ + + log_printf(LOG_ERROR, "bind failed: out of sockets?!"); + + WSASetLastError(WSAEADDRNOTAVAIL); + return false; + } + + return true; +} + +int WSAAPI bind(SOCKET fd, const struct sockaddr *addr, int addrlen) +{ + ipx_socket *sock = get_socket(fd); + + if(sock) + { struct sockaddr_ipx ipxaddr; - if(addrlen < sizeof(ipxaddr)) { + if(addrlen < sizeof(ipxaddr) || addr->sa_family != AF_IPX) + { RETURN_WSA(WSAEFAULT, -1); } @@ -244,52 +309,108 @@ int WSAAPI bind(SOCKET fd, const struct sockaddr *addr, int addrlen) { log_printf(LOG_INFO, "bind(%d, %s)", fd, req_addr_s); - if(ptr->flags & IPX_BOUND) { + if(sock->flags & IPX_BOUND) + { log_printf(LOG_ERROR, "bind failed: socket already bound"); - RETURN_WSA(WSAEINVAL, -1); + + unlock_sockets(); + + WSASetLastError(WSAEINVAL); + return -1; } - if(!rclient_bind(&g_rclient, fd, &ipxaddr, ptr->flags)) { - RETURN(-1); + addr_table_lock(); + + /* Resolve any wildcards in the requested address. */ + + if(!_complete_bind_address(&ipxaddr)) + { + addr_table_unlock(); + unlock_sockets(); + + return -1; } IPX_STRING_ADDR(got_addr_s, addr32_in(ipxaddr.sa_netnum), addr48_in(ipxaddr.sa_nodenum), ipxaddr.sa_socket); log_printf(LOG_INFO, "bind address: %s", got_addr_s); - struct sockaddr_in bind_addr; - bind_addr.sin_family = AF_INET; - bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - bind_addr.sin_port = 0; + /* Check that the address is free. */ - if(r_bind(fd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) == -1) { - log_printf(LOG_ERROR, "Binding local UDP socket failed: %s", w32_error(WSAGetLastError())); + if(!addr_table_check(&ipxaddr, !!(sock->flags & IPX_REUSE))) + { + /* Address has already been bound. */ - rclient_unbind(&g_rclient, fd); - RETURN(-1); + addr_table_unlock(); + unlock_sockets(); + + WSASetLastError(WSAEADDRINUSE); + return -1; } + /* Bind the fake (UDP) socket. */ + + struct sockaddr_in bind_addr; + + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + bind_addr.sin_port = 0; + + if(r_bind(fd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)) == -1) + { + log_printf( + LOG_ERROR, + "Binding local UDP socket failed: %s", + w32_error(WSAGetLastError()) + ); + + addr_table_unlock(); + unlock_sockets(); + + return -1; + } + + /* Find out what port we got allocated. */ + int al = sizeof(bind_addr); - if(r_getsockname(fd, (struct sockaddr*)&bind_addr, &al) == -1) { - log_printf(LOG_ERROR, "getsockname failed: %s", w32_error(WSAGetLastError())); + if(r_getsockname(fd, (struct sockaddr*)&bind_addr, &al) == -1) + { + /* Socket state is now inconsistent as the underlying + * UDP socket has been bound, but the IPX socket failed + * to bind. + * + * We also don't know what port number the socket is + * bound to and can't unbind, so future bind attempts + * will fail. + */ - rclient_unbind(&g_rclient, fd); - RETURN(-1); + log_printf(LOG_ERROR, "getsockname: %s", w32_error(WSAGetLastError())); + log_printf(LOG_WARNING, "SOCKET STATE IS NOW INCONSISTENT!"); + + addr_table_unlock(); + unlock_sockets(); + + return -1; } - log_printf(LOG_DEBUG, "Bound to local UDP port %hu", ntohs(bind_addr.sin_port)); + sock->port = bind_addr.sin_port; - memcpy(&(ptr->addr), &ipxaddr, sizeof(ipxaddr)); - ptr->flags |= IPX_BOUND; + log_printf(LOG_DEBUG, "Bound to local UDP port %hu", ntohs(sock->port)); - if(ptr->flags & IPX_RECV) { - rclient_set_port(&g_rclient, fd, bind_addr.sin_port); - } + /* Add to the address table. */ - rclient_set_filter(&g_rclient, fd, ptr->flags & IPX_FILTER ? ptr->f_ptype : -1); + addr_table_add(&ipxaddr, sock->port, !!(sock->flags & IPX_REUSE)); - RETURN(0); + /* Mark the IPX socket as bound. */ + + memcpy(&(sock->addr), &ipxaddr, sizeof(ipxaddr)); + sock->flags |= IPX_BOUND; + + addr_table_unlock(); + unlock_sockets(); + + return 0; }else{ return r_bind(fd, addr, addrlen); } @@ -317,8 +438,6 @@ int WSAAPI getsockname(SOCKET fd, struct sockaddr *addr, int *addrlen) { } } -#define RECVBUF_SIZE (sizeof(struct rpacket_header) + MAX_PKT_SIZE) - /* Recieve a packet from an IPX socket * addr must be NULL or a region of memory big enough for a sockaddr_ipx * @@ -337,22 +456,22 @@ static int recv_packet(ipx_socket *sockptr, char *buf, int bufsize, int flags, s return -1; } - char *recvbuf = malloc(RECVBUF_SIZE); + char *recvbuf = malloc(MAX_PKT_SIZE); if(!recvbuf) { WSASetLastError(ERROR_OUTOFMEMORY); return -1; } - struct rpacket_header *rp_header = (struct rpacket_header*)recvbuf; - struct ipx_packet *packet = (struct ipx_packet*)(recvbuf + sizeof(*rp_header)); + struct ipx_packet *packet = (struct ipx_packet*)(recvbuf); - int rval = r_recv(fd, recvbuf, RECVBUF_SIZE, flags); + int rval = r_recv(fd, recvbuf, MAX_PKT_SIZE, flags); if(rval == -1) { free(recvbuf); return -1; } - if(rval < sizeof(*rp_header) + sizeof(ipx_packet) - 1 || rval != sizeof(*rp_header) + packet->size + sizeof(ipx_packet) - 1) { + if(rval < sizeof(ipx_packet) - 1 || rval != packet->size + sizeof(ipx_packet) - 1) + { log_printf(LOG_ERROR, "Invalid packet received on loopback port!"); free(recvbuf); @@ -367,15 +486,6 @@ static int recv_packet(ipx_socket *sockptr, char *buf, int bufsize, int flags, s log_printf(LOG_DEBUG, "Received packet from %s", addr_s); } - /* TODO: Move full sockaddr into rp_header? */ - - struct sockaddr_in real_addr; - real_addr.sin_family = AF_INET; - real_addr.sin_addr.s_addr = rp_header->src_ipaddr; - real_addr.sin_port = htons(main_config.udp_port); - - addr_cache_set((struct sockaddr*)&real_addr, sizeof(real_addr), addr32_in(packet->src_net), addr48_in(packet->src_node), 0); - if(addr) { addr->sa_family = AF_IPX; memcpy(addr->sa_netnum, packet->src_net, 4); @@ -611,11 +721,6 @@ int WSAAPI getsockopt(SOCKET fd, int level, int optname, char FAR *optval, int F sockptr->flags &= ~(flag); \ } -#define RC_SET_FLAG(flag, state) \ - if(sockptr->flags & IPX_BOUND && !rclient_set_flags(&g_rclient, fd, (sockptr->flags & ~(flag)) | ((state) ? (flag) : 0))) { \ - RETURN(-1); \ - } - int WSAAPI setsockopt(SOCKET fd, int level, int optname, const char FAR *optval, int optlen) { int *intval = (int*)optval; BOOL *bval = (BOOL*)optval; @@ -649,10 +754,6 @@ int WSAAPI setsockopt(SOCKET fd, int level, int optname, const char FAR *optval, } if(optname == IPX_FILTERPTYPE) { - if(sockptr->flags & IPX_BOUND && !rclient_set_filter(&g_rclient, fd, *intval)) { - RETURN(-1); - } - sockptr->f_ptype = *intval; sockptr->flags |= IPX_FILTER; @@ -660,17 +761,12 @@ int WSAAPI setsockopt(SOCKET fd, int level, int optname, const char FAR *optval, } if(optname == IPX_STOPFILTERPTYPE) { - if(sockptr->flags & IPX_BOUND && !rclient_set_filter(&g_rclient, fd, -1)) { - RETURN(-1); - } - sockptr->flags &= ~IPX_FILTER; RETURN(0); } if(optname == IPX_RECEIVE_BROADCAST) { - RC_SET_FLAG(IPX_RECV_BCAST, *bval); SET_FLAG(IPX_RECV_BCAST, *bval); RETURN(0); @@ -688,14 +784,12 @@ int WSAAPI setsockopt(SOCKET fd, int level, int optname, const char FAR *optval, if(level == SOL_SOCKET) { if(optname == SO_BROADCAST) { - RC_SET_FLAG(IPX_BROADCAST, *bval); SET_FLAG(IPX_BROADCAST, *bval); RETURN(0); } if(optname == SO_REUSEADDR) { - RC_SET_FLAG(IPX_REUSE, *bval); SET_FLAG(IPX_REUSE, *bval); RETURN(0); @@ -727,7 +821,7 @@ static int send_packet(const ipx_packet *packet, int len, struct sockaddr *addr, log_printf(LOG_DEBUG, "Sending packet to %s (%s:%hu)", addr_s, inet_ntoa(v4->sin_addr), ntohs(v4->sin_port)); } - return (r_sendto(send_fd, (char*)packet, len, 0, addr, addrlen) == len); + return (r_sendto(private_socket, (char*)packet, len, 0, addr, addrlen) == len); } int WSAAPI sendto(SOCKET fd, const char *buf, int len, int flags, const struct sockaddr *addr, int addrlen) { @@ -872,10 +966,6 @@ int PASCAL shutdown(SOCKET fd, int cmd) { if(sockptr) { if(cmd == SD_RECEIVE || cmd == SD_BOTH) { - if(sockptr->flags & IPX_BOUND && !rclient_set_port(&g_rclient, fd, 0)) { - RETURN(-1); - } - sockptr->flags &= ~IPX_RECV; } @@ -945,10 +1035,6 @@ int PASCAL connect(SOCKET fd, const struct sockaddr *addr, int addrlen) { struct sockaddr_ipx dc_addr; dc_addr.sa_family = AF_UNSPEC; - if(!rclient_set_remote(&g_rclient, fd, &dc_addr)) { - RETURN(-1); - } - sockptr->flags &= ~IPX_CONNECTED; RETURN(0); @@ -973,10 +1059,6 @@ int PASCAL connect(SOCKET fd, const struct sockaddr *addr, int addrlen) { } } - if(!rclient_set_remote(&g_rclient, fd, ipxaddr)) { - RETURN(-1); - } - memcpy(&(sockptr->remote_addr), addr, sizeof(*ipxaddr)); sockptr->flags |= IPX_CONNECTED;