(Cthulhu88) Netplay - initial netplay tunnel

This commit is contained in:
twinaphex 2021-11-11 04:55:37 +01:00
parent 82b8c0a60e
commit 369cafe1ad
2 changed files with 174 additions and 86 deletions

View File

@ -39,6 +39,9 @@
#define NETPLAY_HOST_STR_LEN 32 #define NETPLAY_HOST_STR_LEN 32
#define NETPLAY_HOST_LONGSTR_LEN 256 #define NETPLAY_HOST_LONGSTR_LEN 256
#define NETPLAY_MITM_MAX_PENDING 8
#define NETPLAY_MITM_ID_SIZE 16
enum rarch_netplay_ctl_state enum rarch_netplay_ctl_state
{ {
RARCH_NETPLAY_CTL_NONE = 0, RARCH_NETPLAY_CTL_NONE = 0,
@ -185,6 +188,16 @@ struct netplay_host_list
size_t size; size_t size;
}; };
struct netplay_mitm_pending
{
int *fds;
uint8_t *ids[NETPLAY_MITM_ID_SIZE];
retro_time_t *timeouts;
int current;
int next;
};
typedef struct typedef struct
{ {
netplay_t *data; /* Used while Netplay is running */ netplay_t *data; /* Used while Netplay is running */
@ -210,6 +223,8 @@ typedef struct
bool in_netplay; bool in_netplay;
bool netplay_client_deferred; bool netplay_client_deferred;
bool is_mitm; bool is_mitm;
uint8_t mitm_id[NETPLAY_MITM_ID_SIZE];
struct netplay_mitm_pending mitm_pending;
bool has_set_netplay_mode; bool has_set_netplay_mode;
bool has_set_netplay_ip_address; bool has_set_netplay_ip_address;
bool has_set_netplay_ip_port; bool has_set_netplay_ip_port;

View File

@ -5759,8 +5759,7 @@ void netplay_init_nat_traversal(netplay_t *netplay)
} }
static int init_tcp_connection(const struct addrinfo *res, static int init_tcp_connection(const struct addrinfo *res,
bool server, bool is_server, bool use_mitm)
struct sockaddr *other_addr, socklen_t addr_size)
{ {
#ifndef HAVE_SOCKET_LEGACY #ifndef HAVE_SOCKET_LEGACY
char msg[512]; char msg[512];
@ -5788,7 +5787,7 @@ static int init_tcp_connection(const struct addrinfo *res,
RARCH_WARN("Cannot set Netplay port to close-on-exec. It may fail to reopen if the client disconnects.\n"); RARCH_WARN("Cannot set Netplay port to close-on-exec. It may fail to reopen if the client disconnects.\n");
#endif #endif
if (server) if (!is_server)
{ {
if (!socket_connect(fd, (void*)res, false)) if (!socket_connect(fd, (void*)res, false))
return fd; return fd;
@ -5809,34 +5808,101 @@ static int init_tcp_connection(const struct addrinfo *res,
} }
else else
{ {
#if defined(HAVE_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY) #ifdef __NETPLAY_MITM_NEW
/* Make sure we accept connections on both IPv6 and IPv4 */ if (use_mitm)
if (res->ai_family == AF_INET6)
{ {
int on = 0; if (!socket_connect(fd, (void*)res, false))
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, {
(const char*)&on, sizeof(on)) < 0) net_driver_state_t *net_st = &networking_driver_st;
RARCH_WARN("Failed to listen on both IPv6 and IPv4\n");
} /* Relay server should provide us with our session ID. */
if (socket_receive_all_blocking(fd, net_st->mitm_id, sizeof(net_st->mitm_id)))
{
/* Initialize data for handling tunneled client connections. */
int *fds;
uint8_t *ids;
retro_time_t *timeouts;
fds = malloc(
NETPLAY_MITM_MAX_PENDING * sizeof(*fds));
ids = malloc(
NETPLAY_MITM_MAX_PENDING * NETPLAY_MITM_ID_SIZE * sizeof(*ids));
timeouts = malloc(
NETPLAY_MITM_MAX_PENDING * sizeof(*timeouts));
if (fds && ids && timeouts)
{
memset(fds, -1,
NETPLAY_MITM_MAX_PENDING * sizeof(*fds));
net_st->mitm_pending.fds = fds;
net_st->mitm_pending.ids = ids;
net_st->mitm_pending.timeouts = timeouts;
net_st->mitm_pending.current = 0;
net_st->mitm_pending.next = -1;
return fd;
}
free(fds);
free(ids);
free(timeouts);
}
dmsg = "Failed to create a tunnel session.";
}
else
{
#ifndef HAVE_SOCKET_LEGACY
if (!getnameinfo(res->ai_addr, res->ai_addrlen,
host, sizeof(host), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV))
{
snprintf(msg, sizeof(msg),
"Failed to connect to relay server %s on port %s.",
host, port);
dmsg = msg;
}
#else
dmsg = "Failed to connect to relay server.";
#endif #endif
if (socket_bind(fd, (void*)res)) }
{
if (!listen(fd, 1024))
return fd;
} }
else else
{ {
#ifndef HAVE_SOCKET_LEGACY
if (!getnameinfo(res->ai_addr, res->ai_addrlen,
NULL, 0, port, sizeof(port), NI_NUMERICSERV))
{
snprintf(msg, sizeof(msg), "Failed to bind port %s.", port);
dmsg = msg;
}
#else
dmsg = "Failed to bind port.";
#endif #endif
#if defined(HAVE_INET6) && defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
/* Make sure we accept connections on both IPv6 and IPv4 */
if (res->ai_family == AF_INET6)
{
int on = 0;
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
(const char*)&on, sizeof(on)) < 0)
RARCH_WARN("Failed to listen on both IPv6 and IPv4\n");
}
#endif
if (socket_bind(fd, (void*)res))
{
if (!listen(fd, 1024))
return fd;
}
else
{
#ifndef HAVE_SOCKET_LEGACY
if (!getnameinfo(res->ai_addr, res->ai_addrlen,
NULL, 0, port, sizeof(port), NI_NUMERICSERV))
{
snprintf(msg, sizeof(msg), "Failed to bind port %s.", port);
dmsg = msg;
}
#else
dmsg = "Failed to bind port.";
#endif
}
#ifdef __NETPLAY_MITM_NEW
} }
#endif
} }
socket_close(fd); socket_close(fd);
@ -5847,20 +5913,19 @@ static int init_tcp_connection(const struct addrinfo *res,
return -1; return -1;
} }
static bool init_tcp_socket(netplay_t *netplay, void *direct_host, static bool init_tcp_socket(netplay_t *netplay,
const char *server, uint16_t port) const char *server, const char *mitm, uint16_t port)
{ {
struct addrinfo *res; struct addrinfo *res;
const struct addrinfo *tmp_info; const struct addrinfo *tmp_info;
struct sockaddr_storage sad; char port_buf[6];
struct addrinfo hints = {0}; struct addrinfo hints = {0};
bool use_mitm = !server && mitm;
int fd = -1; int fd = -1;
if (!direct_host) if (!server)
{ {
char port_buf[6]; if (!use_mitm)
snprintf(port_buf, sizeof(port_buf), "%hu", port);
if (!server)
{ {
hints.ai_flags = AI_PASSIVE; hints.ai_flags = AI_PASSIVE;
#ifdef HAVE_INET6 #ifdef HAVE_INET6
@ -5870,62 +5935,58 @@ static bool init_tcp_socket(netplay_t *netplay, void *direct_host,
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
#endif #endif
} }
hints.ai_socktype = SOCK_STREAM; else
{
if (getaddrinfo_retro(server, port_buf, &hints, &res)) /* IPv4 only for relay servers. */
hints.ai_family = AF_INET;
}
}
hints.ai_socktype = SOCK_STREAM;
snprintf(port_buf, sizeof(port_buf), "%hu", port);
if (getaddrinfo_retro(use_mitm ? mitm : server, port_buf,
&hints, &res))
{
if (!server && !use_mitm)
{ {
if (!server)
{
#ifdef HAVE_INET6 #ifdef HAVE_INET6
try_ipv4: try_ipv4:
/* Didn't work with IPv6, try IPv4 */ /* Didn't work with IPv6, try IPv4 */
hints.ai_family = AF_INET; hints.ai_family = AF_INET;
if (getaddrinfo_retro(server, port_buf, &hints, &res)) if (getaddrinfo_retro(server, port_buf, &hints, &res))
#endif #endif
{
RARCH_ERR("Failed to set a hosting address.\n");
return false;
}
}
else
{ {
char msg[512]; RARCH_ERR("Failed to set a hosting address.\n");
snprintf(msg, sizeof(msg),
"Failed to resolve host: %s\n", server);
RARCH_ERR(msg);
return false; return false;
} }
} }
else
if (!res)
return false;
/* If we're serving on IPv6, make sure we accept all connections, including
* IPv4 */
#ifdef HAVE_INET6
if (!server && res->ai_family == AF_INET6)
{ {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) res->ai_addr; char msg[512];
#if defined(_MSC_VER) && _MSC_VER <= 1200 snprintf(msg, sizeof(msg),
IN6ADDR_SETANY(sin6); "Failed to resolve host: %s\n", use_mitm ? mitm : server);
#else RARCH_ERR(msg);
sin6->sin6_addr = in6addr_any; return false;
#endif
} }
}
if (!res)
return false;
/* If we're serving on IPv6, make sure we accept all connections, including
* IPv4 */
#ifdef HAVE_INET6
if (!server && !use_mitm && res->ai_family == AF_INET6)
{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) res->ai_addr;
#if defined(_MSC_VER) && _MSC_VER <= 1200
IN6ADDR_SETANY(sin6);
#else
sin6->sin6_addr = in6addr_any;
#endif #endif
} }
else #endif
{
/* I'll build my own addrinfo! */
struct netplay_host *host = (struct netplay_host *)direct_host;
hints.ai_family = host->addr.sa_family;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_addrlen = host->addrlen;
hints.ai_addr = &host->addr;
res = &hints;
}
/* If "localhost" is used, it is important to check every possible /* If "localhost" is used, it is important to check every possible
* address for IPv4/IPv6. */ * address for IPv4/IPv6. */
@ -5933,34 +5994,31 @@ try_ipv4:
do do
{ {
fd = init_tcp_connection( fd = init_tcp_connection(tmp_info, !server, use_mitm);
tmp_info, direct_host || server,
(struct sockaddr *)
memset(&sad, 0, sizeof(sad)), sizeof(sad));
if (fd >= 0) if (fd >= 0)
break; break;
} while ((tmp_info = tmp_info->ai_next)); } while ((tmp_info = tmp_info->ai_next));
if (!direct_host) freeaddrinfo_retro(res);
freeaddrinfo_retro(res);
res = NULL; res = NULL;
if (fd < 0) if (fd < 0)
{ {
#ifdef HAVE_INET6 #ifdef HAVE_INET6
if (!direct_host && !server && hints.ai_family == AF_INET6) if (!server && !use_mitm && hints.ai_family == AF_INET6)
goto try_ipv4; goto try_ipv4;
#endif #endif
RARCH_ERR("Failed to set up netplay sockets.\n"); RARCH_ERR("Failed to set up netplay sockets.\n");
return false; return false;
} }
if (direct_host || server) if (server)
{ {
netplay->connections[0].active = true; netplay->connections[0].active = true;
netplay->connections[0].fd = fd; netplay->connections[0].fd = fd;
netplay->connections[0].addr = sad; memset(&netplay->connections[0].addr, 0,
sizeof(netplay->connections[0].addr));
} }
else else
netplay->listen_fd = fd; netplay->listen_fd = fd;
@ -5974,7 +6032,7 @@ static bool init_socket(netplay_t *netplay, void *direct_host,
if (!network_init()) if (!network_init())
return false; return false;
if (!init_tcp_socket(netplay, direct_host, server, port)) if (!init_tcp_socket(netplay, server, NULL, port))
return false; return false;
if (netplay->is_server && netplay->nat_traversal) if (netplay->is_server && netplay->nat_traversal)
@ -7531,11 +7589,26 @@ void deinit_netplay(void)
if (net_st->data) if (net_st->data)
{ {
netplay_free(net_st->data); netplay_free(net_st->data);
net_st->data = NULL;
net_st->netplay_enabled = false; net_st->netplay_enabled = false;
net_st->netplay_is_client = false; net_st->netplay_is_client = false;
net_st->is_mitm = false; net_st->is_mitm = false;
} }
net_st->data = NULL; if (net_st->mitm_pending.fds)
{
free(net_st->mitm_pending.fds);
net_st->mitm_pending.fds = NULL;
}
if (net_st->mitm_pending.ids)
{
free(net_st->mitm_pending.ids);
net_st->mitm_pending.ids = NULL;
}
if (net_st->mitm_pending.timeouts)
{
free(net_st->mitm_pending.timeouts);
net_st->mitm_pending.timeouts = NULL;
}
core_unset_netplay_callbacks(); core_unset_netplay_callbacks();
} }