1
0
mirror of https://github.com/libretro/RetroArch synced 2024-07-08 20:25:47 +00:00

(Netplay/UPnP) Smart interface selection (#13470)

Find the most suitable address for UPnP by scoring interfaces on how close their address is to the device's address.
This commit is contained in:
Cthulhu-throwaway 2022-01-10 11:52:15 -03:00 committed by GitHub
parent 3e3cf904ca
commit fb2d600837
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 125 additions and 80 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2010-2021 The RetroArch team
/* Copyright (C) 2010-2022 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (net_natt.h).
@ -54,10 +54,11 @@ enum nat_traversal_status
struct natt_device
{
struct sockaddr_in addr;
struct sockaddr_in ext_addr;
char desc [512];
char control [512];
char service_type[512];
char desc [256];
char control [256];
char service_type[256];
bool busy;
};
@ -69,10 +70,10 @@ struct natt_request
bool success;
};
/* Use this struct to implement a higher-level interface. */
struct nat_traversal_data
{
struct natt_request request;
size_t iface;
enum natt_forward_type forward_type;
enum nat_traversal_status status;
};

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2016-2021 The RetroArch team
/* Copyright (C) 2016-2022 The RetroArch team
*
* ---------------------------------------------------------------------------------------
* The following license statement only applies to this file (net_natt.c).
@ -87,7 +87,7 @@ bool natt_init(void)
if (!st->interfaces.size)
goto failure;
st->fd = socket_init((void**) &bind_addr, 0, NULL, SOCKET_TYPE_DATAGRAM);
st->fd = socket_init((void **) &bind_addr, 0, NULL, SOCKET_TYPE_DATAGRAM);
if (st->fd < 0)
goto failure;
if (!bind_addr)
@ -196,6 +196,7 @@ void natt_deinit(void)
*st->device.desc = '\0';
*st->device.control = '\0';
*st->device.service_type = '\0';
memset(&st->device.addr, 0, sizeof(st->device.addr));
memset(&st->device.ext_addr, 0, sizeof(st->device.ext_addr));
st->device.busy = false;
#endif
@ -215,13 +216,13 @@ bool natt_device_next(struct natt_device *device)
{
#ifndef HAVE_SOCKET_LEGACY
fd_set fds;
bool error;
char buf[2048];
ssize_t recvd;
char *data;
size_t remaining;
struct timeval tv = {0};
natt_state_t *st = &natt_st;
struct timeval tv = {0};
socklen_t addr_size = sizeof(device->addr);
natt_state_t *st = &natt_st;
if (!device)
return false;
@ -233,6 +234,7 @@ bool natt_device_next(struct natt_device *device)
*device->desc = '\0';
*device->control = '\0';
*device->service_type = '\0';
memset(&device->addr, 0, sizeof(device->addr));
memset(&device->ext_addr, 0, sizeof(device->ext_addr));
device->busy = false;
@ -245,8 +247,8 @@ bool natt_device_next(struct natt_device *device)
if (!FD_ISSET(st->fd, &fds))
return cpu_features_get_time_usec() < st->timeout;
recvd = socket_receive_all_nonblocking(st->fd, &error,
buf, sizeof(buf));
recvd = recvfrom(st->fd, buf, sizeof(buf), 0,
(struct sockaddr *) &device->addr, &addr_size);
if (recvd <= 0)
return false;
@ -262,19 +264,20 @@ bool natt_device_next(struct natt_device *device)
*lnbreak++ = '\0';
/* This also gets rid of any trailing carriage return. */
if (!strncasecmp(string_trim_whitespace(data), "Location:",
STRLEN_CONST("Location:")))
string_trim_whitespace(data);
if (string_starts_with_case_insensitive(data, "Location:"))
{
char *location = string_trim_whitespace(
char *location = string_trim_whitespace_left(
data + STRLEN_CONST("Location:"));
if (!string_is_empty(location) &&
string_starts_with_case_insensitive(location, "http://"))
if (string_starts_with_case_insensitive(location, "http://"))
{
strlcpy(device->desc, location,
sizeof(device->desc));
return true;
/* Make sure the description URL isn't too long. */
if (strlcpy(device->desc, location, sizeof(device->desc)) <
sizeof(device->desc))
return true;
*device->desc = '\0';
}
}
@ -312,8 +315,13 @@ static bool build_control_url(rxml_node_t *control_url,
/* Do we already have the full url? */
if (string_starts_with_case_insensitive(control_url->data, "http://"))
{
strlcpy(device->control, control_url->data,
sizeof(device->control));
/* Make sure the control URL isn't too long. */
if (strlcpy(device->control, control_url->data,
sizeof(device->control)) >= sizeof(device->control))
{
*device->control = '\0';
return false;
}
}
else
{
@ -324,15 +332,20 @@ static bool build_control_url(rxml_node_t *control_url,
strlcpy(device->control, device->desc,
sizeof(device->control));
control_path = (char*) strchr(device->control + STRLEN_CONST("http://"),
'/');
control_path = (char *) strchr(device->control +
STRLEN_CONST("http://"), '/');
if (control_path)
*control_path = '\0';
if (control_url->data[0] != '/')
strlcat(device->control, "/", sizeof(device->control));
strlcat(device->control, control_url->data,
sizeof(device->control));
/* Make sure the control URL isn't too long. */
if (strlcat(device->control, control_url->data,
sizeof(device->control)) >= sizeof(device->control))
{
*device->control = '\0';
return false;
}
}
return true;

View File

@ -1,6 +1,6 @@
/* RetroArch - A frontend for libretro.
* Copyright (C) 2017 - Gregor Richards
* Copyright (C) 2021 - Roberto V. Rampim
* Copyright (C) 2017-2017 - Gregor Richards
* Copyright (C) 2021-2022 - Roberto V. Rampim
*
* RetroArch is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Found-
@ -28,6 +28,82 @@
#include <net/net_natt.h>
#include "../network/netplay/netplay.h"
/* Find the most suitable address within the device's network. */
static bool find_local_address(struct net_ifinfo *interfaces,
struct natt_device *device, struct natt_request *request)
{
size_t i, j;
struct addrinfo **addrs = NULL;
uint32_t *scores = NULL;
uint32_t highest_score = 0;
struct addrinfo hints = {0};
uint8_t *dev_addr8 = (uint8_t *) &device->addr.sin_addr;
bool ret = false;
addrs = calloc(interfaces->size, sizeof(*addrs));
if (!addrs)
goto done;
scores = calloc(interfaces->size, sizeof(*scores));
if (!scores)
goto done;
hints.ai_family = AF_INET;
/* Score interfaces based on how close their address
is from the device's address. */
for (i = 0; i < interfaces->size; i++)
{
struct net_ifinfo_entry *entry = &interfaces->entries[i];
struct addrinfo **addr = &addrs[i];
uint32_t *score = &scores[i];
if (getaddrinfo_retro(entry->host, NULL, &hints, addr))
continue;
if (*addr)
{
uint8_t *addr8 = (uint8_t *)
&((struct sockaddr_in *) (*addr)->ai_addr)->sin_addr;
for (j = 0; j < sizeof(device->addr.sin_addr); j++)
{
if (addr8[j] != dev_addr8[j])
break;
(*score)++;
}
}
}
/* Get the highest scored interface. */
for (j = 0; j < interfaces->size; j++)
{
uint32_t score = scores[j];
if (score > highest_score)
{
highest_score = score;
i = j;
}
}
/* Skip a highest score of zero. */
if (highest_score)
{
/* Copy the interface's address to our request. */
memcpy(&request->addr.sin_addr,
&((struct sockaddr_in *) addrs[i]->ai_addr)->sin_addr,
sizeof(request->addr.sin_addr));
ret = true;
}
for (i = 0; i < interfaces->size; i++)
freeaddrinfo_retro(addrs[i]);
done:
free(addrs);
free(scores);
return ret;
}
static void task_netplay_nat_traversal_handler(retro_task_t *task)
{
struct nat_traversal_data *data = task->task_data;
@ -78,7 +154,6 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
}
if (natt_external_address(&natt_st->device, false))
{
data->iface = 0;
data->forward_type = NATT_FORWARD_TYPE_ANY;
data->status = NAT_TRAVERSAL_STATUS_OPEN;
}
@ -89,55 +164,18 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
case NAT_TRAVERSAL_STATUS_OPEN:
{
size_t i;
struct addrinfo *addr = NULL;
struct net_ifinfo_entry *entry = NULL;
struct addrinfo hints = {0};
if (natt_st->device.ext_addr.sin_family != AF_INET)
{
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
break;
}
/* Grab a suitable interface. */
hints.ai_family = AF_INET;
for (i = data->iface; i < natt_st->interfaces.size; i++)
{
struct net_ifinfo_entry *tmp_entry =
&natt_st->interfaces.entries[i];
if (getaddrinfo_retro(tmp_entry->host, NULL, &hints, &addr) ||
!addr)
continue;
/* Ignore non-LAN interfaces */
if (!netplay_is_lan_address(
(struct sockaddr_in *) addr->ai_addr))
{
freeaddrinfo_retro(addr);
addr = NULL;
continue;
}
entry = tmp_entry;
data->iface = i;
break;
}
/* No more interfaces? */
if (!entry)
if (!find_local_address(&natt_st->interfaces,
&natt_st->device, &data->request))
{
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
break;
}
memcpy(&data->request.addr.sin_addr,
&((struct sockaddr_in *) addr->ai_addr)->sin_addr,
sizeof(data->request.addr.sin_addr));
freeaddrinfo_retro(addr);
if (natt_open_port(&natt_st->device,
&data->request, data->forward_type, false))
{
@ -145,12 +183,7 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
break;
}
if (data->forward_type == NATT_FORWARD_TYPE_ANY)
{
data->forward_type = NATT_FORWARD_TYPE_NONE;
break;
}
if (++data->iface < natt_st->interfaces.size)
data->forward_type = NATT_FORWARD_TYPE_ANY;
else
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
}
@ -165,23 +198,19 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
natt_interfaces_destroy();
data->status = NAT_TRAVERSAL_STATUS_OPENED;
goto finished;
}
if (data->forward_type == NATT_FORWARD_TYPE_ANY)
{
data->forward_type = NATT_FORWARD_TYPE_NONE;
data->status = NAT_TRAVERSAL_STATUS_OPEN;
break;
}
if (++data->iface < natt_st->interfaces.size)
{
data->forward_type = NATT_FORWARD_TYPE_ANY;
data->status = NAT_TRAVERSAL_STATUS_OPEN;
}
else
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
}
break;
default:
break;
}
@ -218,9 +247,11 @@ static void task_netplay_nat_close_handler(retro_task_t *task)
natt_deinit();
data->status = NAT_TRAVERSAL_STATUS_CLOSED;
goto finished;
}
break;
default:
break;
}