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

Support DOSBox servers using DNS names.

This commit is contained in:
Daniel Collins 2023-09-13 00:24:08 +01:00
parent a91e05c11b
commit 435df05496
6 changed files with 202 additions and 65 deletions

View File

@ -1,5 +1,5 @@
/* IPXWrapper - Interface functions /* IPXWrapper - Interface functions
* Copyright (C) 2011-2021 Daniel Collins <solemnwarning@solemnwarning.net> * Copyright (C) 2011-2023 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
@ -24,6 +24,7 @@
#include <pcap.h> #include <pcap.h>
#include "interface.h" #include "interface.h"
#include "ipxwrapper.h"
#include "common.h" #include "common.h"
#include "config.h" #include "config.h"
@ -523,6 +524,12 @@ void ipx_interfaces_init(void)
free(ip_ifaces); free(ip_ifaces);
} }
if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
log_printf(LOG_INFO, "Using DOSBox server: %s port %hu",
main_config.dosbox_server_addr, main_config.dosbox_server_port);
}
else{
/* Virtual IPX interfaces... */ /* Virtual IPX interfaces... */
log_printf(LOG_INFO, "Listing IPX interfaces:"); log_printf(LOG_INFO, "Listing IPX interfaces:");
@ -560,6 +567,7 @@ void ipx_interfaces_init(void)
} }
free_ipx_interface_list(&ipx_root); free_ipx_interface_list(&ipx_root);
}
} }
/* Release any resources used by the IPX interface cache. */ /* Release any resources used by the IPX interface cache. */

View File

@ -1,5 +1,5 @@
/* IPXWrapper - Interface header /* IPXWrapper - Interface header
* Copyright (C) 2011-2021 Daniel Collins <solemnwarning@solemnwarning.net> * Copyright (C) 2011-2023 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
@ -81,7 +81,7 @@ extern enum main_config_encap_type ipx_encap_type;
enum dosbox_state enum dosbox_state
{ {
DOSBOX_DISCONNECTED, DOSBOX_DISCONNECTED,
DOSBOX_RESOLVING, /* DOSBOX_RESOLVING, */
DOSBOX_REGISTERING, DOSBOX_REGISTERING,
DOSBOX_CONNECTED, DOSBOX_CONNECTED,
}; };

View File

@ -35,3 +35,4 @@ r_getpeername:4
inet_ntoa:4 inet_ntoa:4
__WSAFDIsSet:4 __WSAFDIsSet:4
r_WSAAsyncSelect:4 r_WSAAsyncSelect:4
gethostbyname:4

View File

