1
0
mirror of https://github.com/solemnwarning/ipxwrapper synced 2024-12-30 16:45:37 +01:00

Initial implementation of DOSBox encapsulation support.

Incomplete and completely untested at this point, beyond "it compiles".
This commit is contained in:
Daniel Collins 2021-08-21 14:42:49 +01:00
parent 0bee989d8a
commit a0234c7459
5 changed files with 282 additions and 33 deletions

View File

@ -29,13 +29,20 @@
#define INTERFACE_CACHE_TTL 5 #define INTERFACE_CACHE_TTL 5
BOOL ipx_use_pcap; enum main_config_encap_type ipx_encap_type;
enum dosbox_state dosbox_state = DOSBOX_DISCONNECTED;
addr32_t dosbox_local_netnum;
addr48_t dosbox_local_nodenum;
static CRITICAL_SECTION interface_cache_cs; static CRITICAL_SECTION interface_cache_cs;
static ipx_interface_t *interface_cache = NULL; static ipx_interface_t *interface_cache = NULL;
static time_t interface_cache_ctime = 0; static time_t interface_cache_ctime = 0;
static void renew_interface_cache(bool force);
/* Fetch a list of network interfaces available on the system. /* Fetch a list of network interfaces available on the system.
* *
* Returns a linked list of IP_ADAPTER_INFO structures, all allocated within a * Returns a linked list of IP_ADAPTER_INFO structures, all allocated within a
@ -465,11 +472,12 @@ void ipx_interfaces_init(void)
log_printf(LOG_INFO, "--"); log_printf(LOG_INFO, "--");
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
_init_pcap_interfaces(); _init_pcap_interfaces();
} }
else{ else if(ipx_encap_type == ENCAP_TYPE_IPXWRAPPER)
{
/* IP interfaces... */ /* IP interfaces... */
IP_ADAPTER_INFO *ip_ifaces = load_sys_interfaces(), *ip; IP_ADAPTER_INFO *ip_ifaces = load_sys_interfaces(), *ip;
@ -559,7 +567,7 @@ void ipx_interfaces_cleanup(void)
{ {
DeleteCriticalSection(&interface_cache_cs); DeleteCriticalSection(&interface_cache_cs);
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
for(ipx_interface_t *i = interface_cache; i; i = i->next) for(ipx_interface_t *i = interface_cache; i; i = i->next)
{ {
@ -570,12 +578,18 @@ void ipx_interfaces_cleanup(void)
free_ipx_interface_list(&interface_cache); free_ipx_interface_list(&interface_cache);
} }
/* Flush and repopulate the interface cache. */
void ipx_interfaces_reload(void)
{
renew_interface_cache(true);
}
/* Check the age of the IPX interface cache and reload it if necessary. /* Check the age of the IPX interface cache and reload it if necessary.
* Ensure you hold interface_cache_cs before calling. * Ensure you hold interface_cache_cs before calling.
*/ */
static void renew_interface_cache(void) static void renew_interface_cache(bool force)
{ {
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
/* interface_cache is initialised during init when pcap is in /* interface_cache is initialised during init when pcap is in
* use and survives for the lifetime of the program. * use and survives for the lifetime of the program.
@ -583,11 +597,18 @@ static void renew_interface_cache(void)
return; return;
} }
if(time(NULL) - interface_cache_ctime > INTERFACE_CACHE_TTL) if(force || time(NULL) - interface_cache_ctime > INTERFACE_CACHE_TTL)
{ {
free_ipx_interface_list(&interface_cache); free_ipx_interface_list(&interface_cache);
interface_cache = load_ipx_interfaces(); if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
interface_cache = load_dosbox_interfaces();
}
else{
interface_cache = load_ipx_interfaces();
}
interface_cache_ctime = time(NULL); interface_cache_ctime = time(NULL);
} }
} }
@ -599,7 +620,7 @@ ipx_interface_t *get_ipx_interfaces(void)
{ {
EnterCriticalSection(&interface_cache_cs); EnterCriticalSection(&interface_cache_cs);
renew_interface_cache(); renew_interface_cache(false);
ipx_interface_t *copy = copy_ipx_interface_list(interface_cache); ipx_interface_t *copy = copy_ipx_interface_list(interface_cache);
@ -615,7 +636,7 @@ ipx_interface_t *ipx_interface_by_addr(addr32_t net, addr48_t node)
{ {
EnterCriticalSection(&interface_cache_cs); EnterCriticalSection(&interface_cache_cs);
renew_interface_cache(); renew_interface_cache(false);
ipx_interface_t *iface; ipx_interface_t *iface;
@ -640,7 +661,7 @@ ipx_interface_t *ipx_interface_by_subnet(uint32_t ipaddr)
{ {
EnterCriticalSection(&interface_cache_cs); EnterCriticalSection(&interface_cache_cs);
renew_interface_cache(); renew_interface_cache(false);
ipx_interface_t *iface; ipx_interface_t *iface;
@ -671,7 +692,7 @@ ipx_interface_t *ipx_interface_by_index(int index)
{ {
EnterCriticalSection(&interface_cache_cs); EnterCriticalSection(&interface_cache_cs);
renew_interface_cache(); renew_interface_cache(false);
int iface_index = 0; int iface_index = 0;
ipx_interface_t *iface; ipx_interface_t *iface;
@ -695,7 +716,7 @@ int ipx_interface_count(void)
{ {
EnterCriticalSection(&interface_cache_cs); EnterCriticalSection(&interface_cache_cs);
renew_interface_cache(); renew_interface_cache(false);
int count = 0; int count = 0;
ipx_interface_t *iface; ipx_interface_t *iface;
@ -807,3 +828,19 @@ void ipx_free_pcap_interfaces(ipx_pcap_interface_t **interfaces)
free(p); free(p);
} }
} }
ipx_interface_t *load_dosbox_interfaces(void)
{
ipx_interface_t *nics = NULL;
if(dosbox_state == DOSBOX_CONNECTED)
{
ipx_interface_t *dosbox_iface = _new_iface(dosbox_local_netnum, dosbox_local_nodenum);
if(dosbox_iface)
{
DL_APPEND(nics, dosbox_iface);
}
}
return nics;
}

View File

@ -1,5 +1,5 @@
/* IPXWrapper - Interface header /* IPXWrapper - Interface header
* Copyright (C) 2011 Daniel Collins <solemnwarning@solemnwarning.net> * Copyright (C) 2011-2021 Daniel Collins <solemnwarning@solemnwarning.net>
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License version 2 as published by
@ -23,6 +23,7 @@
#include <utlist.h> #include <utlist.h>
#include <pcap.h> #include <pcap.h>
#include "config.h"
#include "common.h" #include "common.h"
#ifdef __cplusplus #ifdef __cplusplus
@ -75,7 +76,19 @@ struct ipx_pcap_interface {
ipx_pcap_interface_t *next; ipx_pcap_interface_t *next;
}; };
extern BOOL ipx_use_pcap; extern enum main_config_encap_type ipx_encap_type;
enum dosbox_state
{
DOSBOX_DISCONNECTED,
DOSBOX_RESOLVING,
DOSBOX_REGISTERING,
DOSBOX_CONNECTED,
};
extern enum dosbox_state dosbox_state;
extern addr32_t dosbox_local_netnum;
extern addr48_t dosbox_local_nodenum;
IP_ADAPTER_INFO *load_sys_interfaces(void); IP_ADAPTER_INFO *load_sys_interfaces(void);
ipx_interface_t *load_ipx_interfaces(void); ipx_interface_t *load_ipx_interfaces(void);
@ -88,6 +101,7 @@ void free_ipx_interface_list(ipx_interface_t **list);
void ipx_interfaces_init(void); void ipx_interfaces_init(void);
void ipx_interfaces_cleanup(void); void ipx_interfaces_cleanup(void);
void ipx_interfaces_reload(void);
ipx_interface_t *get_ipx_interfaces(void); ipx_interface_t *get_ipx_interfaces(void);
ipx_interface_t *ipx_interface_by_addr(addr32_t net, addr48_t node); ipx_interface_t *ipx_interface_by_addr(addr32_t net, addr48_t node);
@ -98,6 +112,8 @@ int ipx_interface_count(void);
ipx_pcap_interface_t *ipx_get_pcap_interfaces(void); ipx_pcap_interface_t *ipx_get_pcap_interfaces(void);
void ipx_free_pcap_interfaces(ipx_pcap_interface_t **interfaces); void ipx_free_pcap_interfaces(ipx_pcap_interface_t **interfaces);
ipx_interface_t *load_dosbox_interfaces(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -81,7 +81,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
main_config = get_main_config(); main_config = get_main_config();
min_log_level = main_config.log_level; min_log_level = main_config.log_level;
ipx_use_pcap = main_config.encap_type == ENCAP_TYPE_PCAP; ipx_encap_type = main_config.encap_type;
if(main_config.fw_except) if(main_config.fw_except)
{ {

View File

@ -1,5 +1,5 @@
/* IPXWrapper - Router code /* IPXWrapper - Router code
* Copyright (C) 2011 Daniel Collins <solemnwarning@solemnwarning.net> * Copyright (C) 2011-2021 Daniel Collins <solemnwarning@solemnwarning.net>
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License version 2 as published by
@ -38,23 +38,32 @@ static HANDLE router_thread = NULL;
/* The shared socket uses the UDP port number specified in the configuration, /* The shared socket uses the UDP port number specified in the configuration,
* every IPXWrapper instance will share it and use it to receive broadcast * every IPXWrapper instance will share it and use it to receive broadcast
* packets. * packets.
* *
* The private socket uses a randomly allocated UDP port number and is used to * 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 * send packets and receive unicast, it is unique to a single IPXWrapper
* instance. * instance.
* *
* When running in WinPcap mode, only the private socket will be opened and it * When running in WinPcap mode, only the private socket will be opened and it
* will be bound to loopback rather than INADDR_ANY since it is only used for * will be bound to loopback rather than INADDR_ANY since it is only used for
* forwarding IPX packets on to local sockets. * forwarding IPX packets on to local sockets.
*
* When running in DOSBox mode, only the private socket will be opened and it
* will be "connected" to the DOSBox server address.
*/ */
SOCKET shared_socket = -1; SOCKET shared_socket = -1;
SOCKET private_socket = -1; SOCKET private_socket = -1;
#define DOSBOX_CONNECT_TIMEOUT_SECS 10
static struct sockaddr_in dosbox_server_addr;
static time_t dosbox_connect_begin;
static void _send_dosbox_registration_request(void);
static DWORD router_main(void *arg); static DWORD router_main(void *arg);
/* Initialise a UDP socket. */ /* Initialise a UDP socket. */
static void _init_socket(SOCKET *sock, uint16_t port, BOOL reuseaddr) static void _init_socket(SOCKET *sock, uint16_t port, BOOL broadcast, BOOL reuseaddr)
{ {
/* Socket used for sending and receiving packets on the network. */ /* Socket used for sending and receiving packets on the network. */
@ -66,8 +75,6 @@ static void _init_socket(SOCKET *sock, uint16_t port, BOOL reuseaddr)
/* Enable broadcast and address reuse. */ /* Enable broadcast and address reuse. */
BOOL broadcast = TRUE;
setsockopt(*sock, SOL_SOCKET, SO_BROADCAST, (char*)&broadcast, sizeof(BOOL)); setsockopt(*sock, SOL_SOCKET, SO_BROADCAST, (char*)&broadcast, sizeof(BOOL));
setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseaddr, sizeof(BOOL)); setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseaddr, sizeof(BOOL));
@ -110,7 +117,7 @@ void router_init(void)
abort(); abort();
} }
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
if((private_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) if((private_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{ {
@ -129,9 +136,29 @@ void router_init(void)
abort(); abort();
} }
} }
else if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
/* TODO: Support DNS. Do this async somewhere within router_main. */
dosbox_server_addr.sin_family = AF_INET;
dosbox_server_addr.sin_addr.s_addr = inet_addr(main_config.dosbox_server_addr);
dosbox_server_addr.sin_port = htons(main_config.dosbox_server_port);
if(connect(private_socket, (struct sockaddr*)(&dosbox_server_addr), sizeof(dosbox_server_addr)) != 0)
{
log_printf(LOG_ERROR, "Error connecting private socket: %s", w32_error(WSAGetLastError()));
abort();
}
_send_dosbox_registration_request();
dosbox_state = DOSBOX_REGISTERING;
_init_socket(&private_socket, 0, FALSE, FALSE);
}
else{ else{
_init_socket(&shared_socket, main_config.udp_port, TRUE); _init_socket(&shared_socket, main_config.udp_port, TRUE, TRUE);
_init_socket(&private_socket, 0, FALSE); _init_socket(&private_socket, 0, TRUE, FALSE);
} }
router_running = true; router_running = true;
@ -472,6 +499,68 @@ static void _handle_udp_recv(ipx_packet *packet, size_t packet_size, struct sock
data_size); data_size);
} }
static void _handle_dosbox_registration_response(novell_ipx_packet *packet, size_t packet_size)
{
if(packet_size < sizeof(novell_ipx_packet)
|| ntohs(packet->length) != packet_size
|| ntohs(packet->checksum) != 0xFFFF
|| ntohs(packet->type) != 2)
{
/* Doesn't look valid. */
log_printf(LOG_ERROR, "Got invalid registration response from DOSBox server!");
abort();
}
dosbox_local_netnum = addr32_in(packet->dest_net);
dosbox_local_nodenum = addr48_in(packet->dest_node);
dosbox_state = DOSBOX_CONNECTED;
ipx_interfaces_reload();
char local_netnum_s[ADDR32_STRING_SIZE];
addr32_string(local_netnum_s, dosbox_local_netnum);
char local_nodenum_s[ADDR48_STRING_SIZE];
addr48_string(local_nodenum_s, dosbox_local_nodenum);
log_printf(LOG_INFO, "Connected to DOSBox server, local address: %s/%s", local_netnum_s, local_nodenum_s);
}
static void _handle_dosbox_recv(novell_ipx_packet *packet, size_t packet_size)
{
if(packet_size < sizeof(novell_ipx_packet) || ntohs(packet->length) != packet_size)
{
/* Doesn't look valid. */
log_printf(LOG_ERROR, "Recieved invalid IPX packet from DOSBox server, dropping");
return;
}
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 for %s", src_addr, dest_addr);
}
size_t data_size = ntohs(packet->length) - sizeof(novell_ipx_packet);
_deliver_packet(
packet->type,
addr32_in(packet->src_net),
addr48_in(packet->src_node),
packet->src_socket,
addr32_in(packet->dest_net),
addr48_in(packet->dest_node),
packet->dest_socket,
packet->data,
data_size);
}
static bool _do_udp_recv(int fd) static bool _do_udp_recv(int fd)
{ {
struct sockaddr_in addr; struct sockaddr_in addr;
@ -489,7 +578,24 @@ static bool _do_udp_recv(int fd)
return false; return false;
} }
_handle_udp_recv((ipx_packet*)(buf), len, addr); if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
if(dosbox_state == DOSBOX_REGISTERING)
{
_handle_dosbox_registration_response((novell_ipx_packet*)(buf), len);
}
else if(dosbox_state == DOSBOX_CONNECTED)
{
_handle_dosbox_recv((novell_ipx_packet*)(buf), len);
}
else{
/* Unreachable. */
abort();
}
}
else{
_handle_udp_recv((ipx_packet*)(buf), len, addr);
}
return true; return true;
} }
@ -566,6 +672,29 @@ static void _handle_pcap_frame(u_char *user, const struct pcap_pkthdr *pkt_heade
(ntohs(ipx->length) - sizeof(novell_ipx_packet))); (ntohs(ipx->length) - sizeof(novell_ipx_packet)));
} }
static void _send_dosbox_registration_request(void)
{
novell_ipx_packet reg_pkt;
reg_pkt.checksum = 0xFFFF;
reg_pkt.length = htons(sizeof(novell_ipx_packet));
reg_pkt.hops = 0;
reg_pkt.type = 2;
memset(reg_pkt.dest_net, 0, sizeof(reg_pkt.dest_net));
memset(reg_pkt.dest_node, 0, sizeof(reg_pkt.dest_node));
reg_pkt.dest_socket = 0;
memset(reg_pkt.src_net, 0, sizeof(reg_pkt.src_net));
memset(reg_pkt.src_node, 0, sizeof(reg_pkt.src_node));
reg_pkt.src_socket = 0;
if(send(private_socket, (const void*)(&reg_pkt), sizeof(reg_pkt), 0) < 0)
{
log_printf(LOG_ERROR, "Error sending DOSBox IPX registration request: %s", w32_error(WSAGetLastError()));
}
}
static DWORD router_main(void *arg) static DWORD router_main(void *arg)
{ {
DWORD exit_status = 0; DWORD exit_status = 0;
@ -575,7 +704,7 @@ static DWORD router_main(void *arg)
HANDLE *wait_events = &router_event; HANDLE *wait_events = &router_event;
int n_events = 1; int n_events = 1;
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
interfaces = get_ipx_interfaces(); interfaces = get_ipx_interfaces();
ipx_interface_t *i; ipx_interface_t *i;
@ -613,7 +742,7 @@ static DWORD router_main(void *arg)
break; break;
} }
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
ipx_interface_t *i; ipx_interface_t *i;
DL_FOREACH(interfaces, i) DL_FOREACH(interfaces, i)
@ -628,6 +757,14 @@ static DWORD router_main(void *arg)
} }
} }
} }
else if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
if(!_do_udp_recv(private_socket))
{
exit_status = 1;
break;
}
}
else{ else{
if(!_do_udp_recv(shared_socket) || !_do_udp_recv(private_socket)) if(!_do_udp_recv(shared_socket) || !_do_udp_recv(private_socket))
{ {
@ -637,7 +774,7 @@ static DWORD router_main(void *arg)
} }
} }
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
free(wait_events); free(wait_events);
free_ipx_interface_list(&interfaces); free_ipx_interface_list(&interfaces);

View File

@ -1,5 +1,5 @@
/* ipxwrapper - Winsock functions /* ipxwrapper - Winsock functions
* Copyright (C) 2008-2014 Daniel Collins <solemnwarning@solemnwarning.net> * Copyright (C) 2008-2021 Daniel Collins <solemnwarning@solemnwarning.net>
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License version 2 as published by
@ -50,7 +50,7 @@ static size_t strsize(void *str, bool unicode)
static int _max_ipx_payload(void) static int _max_ipx_payload(void)
{ {
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
/* TODO: Use real interface MTU */ /* TODO: Use real interface MTU */
@ -66,6 +66,11 @@ static int _max_ipx_payload(void)
abort(); abort();
} }
else if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
// TODO: DOXBox MTU
return 1500;
}
else{ else{
return MAX_DATA_SIZE; return MAX_DATA_SIZE;
} }
@ -331,13 +336,20 @@ SOCKET WSAAPI socket(int af, int type, int protocol)
} }
else if(type == SOCK_STREAM) else if(type == SOCK_STREAM)
{ {
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
log_printf(LOG_WARNING, "Application attempted to create an SPX socket, this isn't supported when using Ethernet encapsulation"); log_printf(LOG_WARNING, "Application attempted to create an SPX socket, this isn't supported when using Ethernet encapsulation");
WSASetLastError(WSAEPROTONOSUPPORT); WSASetLastError(WSAEPROTONOSUPPORT);
return -1; return -1;
} }
else if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
log_printf(LOG_WARNING, "Application attempted to create an SPX socket, this isn't supported when using DOSBox encapsulation");
WSASetLastError(WSAEPROTONOSUPPORT);
return -1;
}
if(protocol != 0 && protocol != NSPROTO_SPX && protocol != NSPROTO_SPXII) if(protocol != 0 && protocol != NSPROTO_SPX && protocol != NSPROTO_SPXII)
{ {
@ -1210,7 +1222,7 @@ static DWORD ipx_send_packet(
(unsigned int)(data_size), src_addr, dest_addr); (unsigned int)(data_size), src_addr, dest_addr);
} }
if(ipx_use_pcap) if(ipx_encap_type == ENCAP_TYPE_PCAP)
{ {
ipx_interface_t *iface = ipx_interface_by_addr(src_net, src_node); ipx_interface_t *iface = ipx_interface_by_addr(src_net, src_node);
if(iface) if(iface)
@ -1303,6 +1315,53 @@ static DWORD ipx_send_packet(
return WSAENETDOWN; return WSAENETDOWN;
} }
} }
else if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
if(dosbox_state != DOSBOX_CONNECTED)
{
return WSAENETDOWN;
}
else if(src_net != dosbox_local_netnum || src_node != dosbox_local_nodenum)
{
return WSAENETDOWN;
}
else{
size_t packet_size = sizeof(novell_ipx_packet) + data_size;
novell_ipx_packet *packet = malloc(packet_size);
if(packet == NULL)
{
return ERROR_OUTOFMEMORY;
}
packet->checksum = 0xFFFF;
packet->length = htons(sizeof(novell_ipx_packet) + data_size);
packet->hops = 0;
packet->type = type;
addr32_out(packet->dest_net, dest_net);
addr48_out(packet->dest_node, dest_node);
packet->dest_socket = dest_socket;
addr32_out(packet->src_net, src_net);
addr48_out(packet->src_node, src_node);
packet->src_socket = src_socket;
memcpy(packet->data, data, data_size);
DWORD error = ERROR_SUCCESS;
if(r_send(private_socket, (const void*)(packet), packet_size, 0) < 0)
{
error = WSAGetLastError();
log_printf(LOG_ERROR, "Error sending DOSBox IPX packet: %s", w32_error(error));
}
free(packet);
return error;
}
}
else{ else{
int packet_size = sizeof(ipx_packet) - 1 + data_size; int packet_size = sizeof(ipx_packet) - 1 + data_size;