mirror of
https://github.com/solemnwarning/ipxwrapper
synced 2024-12-30 16:45:37 +01:00
Implemented new "address table" code for maintaining address uniqueness between
multiple IPXWrapper processes without needing a shared router thread. Replaced router/rclient code with a single, per-process router thread. Add addresses to the cache in the router main loop rather than winsock recv functions. Packets are no longer preceeded by an rpacket_header structure when relayed to the local UDP sockets. Each IPXWrapper instance creates a "private" UDP socket bound to a random port on INADDR_ANY which is used for sending all packets and receiving unicast. Bugfix: Check source socket number before relaying to a connected socket.
This commit is contained in:
parent
8dc0d0aaa3
commit
920b5ee2c3
3
Makefile
3
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
|
||||
|
416
src/addrtable.c
Normal file
416
src/addrtable.c
Normal file
@ -0,0 +1,416 @@
|
||||
/* IPXWrapper - Address table
|
||||
* Copyright (C) 2008-2012 Daniel Collins <solemnwarning@solemnwarning.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/* The address table is used to co-ordinate the IPX addresses in use accross all
|
||||
* IPXWrapper instances.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
|
||||
#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();
|
||||
}
|
72
src/addrtable.h
Normal file
72
src/addrtable.h
Normal file
@ -0,0 +1,72 @@
|
||||
/* IPXWrapper - Address table
|
||||
* Copyright (C) 2008-2012 Daniel Collins <solemnwarning@solemnwarning.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef ADDRTABLE_H
|
||||
#define ADDRTABLE_H
|
||||
|
||||
#include <wsipx.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#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 */
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
1196
src/router.c
1196
src/router.c
File diff suppressed because it is too large
Load Diff
100
src/router.h
100
src/router.h
@ -23,102 +23,10 @@
|
||||
#include <wsipx.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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 */
|
||||
|
232
src/winsock.c
232
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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user