mirror of
https://github.com/libretro/RetroArch
synced 2025-02-28 12:40:23 +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:
parent
3e3cf904ca
commit
fb2d600837
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user