diff --git a/Makefile b/Makefile index e181a6d..3ae2b4f 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ VERSION := git 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/addrtable.o src/firewall.o src/wpcap_stubs.o + src/firewall.o src/wpcap_stubs.o BIN_FILES := $(shell cat manifest.bin.txt) SRC_FILES := $(shell cat manifest.src.txt) diff --git a/src/addrtable.c b/src/addrtable.c deleted file mode 100644 index 052216c..0000000 --- a/src/addrtable.c +++ /dev/null @@ -1,442 +0,0 @@ -/* IPXWrapper - Address table - * Copyright (C) 2008-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. -*/ - -/* The address table is used to co-ordinate the IPX addresses in use accross all - * IPXWrapper instances. -*/ - -#include -#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(); -} - -/* Find the last entry in the address table. - * Returns addr_table_base if there are none. -*/ -static addr_table_entry_t *_last_entry() -{ - addr_table_entry_t *last = addr_table_base, *next = addr_table_base + 1; - addr_table_entry_t *end = addr_table_base + ADDR_TABLE_MAX_ENTRIES; - - while(next < end && next->flags & ADDR_TABLE_ENTRY_VALID) - { - last = next++; - } - - return last; -} - -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; - } - - addr_table_base = (addr_table_entry_t*)(addr_table_header + 1); - - 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; - } - - addr_table_unlock(); -} - -void addr_table_cleanup(void) -{ - /* Release any remaining addresses bound by this process. */ - - if(addr_table_base) - { - ipx_socket *s, *tmp; - - HASH_ITER(hh, sockets, s, tmp) - { - if(s->flags & IPX_BOUND) - { - addr_table_remove(s); - } - } - } - - /* Close handles to the address table. */ - - 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) -{ - 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) - { - 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, *tmp; - - HASH_ITER(hh, sockets, s, tmp) - { - if(memcmp(&(s->addr), addr, sizeof(struct sockaddr_ipx)) == 0) - { - 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, *tmp; - - HASH_ITER(hh, sockets, s, tmp) - { - if((s->flags & IPX_BOUND) && ntohs(sock) == s->addr.sa_socket) - { - if(sock == 65535) - { - unlock_sockets(); - return 0; - } - - sock++; - - s = sockets; - continue; - } - } - - 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 ipx_socket *sock) -{ - 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(sock->addr.sa_netnum); - entry->nodenum = addr48_in(sock->addr.sa_nodenum); - entry->socket = sock->addr.sa_socket; - - if(sock->flags & IPX_IS_SPXII) - { - entry->type = ADDR_TABLE_TYPE_SPXII; - } - else if(sock->flags & IPX_IS_SPX) - { - entry->type = ADDR_TABLE_TYPE_SPX; - } - else{ - entry->type = ADDR_TABLE_TYPE_IPX; - } - - entry->process = GetCurrentProcessId(); - entry->sock = sock->fd; - - entry->flags = ADDR_TABLE_ENTRY_VALID; - 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(const ipx_socket *sock) -{ - 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)) - { - if(entry->process == GetCurrentProcessId() && entry->sock == sock->fd) - { - addr_table_entry_t *last = _last_entry(); - - *entry = *last; - last->flags &= ~ADDR_TABLE_ENTRY_VALID; - - break; - } - - ++entry; - } - - 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(); - - /* Remove any expired entries. */ - - addr_table_entry_t *entry = addr_table_base; - addr_table_entry_t *last = _last_entry(); - addr_table_entry_t *end = addr_table_base + ADDR_TABLE_MAX_ENTRIES; - - for(; entry < end && (entry->flags & ADDR_TABLE_ENTRY_VALID); entry++) - { - if(entry->time + ADDR_TABLE_ENTRY_TIMEOUT <= time(NULL)) - { - *entry = *last; - - last->flags &= ~ADDR_TABLE_ENTRY_VALID; - last--; - } - } - - /* This is really, really efficient. */ - - ipx_socket *sock, *tmp; - - HASH_ITER(hh, sockets, sock, tmp) - { - 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->process == GetCurrentProcessId() && entry->sock == sock->fd) - { - entry->time = time(NULL); - break; - } - } - } - } - - addr_table_unlock(); - - unlock_sockets(); -} diff --git a/src/addrtable.h b/src/addrtable.h deleted file mode 100644 index 8320d46..0000000 --- a/src/addrtable.h +++ /dev/null @@ -1,80 +0,0 @@ -/* IPXWrapper - Address table - * Copyright (C) 2008-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. -*/ - -#ifndef ADDRTABLE_H -#define ADDRTABLE_H - -#include -#include -#include - -#include "addr.h" -#include "ipxwrapper.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 2 - -typedef struct addr_table_header addr_table_header_t; - -struct addr_table_header -{ - int version; -}; - -#define ADDR_TABLE_ENTRY_VALID ((int)(1<<0)) - -typedef struct addr_table_entry addr_table_entry_t; - -struct addr_table_entry -{ - addr32_t netnum; - addr48_t nodenum; - uint16_t socket; - - enum { - ADDR_TABLE_TYPE_IPX, - ADDR_TABLE_TYPE_SPX, - ADDR_TABLE_TYPE_SPXII - } type; - - DWORD process; - int sock; - - int flags; - 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); -uint16_t addr_table_auto_socket(void); - -void addr_table_add(const ipx_socket *sock); -void addr_table_remove(const ipx_socket *sock); - -void addr_table_update(void); - -#endif /* !ADDRTABLE_H */ diff --git a/src/ipxwrapper.c b/src/ipxwrapper.c index ab4533b..5a6d5df 100644 --- a/src/ipxwrapper.c +++ b/src/ipxwrapper.c @@ -31,7 +31,6 @@ #include "interface.h" #include "router.h" #include "addrcache.h" -#include "addrtable.h" int _putenv(const char *envstring); @@ -104,16 +103,12 @@ BOOL WINAPI DllMain(HINSTANCE me, DWORD why, LPVOID res) return FALSE; } - addr_table_init(); - router_init(); } else if(why == DLL_PROCESS_DETACH) { router_cleanup(); - addr_table_cleanup(); - WSACleanup(); DeleteCriticalSection(&sockets_cs); diff --git a/src/ipxwrapper.h b/src/ipxwrapper.h index beb3926..69bd2c4 100644 --- a/src/ipxwrapper.h +++ b/src/ipxwrapper.h @@ -66,6 +66,7 @@ struct ipx_socket { /* The following values are undefined when IPX_BOUND is not set */ struct sockaddr_ipx addr; + HANDLE sock_mut; /* Address used with connect call, only set when IPX_CONNECTED is */ struct sockaddr_ipx remote_addr; diff --git a/src/router.c b/src/router.c index 237c7fa..0924476 100644 --- a/src/router.c +++ b/src/router.c @@ -27,7 +27,6 @@ #include "ipxwrapper.h" #include "interface.h" #include "addrcache.h" -#include "addrtable.h" static bool router_running = false; static WSAEVENT router_event = WSA_INVALID_EVENT; @@ -552,8 +551,6 @@ static DWORD router_main(void *arg) { DWORD exit_status = 0; - time_t last_at_update = 0; - ipx_interface_t *interfaces = NULL; HANDLE *wait_events = &router_event; @@ -597,12 +594,6 @@ static DWORD router_main(void *arg) break; } - if(last_at_update != time(NULL)) - { - addr_table_update(); - last_at_update = time(NULL); - } - if(ipx_use_pcap) { ipx_interface_t *i; diff --git a/src/winsock.c b/src/winsock.c index a15940a..27ea058 100644 --- a/src/winsock.c +++ b/src/winsock.c @@ -27,7 +27,6 @@ #include "interface.h" #include "router.h" #include "addrcache.h" -#include "addrtable.h" #define XP_CONNECTIONLESS 0x00000001 #define XP_GUARANTEED_DELIVERY 0x00000002 @@ -420,7 +419,7 @@ int WSAAPI closesocket(SOCKET sockfd) if(sock->flags & IPX_BOUND) { - addr_table_remove(sock); + CloseHandle(sock->sock_mut); } HASH_DEL(sockets, sock); @@ -431,7 +430,59 @@ int WSAAPI closesocket(SOCKET sockfd) return 0; } -static bool _complete_bind_address(struct sockaddr_ipx *addr) +static HANDLE _open_socket_mutex(uint16_t socket, bool exclusive) +{ + char mutex_name[256]; + snprintf(mutex_name, sizeof(mutex_name), "ipxwrapper_socket_%hu", socket); + + HANDLE mutex = CreateMutex(NULL, FALSE, mutex_name); + if(!mutex) + { + log_printf(LOG_ERROR, "Error when creating mutex %s: %s", + mutex_name, w32_error(GetLastError())); + } + + if(GetLastError() == ERROR_ALREADY_EXISTS && exclusive) + { + CloseHandle(mutex); + return NULL; + } + + return mutex; +} + +bool _complete_bind(ipx_socket *sock) +{ + if(ntohs(sock->addr.sa_socket) == 0) + { + uint16_t socknum = 1024; + + do { + HANDLE mutex = _open_socket_mutex(socknum, true); + if(mutex) + { + sock->addr.sa_socket = htons(socknum); + sock->sock_mut = mutex; + sock->flags |= IPX_BOUND; + + return true; + } + } while(socknum++ != 65535); + } + else{ + if((sock->sock_mut = _open_socket_mutex( + ntohs(sock->addr.sa_socket), !(sock->flags & IPX_REUSE)))) + { + sock->flags |= IPX_BOUND; + + return true; + } + } + + return false; +} + +static bool _resolve_bind_address(ipx_socket *sock, const 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. @@ -466,28 +517,14 @@ static bool _complete_bind_address(struct sockaddr_ipx *addr) 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); + addr32_out(sock->addr.sa_netnum, iface->ipx_net); + addr48_out(sock->addr.sa_nodenum, iface->ipx_node); + sock->addr.sa_socket = addr->sa_socket; 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; } @@ -523,38 +560,30 @@ int WSAAPI bind(SOCKET fd, const struct sockaddr *addr, int addrlen) return -1; } - addr_table_lock(); - /* Resolve any wildcards in the requested address. */ - if(!_complete_bind_address(&ipxaddr)) + if(!_resolve_bind_address(sock, &ipxaddr)) { - addr_table_unlock(); unlock_sockets(); + WSASetLastError(WSAEADDRNOTAVAIL); 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); - /* Check that the address is free. */ - if(!(sock->flags & IPX_REUSE) && !addr_table_check(&ipxaddr)) + if(!_complete_bind(sock)) { - /* Address has already been bound. */ - - log_printf(LOG_ERROR, "bind failed: address already in use"); - - WSASetLastError(WSAEADDRINUSE); - - addr_table_unlock(); unlock_sockets(); + WSASetLastError(WSAEADDRINUSE); return -1; } + IPX_STRING_ADDR(got_addr_s, addr32_in(sock->addr.sa_netnum), addr48_in(sock->addr.sa_nodenum), sock->addr.sa_socket); + + log_printf(LOG_INFO, "bind address: %s", got_addr_s); + /* Bind the underlying socket. */ struct sockaddr_in bind_addr; @@ -567,7 +596,9 @@ int WSAAPI bind(SOCKET fd, const struct sockaddr *addr, int addrlen) { log_printf(LOG_ERROR, "Binding local socket failed: %s", w32_error(WSAGetLastError())); - addr_table_unlock(); + CloseHandle(sock->sock_mut); + sock->flags &= ~IPX_BOUND; + unlock_sockets(); return -1; @@ -590,26 +621,17 @@ int WSAAPI bind(SOCKET fd, const struct sockaddr *addr, int addrlen) log_printf(LOG_ERROR, "Cannot get local port of socket: %s", w32_error(WSAGetLastError())); log_printf(LOG_WARNING, "Socket %d is NOW INCONSISTENT!", fd); - addr_table_unlock(); + CloseHandle(sock->sock_mut); + sock->flags &= ~IPX_BOUND; + unlock_sockets(); return -1; } sock->port = bind_addr.sin_port; - log_printf(LOG_DEBUG, "Bound to local port %hu", ntohs(sock->port)); - /* Mark the IPX socket as bound and insert it into the address - * table. - */ - - memcpy(&(sock->addr), &ipxaddr, sizeof(ipxaddr)); - sock->flags |= IPX_BOUND; - - addr_table_add(sock); - - addr_table_unlock(); unlock_sockets(); return 0; @@ -1959,26 +1981,16 @@ static int _connect_spx(ipx_socket *sock, struct sockaddr_ipx *ipxaddr) /* The sa_netnum and sa_nodenum fields are filled out above. */ - addr_table_lock(); - - if((sock->addr.sa_socket = addr_table_auto_socket()) != 0) + if(!_complete_bind(sock)) { - sock->flags |= IPX_BOUND; - - addr_table_add(sock); - } - else{ log_printf(LOG_ERROR, "Cannot allocate socket number for SPX socket"); log_printf(LOG_WARNING, "Socket %d is NOW INCONSISTENT!", sock->fd); - addr_table_unlock(); unlock_sockets(); return -1; } - addr_table_unlock(); - { IPX_STRING_ADDR( addr_s, @@ -2311,6 +2323,25 @@ SOCKET PASCAL accept(SOCKET s, struct sockaddr *addr, int *addrlen) nsock->addr = sock->addr; + /* Duplicate the mutex handle held by the listening + * socket used to detect address collisions. There is no + * way to recover from an error here. + */ + + if(!(DuplicateHandle(GetCurrentProcess(), sock->sock_mut, + GetCurrentProcess(), &(nsock->sock_mut), + 0, FALSE, DUPLICATE_SAME_ACCESS))) + { + log_printf(LOG_ERROR, "Could not duplicate socket mutex: %s", w32_error(GetLastError())); + + closesocket(nsock->fd); + free(nsock); + unlock_sockets(); + + WSASetLastError(WSAENETDOWN); + return -1; + } + /* Copy remote address from the spxinit packet. */ nsock->remote_addr.sa_family = AF_IPX; @@ -2319,7 +2350,6 @@ SOCKET PASCAL accept(SOCKET s, struct sockaddr *addr, int *addrlen) nsock->remote_addr.sa_socket = spxinit.socket; HASH_ADD_INT(sockets, fd, nsock); - addr_table_add(nsock); if(addr) {