(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).
|
* The following license statement only applies to this file (net_natt.h).
|
||||||
|
@ -54,10 +54,11 @@ enum nat_traversal_status
|
||||||
|
|
||||||
struct natt_device
|
struct natt_device
|
||||||
{
|
{
|
||||||
|
struct sockaddr_in addr;
|
||||||
struct sockaddr_in ext_addr;
|
struct sockaddr_in ext_addr;
|
||||||
char desc [512];
|
char desc [256];
|
||||||
char control [512];
|
char control [256];
|
||||||
char service_type[512];
|
char service_type[256];
|
||||||
bool busy;
|
bool busy;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,10 +70,10 @@ struct natt_request
|
||||||
bool success;
|
bool success;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Use this struct to implement a higher-level interface. */
|
||||||
struct nat_traversal_data
|
struct nat_traversal_data
|
||||||
{
|
{
|
||||||
struct natt_request request;
|
struct natt_request request;
|
||||||
size_t iface;
|
|
||||||
enum natt_forward_type forward_type;
|
enum natt_forward_type forward_type;
|
||||||
enum nat_traversal_status status;
|
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).
|
* The following license statement only applies to this file (net_natt.c).
|
||||||
|
@ -87,7 +87,7 @@ bool natt_init(void)
|
||||||
if (!st->interfaces.size)
|
if (!st->interfaces.size)
|
||||||
goto failure;
|
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)
|
if (st->fd < 0)
|
||||||
goto failure;
|
goto failure;
|
||||||
if (!bind_addr)
|
if (!bind_addr)
|
||||||
|
@ -196,6 +196,7 @@ void natt_deinit(void)
|
||||||
*st->device.desc = '\0';
|
*st->device.desc = '\0';
|
||||||
*st->device.control = '\0';
|
*st->device.control = '\0';
|
||||||
*st->device.service_type = '\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));
|
memset(&st->device.ext_addr, 0, sizeof(st->device.ext_addr));
|
||||||
st->device.busy = false;
|
st->device.busy = false;
|
||||||
#endif
|
#endif
|
||||||
|
@ -215,13 +216,13 @@ bool natt_device_next(struct natt_device *device)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_SOCKET_LEGACY
|
#ifndef HAVE_SOCKET_LEGACY
|
||||||
fd_set fds;
|
fd_set fds;
|
||||||
bool error;
|
|
||||||
char buf[2048];
|
char buf[2048];
|
||||||
ssize_t recvd;
|
ssize_t recvd;
|
||||||
char *data;
|
char *data;
|
||||||
size_t remaining;
|
size_t remaining;
|
||||||
struct timeval tv = {0};
|
struct timeval tv = {0};
|
||||||
natt_state_t *st = &natt_st;
|
socklen_t addr_size = sizeof(device->addr);
|
||||||
|
natt_state_t *st = &natt_st;
|
||||||
|
|
||||||
if (!device)
|
if (!device)
|
||||||
return false;
|
return false;
|
||||||
|
@ -233,6 +234,7 @@ bool natt_device_next(struct natt_device *device)
|
||||||
*device->desc = '\0';
|
*device->desc = '\0';
|
||||||
*device->control = '\0';
|
*device->control = '\0';
|
||||||
*device->service_type = '\0';
|
*device->service_type = '\0';
|
||||||
|
memset(&device->addr, 0, sizeof(device->addr));
|
||||||
memset(&device->ext_addr, 0, sizeof(device->ext_addr));
|
memset(&device->ext_addr, 0, sizeof(device->ext_addr));
|
||||||
device->busy = false;
|
device->busy = false;
|
||||||
|
|
||||||
|
@ -245,8 +247,8 @@ bool natt_device_next(struct natt_device *device)
|
||||||
if (!FD_ISSET(st->fd, &fds))
|
if (!FD_ISSET(st->fd, &fds))
|
||||||
return cpu_features_get_time_usec() < st->timeout;
|
return cpu_features_get_time_usec() < st->timeout;
|
||||||
|
|
||||||
recvd = socket_receive_all_nonblocking(st->fd, &error,
|
recvd = recvfrom(st->fd, buf, sizeof(buf), 0,
|
||||||
buf, sizeof(buf));
|
(struct sockaddr *) &device->addr, &addr_size);
|
||||||
if (recvd <= 0)
|
if (recvd <= 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -262,19 +264,20 @@ bool natt_device_next(struct natt_device *device)
|
||||||
*lnbreak++ = '\0';
|
*lnbreak++ = '\0';
|
||||||
|
|
||||||
/* This also gets rid of any trailing carriage return. */
|
/* This also gets rid of any trailing carriage return. */
|
||||||
if (!strncasecmp(string_trim_whitespace(data), "Location:",
|
string_trim_whitespace(data);
|
||||||
STRLEN_CONST("Location:")))
|
|
||||||
|
if (string_starts_with_case_insensitive(data, "Location:"))
|
||||||
{
|
{
|
||||||
char *location = string_trim_whitespace(
|
char *location = string_trim_whitespace_left(
|
||||||
data + STRLEN_CONST("Location:"));
|
data + STRLEN_CONST("Location:"));
|
||||||
|
|
||||||
if (!string_is_empty(location) &&
|
if (string_starts_with_case_insensitive(location, "http://"))
|
||||||
string_starts_with_case_insensitive(location, "http://"))
|
|
||||||
{
|
{
|
||||||
strlcpy(device->desc, location,
|
/* Make sure the description URL isn't too long. */
|
||||||
sizeof(device->desc));
|
if (strlcpy(device->desc, location, sizeof(device->desc)) <
|
||||||
|
sizeof(device->desc))
|
||||||
return true;
|
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? */
|
/* Do we already have the full url? */
|
||||||
if (string_starts_with_case_insensitive(control_url->data, "http://"))
|
if (string_starts_with_case_insensitive(control_url->data, "http://"))
|
||||||
{
|
{
|
||||||
strlcpy(device->control, control_url->data,
|
/* Make sure the control URL isn't too long. */
|
||||||
sizeof(device->control));
|
if (strlcpy(device->control, control_url->data,
|
||||||
|
sizeof(device->control)) >= sizeof(device->control))
|
||||||
|
{
|
||||||
|
*device->control = '\0';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -324,15 +332,20 @@ static bool build_control_url(rxml_node_t *control_url,
|
||||||
strlcpy(device->control, device->desc,
|
strlcpy(device->control, device->desc,
|
||||||
sizeof(device->control));
|
sizeof(device->control));
|
||||||
|
|
||||||
control_path = (char*) strchr(device->control + STRLEN_CONST("http://"),
|
control_path = (char *) strchr(device->control +
|
||||||
'/');
|
STRLEN_CONST("http://"), '/');
|
||||||
|
|
||||||
if (control_path)
|
if (control_path)
|
||||||
*control_path = '\0';
|
*control_path = '\0';
|
||||||
if (control_url->data[0] != '/')
|
if (control_url->data[0] != '/')
|
||||||
strlcat(device->control, "/", sizeof(device->control));
|
strlcat(device->control, "/", sizeof(device->control));
|
||||||
strlcat(device->control, control_url->data,
|
/* Make sure the control URL isn't too long. */
|
||||||
sizeof(device->control));
|
if (strlcat(device->control, control_url->data,
|
||||||
|
sizeof(device->control)) >= sizeof(device->control))
|
||||||
|
{
|
||||||
|
*device->control = '\0';
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* RetroArch - A frontend for libretro.
|
/* RetroArch - A frontend for libretro.
|
||||||
* Copyright (C) 2017 - Gregor Richards
|
* Copyright (C) 2017-2017 - Gregor Richards
|
||||||
* Copyright (C) 2021 - Roberto V. Rampim
|
* Copyright (C) 2021-2022 - Roberto V. Rampim
|
||||||
*
|
*
|
||||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
* 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-
|
* of the GNU General Public License as published by the Free Software Found-
|
||||||
|
@ -28,6 +28,82 @@
|
||||||
#include <net/net_natt.h>
|
#include <net/net_natt.h>
|
||||||
#include "../network/netplay/netplay.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)
|
static void task_netplay_nat_traversal_handler(retro_task_t *task)
|
||||||
{
|
{
|
||||||
struct nat_traversal_data *data = task->task_data;
|
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))
|
if (natt_external_address(&natt_st->device, false))
|
||||||
{
|
{
|
||||||
data->iface = 0;
|
|
||||||
data->forward_type = NATT_FORWARD_TYPE_ANY;
|
data->forward_type = NATT_FORWARD_TYPE_ANY;
|
||||||
data->status = NAT_TRAVERSAL_STATUS_OPEN;
|
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:
|
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)
|
if (natt_st->device.ext_addr.sin_family != AF_INET)
|
||||||
{
|
{
|
||||||
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
|
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!find_local_address(&natt_st->interfaces,
|
||||||
/* Grab a suitable interface. */
|
&natt_st->device, &data->request))
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
|
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
|
||||||
break;
|
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,
|
if (natt_open_port(&natt_st->device,
|
||||||
&data->request, data->forward_type, false))
|
&data->request, data->forward_type, false))
|
||||||
{
|
{
|
||||||
|
@ -145,12 +183,7 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (data->forward_type == NATT_FORWARD_TYPE_ANY)
|
if (data->forward_type == NATT_FORWARD_TYPE_ANY)
|
||||||
{
|
|
||||||
data->forward_type = NATT_FORWARD_TYPE_NONE;
|
data->forward_type = NATT_FORWARD_TYPE_NONE;
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (++data->iface < natt_st->interfaces.size)
|
|
||||||
data->forward_type = NATT_FORWARD_TYPE_ANY;
|
|
||||||
else
|
else
|
||||||
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
|
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();
|
natt_interfaces_destroy();
|
||||||
|
|
||||||
data->status = NAT_TRAVERSAL_STATUS_OPENED;
|
data->status = NAT_TRAVERSAL_STATUS_OPENED;
|
||||||
|
|
||||||
goto finished;
|
goto finished;
|
||||||
}
|
}
|
||||||
if (data->forward_type == NATT_FORWARD_TYPE_ANY)
|
if (data->forward_type == NATT_FORWARD_TYPE_ANY)
|
||||||
{
|
{
|
||||||
data->forward_type = NATT_FORWARD_TYPE_NONE;
|
data->forward_type = NATT_FORWARD_TYPE_NONE;
|
||||||
data->status = NAT_TRAVERSAL_STATUS_OPEN;
|
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
|
else
|
||||||
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
|
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -218,9 +247,11 @@ static void task_netplay_nat_close_handler(retro_task_t *task)
|
||||||
natt_deinit();
|
natt_deinit();
|
||||||
|
|
||||||
data->status = NAT_TRAVERSAL_STATUS_CLOSED;
|
data->status = NAT_TRAVERSAL_STATUS_CLOSED;
|
||||||
|
|
||||||
goto finished;
|
goto finished;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue