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
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 ipx_interface_t *interface_cache = NULL;
static time_t interface_cache_ctime = 0;
static void renew_interface_cache(bool force);
/* Fetch a list of network interfaces available on the system.
*
* 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, "--");
if(ipx_use_pcap)
if(ipx_encap_type == ENCAP_TYPE_PCAP)
{
_init_pcap_interfaces();
}
else{
else if(ipx_encap_type == ENCAP_TYPE_IPXWRAPPER)
{
/* IP interfaces... */
IP_ADAPTER_INFO *ip_ifaces = load_sys_interfaces(), *ip;
@ -559,7 +567,7 @@ void ipx_interfaces_cleanup(void)
{
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)
{
@ -570,12 +578,18 @@ void ipx_interfaces_cleanup(void)
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.
* 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
* use and survives for the lifetime of the program.
@ -583,11 +597,18 @@ static void renew_interface_cache(void)
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);
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);
}
}
@ -599,7 +620,7 @@ ipx_interface_t *get_ipx_interfaces(void)
{
EnterCriticalSection(&interface_cache_cs);
renew_interface_cache();
renew_interface_cache(false);
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);
renew_interface_cache();
renew_interface_cache(false);
ipx_interface_t *iface;
@ -640,7 +661,7 @@ ipx_interface_t *ipx_interface_by_subnet(uint32_t ipaddr)
{
EnterCriticalSection(&interface_cache_cs);
renew_interface_cache();
renew_interface_cache(false);
ipx_interface_t *iface;
@ -671,7 +692,7 @@ ipx_interface_t *ipx_interface_by_index(int index)
{
EnterCriticalSection(&interface_cache_cs);
renew_interface_cache();
renew_interface_cache(false);
int iface_index = 0;
ipx_interface_t *iface;
@ -695,7 +716,7 @@ int ipx_interface_count(void)
{
EnterCriticalSection(&interface_cache_cs);
renew_interface_cache();
renew_interface_cache(false);
int count = 0;
ipx_interface_t *iface;
@ -807,3 +828,19 @@ void ipx_free_pcap_interfaces(ipx_pcap_interface_t **interfaces)
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
* 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
* under the terms of the GNU General Public License version 2 as published by
@ -23,6 +23,7 @@
#include <utlist.h>
#include <pcap.h>
#include "config.h"
#include "common.h"
#ifdef __cplusplus
@ -75,7 +76,19 @@ struct ipx_pcap_interface {
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);
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_cleanup(void);
void ipx_interfaces_reload(void);
ipx_interface_t *get_ipx_interfaces(void);
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);
void ipx_free_pcap_interfaces(ipx_pcap_interface_t **interfaces);
ipx_interface_t *load_dosbox_interfaces(void);
#ifdef __cplusplus
}
#endif

View File

@ -81,7 +81,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
main_config = get_main_config();
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)
{

View File

@ -1,5 +1,5 @@
/* 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
* 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,
* every IPXWrapper instance will share it and use it to receive broadcast
* packets.
*
*
* The private socket uses a randomly allocated UDP port number and is used to
* send packets and receive unicast, it is unique to a single IPXWrapper
* instance.
*
*
* 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
* 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 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);
/* 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. */
@ -66,8 +75,6 @@ static void _init_socket(SOCKET *sock, uint16_t port, BOOL reuseaddr)
/* Enable broadcast and address reuse. */
BOOL broadcast = TRUE;
setsockopt(*sock, SOL_SOCKET, SO_BROADCAST, (char*)&broadcast, sizeof(BOOL));
setsockopt(*sock, SOL_SOCKET, SO_REUSEADDR, (char*)&reuseaddr, sizeof(BOOL));
@ -110,7 +117,7 @@ void router_init(void)
abort();
}
if(ipx_use_pcap)
if(ipx_encap_type == ENCAP_TYPE_PCAP)
{
if((private_socket = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
@ -129,9 +136,29 @@ void router_init(void)
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{
_init_socket(&shared_socket, main_config.udp_port, TRUE);
_init_socket(&private_socket, 0, FALSE);
_init_socket(&shared_socket, main_config.udp_port, TRUE, TRUE);
_init_socket(&private_socket, 0, TRUE, FALSE);
}
router_running = true;
@ -472,6 +499,68 @@ static void _handle_udp_recv(ipx_packet *packet, size_t packet_size, struct sock
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)
{
struct sockaddr_in addr;
@ -489,7 +578,24 @@ static bool _do_udp_recv(int fd)
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;
}
@ -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)));
}
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)
{
DWORD exit_status = 0;
@ -575,7 +704,7 @@ static DWORD router_main(void *arg)
HANDLE *wait_events = &router_event;
int n_events = 1;
if(ipx_use_pcap)
if(ipx_encap_type == ENCAP_TYPE_PCAP)
{
interfaces = get_ipx_interfaces();
ipx_interface_t *i;
@ -613,7 +742,7 @@ static DWORD router_main(void *arg)
break;
}
if(ipx_use_pcap)
if(ipx_encap_type == ENCAP_TYPE_PCAP)
{
ipx_interface_t *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{
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_ipx_interface_list(&interfaces);

View File

@ -1,5 +1,5 @@
/* 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
* 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)
{
if(ipx_use_pcap)
if(ipx_encap_type == ENCAP_TYPE_PCAP)
{
/* TODO: Use real interface MTU */
@ -66,6 +66,11 @@ static int _max_ipx_payload(void)
abort();
}
else if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
// TODO: DOXBox MTU
return 1500;
}
else{
return MAX_DATA_SIZE;
}
@ -331,13 +336,20 @@ SOCKET WSAAPI socket(int af, int type, int protocol)
}
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");
WSASetLastError(WSAEPROTONOSUPPORT);
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)
{
@ -1210,7 +1222,7 @@ static DWORD ipx_send_packet(
(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);
if(iface)
@ -1303,6 +1315,53 @@ static DWORD ipx_send_packet(
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{
int packet_size = sizeof(ipx_packet) - 1 + data_size;