1
0
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:
Daniel Collins 2012-11-11 15:54:54 +00:00
parent 8dc0d0aaa3
commit 920b5ee2c3
9 changed files with 942 additions and 1157 deletions

View File

@ -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
View 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
View 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 */

View File

@ -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;
}

View File

@ -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();

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -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;