diff --git a/src/interface.c b/src/interface.c index ec1fbae..7c420a4 100644 --- a/src/interface.c +++ b/src/interface.c @@ -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; +} diff --git a/src/interface.h b/src/interface.h index a6df2ca..6295be4 100644 --- a/src/interface.h +++ b/src/interface.h @@ -1,5 +1,5 @@ /* IPXWrapper - Interface header - * Copyright (C) 2011 Daniel Collins + * Copyright (C) 2011-2021 Daniel Collins * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -23,6 +23,7 @@ #include #include +#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 diff --git a/src/ipxwrapper.c b/src/ipxwrapper.c index d81425c..0e6e630 100644 --- a/src/ipxwrapper.c +++ b/src/ipxwrapper.c @@ -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) { diff --git a/src/router.c b/src/router.c index d3f8955..9d4d4e9 100644 --- a/src/router.c +++ b/src/router.c @@ -1,5 +1,5 @@ /* IPXWrapper - Router code - * Copyright (C) 2011 Daniel Collins + * Copyright (C) 2011-2021 Daniel Collins * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -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*)(®_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); diff --git a/src/winsock.c b/src/winsock.c index e4a6b9d..b39e9bb 100644 --- a/src/winsock.c +++ b/src/winsock.c @@ -1,5 +1,5 @@ /* ipxwrapper - Winsock functions - * Copyright (C) 2008-2014 Daniel Collins + * Copyright (C) 2008-2021 Daniel Collins * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -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;