@ -57,17 +57,20 @@ static HANDLE router_thread = NULL;
SOCKET shared_socket = -1; SOCKET shared_socket = -1;
SOCKET private_socket = -1; SOCKET private_socket = -1;
#define DOSBOX_CONNECT_TIMEOUT_SECS 10
struct sockaddr_in dosbox_server_addr; struct sockaddr_in dosbox_server_addr;
static time_t dosbox_connect_begin;
static HANDLE dosbox_ready_event = NULL; static HANDLE dosbox_ready_event = NULL;
static int dosbox_next_registration_request_at; static uint64_t dosbox_next_connection_attempt_at;
static int dosbox_registration_retry_interval_ms; static const unsigned int dosbox_connect_retry_interval_ms = 10000;
static const int INITIAL_DOSBOX_REGISTRATION_RETRY_INTERVAL_MS = 250; static uint64_t dosbox_next_registration_request_at;
static const int MAX_DOSBOX_REGISTRATION_RETRY_INTERVAL_MS = 8000; static unsigned int dosbox_registration_retry_interval_ms;
static uint64_t dosbox_registration_timeout_at;
static const unsigned int INITIAL_DOSBOX_REGISTRATION_RETRY_INTERVAL_MS = 250;
static const unsigned int MAX_DOSBOX_REGISTRATION_RETRY_INTERVAL_MS = 8000;
static const unsigned int DOSBOX_REGISTRATION_TIMEOUT_MS = 30000;
static void _send_dosbox_registration_request(void); static void _send_dosbox_registration_request(void);
static DWORD router_main(void *arg); static DWORD router_main(void *arg);
@ -155,18 +158,9 @@ void router_init(void)
abort(); abort();
} }
/* TODO: Support DNS. Do this async somewhere within router_main. */
_init_socket(&private_socket, 0, FALSE, FALSE); _init_socket(&private_socket, 0, FALSE, FALSE);
dosbox_server_addr.sin_family = AF_INET; dosbox_next_connection_attempt_at = 0;
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);
dosbox_next_registration_request_at = 0;
dosbox_registration_retry_interval_ms = INITIAL_DOSBOX_REGISTRATION_RETRY_INTERVAL_MS;
dosbox_state = DOSBOX_REGISTERING;
} }
else{ else{
_init_socket(&shared_socket, main_config.udp_port, TRUE, TRUE); _init_socket(&shared_socket, main_config.udp_port, TRUE, TRUE);
@ -519,8 +513,7 @@ static void _handle_dosbox_registration_response(novell_ipx_packet *packet, size
/* || packet->type != 2) */ /* || packet->type != 2) */
{ {
/* Doesn't look valid. */ /* Doesn't look valid. */
log_printf(LOG_ERROR, "Got invalid registration response from DOSBox server!"); log_printf(LOG_ERROR, "Got invalid registration response from DOSBox server, ignoring");
abort();
} }
dosbox_local_netnum = addr32_in(packet->dest_net); dosbox_local_netnum = addr32_in(packet->dest_net);
@ -546,7 +539,7 @@ 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) if(packet_size < sizeof(novell_ipx_packet) || ntohs(packet->length) != packet_size)
{ {
/* Doesn't look valid. */ /* Doesn't look valid. */
log_printf(LOG_ERROR, "Recieved invalid IPX packet from DOSBox server, dropping"); log_printf(LOG_ERROR, "Recieved invalid IPX packet from DOSBox server, ignoring");
return; return;
} }
@ -758,7 +751,22 @@ static DWORD router_main(void *arg)
{ {
DWORD wait_ms = 1000; DWORD wait_ms = 1000;
if(ipx_encap_type == ENCAP_TYPE_DOSBOX && dosbox_state == DOSBOX_REGISTERING) if(ipx_encap_type == ENCAP_TYPE_DOSBOX)
{
if(dosbox_state == DOSBOX_DISCONNECTED)
{
uint64_t now = get_ticks();
if(now >= dosbox_next_connection_attempt_at)
{
wait_ms = 0;
}
else{
wait_ms = dosbox_next_connection_attempt_at - now;
wait_ms = min(wait_ms, 1000);
}
}
else if(dosbox_state == DOSBOX_REGISTERING)
{ {
uint64_t now = get_ticks(); uint64_t now = get_ticks();
@ -767,11 +775,8 @@ static DWORD router_main(void *arg)
wait_ms = 0; wait_ms = 0;
} }
else{ else{
wait_ms = dosbox_next_registration_request_at - 1; wait_ms = dosbox_next_registration_request_at - now;
wait_ms = min(wait_ms, 1000);
if(wait_ms > 1000)
{
wait_ms = 1000;
} }
} }
} }
@ -815,10 +820,48 @@ static DWORD router_main(void *arg)
} }
} }
if(ipx_encap_type == ENCAP_TYPE_DOSBOX && dosbox_state == DOSBOX_DISCONNECTED)
{
if(get_ticks() >= dosbox_next_connection_attempt_at)
{
struct hostent *host = gethostbyname(main_config.dosbox_server_addr);
uint64_t now = get_ticks();
if(host != NULL)
{
dosbox_server_addr.sin_family = AF_INET;
memcpy(&(dosbox_server_addr.sin_addr), host->h_addr, 4);
dosbox_server_addr.sin_port = htons(main_config.dosbox_server_port);
log_printf(LOG_INFO, "Resolved DOSBox server address %s, connecting...\n", inet_ntoa(dosbox_server_addr.sin_addr));
dosbox_next_registration_request_at = 0;
dosbox_registration_retry_interval_ms = INITIAL_DOSBOX_REGISTRATION_RETRY_INTERVAL_MS;
dosbox_registration_timeout_at = now + DOSBOX_REGISTRATION_TIMEOUT_MS;
dosbox_state = DOSBOX_REGISTERING;
}
else{
DWORD error = WSAGetLastError();
log_printf(LOG_ERROR, "Error resolving %s: %s (%u)",
main_config.dosbox_server_addr, w32_error(error), (unsigned int)(error));
dosbox_next_connection_attempt_at = now + dosbox_connect_retry_interval_ms;
}
}
}
if(ipx_encap_type == ENCAP_TYPE_DOSBOX && dosbox_state == DOSBOX_REGISTERING) if(ipx_encap_type == ENCAP_TYPE_DOSBOX && dosbox_state == DOSBOX_REGISTERING)
{ {
uint64_t now = get_ticks(); uint64_t now = get_ticks();
if(now >= dosbox_registration_timeout_at)
{
log_printf(LOG_ERROR, "Connection to DOSBox server %s timed out", inet_ntoa(dosbox_server_addr.sin_addr));
dosbox_state = DOSBOX_DISCONNECTED;
}
if(now >= dosbox_next_registration_request_at) if(now >= dosbox_next_registration_request_at)
{ {
_send_dosbox_registration_request(); _send_dosbox_registration_request();

View File

@ -266,10 +266,10 @@ describe "IPXWrapper" => sub
}; };
}; };
describe "using DOSBox UDP encapsulation" => sub
{
my $dosbox_server; my $dosbox_server;
describe "using DOSBox UDP encapsulation (via IP address)" => sub
{
before all => sub before all => sub
{ {
reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper"); reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper");
@ -277,6 +277,7 @@ describe "IPXWrapper" => sub
reg_set_string($remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_addr", $local_ip_a); reg_set_string($remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_addr", $local_ip_a);
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_port", $dosbox_port); reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_port", $dosbox_port);
$dosbox_server = undef;
$dosbox_server = IPXWrapper::DOSBoxServer->new($dosbox_port); $dosbox_server = IPXWrapper::DOSBoxServer->new($dosbox_port);
@expected_addrs = ( @expected_addrs = (
@ -316,6 +317,90 @@ describe "IPXWrapper" => sub
}; };
}; };
}; };
describe "using DOSBox UDP encapsulation (via DNS name)" => sub
{
before all => sub
{
reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper");
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "use_pcap", ENCAP_TYPE_DOSBOX);
reg_set_string($remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_addr", "dosbox-ipv4.com");
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_port", $dosbox_port);
$dosbox_server = undef;
$dosbox_server = IPXWrapper::DOSBoxServer->new($dosbox_port);
@expected_addrs = (
{
# The node number is randomly selected by the DOSBox server
# when each client connects.
net => "00:00:00:00",
maxpkt => DOSBOX_MAX_DATA_SIZE,
},
);
};
after all => sub
{
$dosbox_server = undef;
};
# Duplicate of common getsockopt block to skip the default interface selection
# logic test (because there is only ever one interface here).
describe "getsockopt" => sub
{
it "returns correct addresses" => sub
{
my @addrs = getsockopt_interfaces($remote_ip_a);
cmp_hashes_partial(\@addrs, \@expected_addrs);
};
it "returns correct IPX_MAX_ADAPTER_NUM" => sub
{
my @addrs = getsockopt_interfaces($remote_ip_a);
my $output = run_remote_cmd($remote_ip_a, "Z:\\tools\\list-interfaces.exe");
my ($got_num) = ($output =~ m/^IPX_MAX_ADAPTER_NUM = (\d+)$/m);
is($got_num, (scalar @addrs));
};
};
};
describe "using DOSBox UDP encapsulation (server is down)" => sub
{
before all => sub
{
reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper");
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "use_pcap", ENCAP_TYPE_DOSBOX);
reg_set_string($remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_addr", $local_ip_a);
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_port", $dosbox_port);
$dosbox_server = undef;
};
# Duplicate of common getsockopt block to skip the default interface selection
# logic test (because there is only ever one interface here).
describe "getsockopt" => sub
{
it "returns no addresses" => sub
{
my @addrs = getsockopt_interfaces($remote_ip_a);
cmp_deeply(\@addrs, []);
};
it "returns correct IPX_MAX_ADAPTER_NUM" => sub
{
my @addrs = getsockopt_interfaces($remote_ip_a);
my $output = run_remote_cmd($remote_ip_a, "Z:\\tools\\list-interfaces.exe");
my ($got_num) = ($output =~ m/^IPX_MAX_ADAPTER_NUM = (\d+)$/m);
is($got_num, 0);
};
};
};
}; };
sub get_first_addr_node sub get_first_addr_node

View File

@ -45,7 +45,7 @@ describe "IPXWrapper using DOSBox UDP encapsulation" => sub
{ {
reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper"); reg_delete_key($remote_ip_a, "HKCU\\Software\\IPXWrapper");
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "use_pcap", ENCAP_TYPE_DOSBOX); reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "use_pcap", ENCAP_TYPE_DOSBOX);
reg_set_string($remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_addr", $local_ip_a); reg_set_string($remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_addr", "dosbox-ipv4.com");
reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_port", $dosbox_port); reg_set_dword( $remote_ip_a, "HKCU\\Software\\IPXWrapper", "dosbox_server_port", $dosbox_port);
}; };