(UPnP) Refactor (#13503)

COMMON: The common library can now be used independently by any number of features.
COMMON: The common library no longer restricts HAVE_SOCKET_LEGACY.
Netplay Task: Network interface scoring changed from byte-level to bit-level.
This commit is contained in:
Cthulhu-throwaway 2022-01-17 09:46:03 -03:00 committed by GitHub
parent eaf32daf25
commit 1a7e8e6628
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 283 additions and 341 deletions

View File

@ -25,7 +25,6 @@
#include <net/net_compat.h>
#include <net/net_socket.h>
#include <net/net_ifinfo.h>
#include <retro_common_api.h>
@ -52,6 +51,12 @@ enum nat_traversal_status
NAT_TRAVERSAL_STATUS_CLOSED
};
struct natt_discovery
{
retro_time_t timeout;
int fd;
};
struct natt_device
{
struct sockaddr_in addr;
@ -78,76 +83,41 @@ struct nat_traversal_data
enum nat_traversal_status status;
};
typedef struct
{
/* Timeout for our discovery request. */
retro_time_t timeout;
/* List of available network interfaces. */
struct net_ifinfo interfaces;
/* Device we are operating on. */
struct natt_device device;
/* File descriptor of the socket we are receiving discovery data. */
int fd;
} natt_state_t;
natt_state_t *natt_state_get_ptr(void);
/**
* natt_init:
*
* @discovery : Pointer to a discovery object that will be written to.
*
* Starts a multicast discovery for UPnP devices.
* Must be followed by natt_device_next.
*
* Returns: true if the discovery was started.
*/
bool natt_init(void);
/**
* natt_deinit:
*
* Uninitializes NAT traversal.
* Call this when UPnP is no longer required and before
* natt_init.
*
*/
void natt_deinit(void);
/**
* natt_interfaces_destroy:
*
* Free network interfaces data.
* Call this when you've choosen an appropriate interface,
* generally after a successful port forwarding.
*
*/
void natt_interfaces_destroy(void);
bool natt_init(struct natt_discovery *discovery);
/**
* natt_device_next:
*
* @device : Pointer to a device object that will be written to.
* @discovery : Pointer to a discovery object.
* @device : Pointer to a device object that will be written to.
*
* Grabs the next device that has reported in to our discovery.
* natt_init must be called before this function.
*
* Returns: true if we've retrieved a new device or
* if timeout has not yet been reached. If device->desc is not an empty string,
* a new device was retrieved.
* a new valid device was retrieved.
*/
bool natt_device_next(struct natt_device *device);
bool natt_device_next(struct natt_discovery *discovery,
struct natt_device *device);
/**
* natt_device_end:
*
* @discovery : Pointer to a discovery object.
*
* Stop checking for new devices and close the discovery socket.
* Call this when you've choosen an appropriate device,
* generally after a successful port forwarding.
*
*/
void natt_device_end(void);
void natt_device_end(struct natt_discovery *discovery);
/**
* natt_query_device:
@ -156,7 +126,6 @@ void natt_device_end(void);
* @block : Blocks until the HTTP task is finished.
*
* Query an IGD for its service type and control URL.
* Call this after retrieving a device from natt_device_next.
*
* Returns: true if the task was successfully started.
* If both device->service_type and device->control are not empty strings,
@ -171,7 +140,7 @@ bool natt_query_device(struct natt_device *device, bool block);
* @block : Blocks until the HTTP task is finished.
*
* Retrieve the external IP address of an IGD.
* natt_query_device must have been called first.
* natt_query_device must have been successfully called.
*
* Returns: true if the task was successfully started.
* If device->ext_addr.sin_family is AF_INET,
@ -188,7 +157,7 @@ bool natt_external_address(struct natt_device *device, bool block);
* @block : Blocks until the HTTP task is finished.
*
* Forward a port.
* natt_external_address must have been called first.
* natt_query_device must have been successfully called.
*
* Returns: true if the task was successfully started.
* If request->success is true, the task completed successfully.
@ -205,7 +174,7 @@ bool natt_open_port(struct natt_device *device,
* @block : Blocks until the HTTP task is finished.
*
* Unforward a port.
* natt_open_port must have been called first.
* natt_query_device must have been successfully called.
*
* Returns: true if the task was successfully started.
* If request->success is true, the task completed successfully.

View File

@ -20,14 +20,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if !defined(HAVE_SOCKET_LEGACY) && defined(_WIN32) && defined(_MSC_VER)
#if defined(_WIN32) && defined(_MSC_VER)
#pragma comment(lib, "Iphlpapi")
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>
#include <formats/rxml.h>
#include <features/features_cpu.h>
@ -39,20 +37,47 @@
#include <net/net_natt.h>
#if !defined(HAVE_SOCKET_LEGACY) && defined(_WIN32)
#if defined(_WIN32)
#include <iphlpapi.h>
#endif
static natt_state_t natt_st = {0, {0}, {{0}}, -1};
natt_state_t *natt_state_get_ptr(void)
{
return &natt_st;
}
bool natt_init(void)
static bool translate_addr(struct sockaddr_in *addr,
char *host, size_t hostlen, char *port, size_t portlen)
{
#ifndef HAVE_SOCKET_LEGACY
if (getnameinfo((struct sockaddr *) addr, sizeof(*addr),
host, hostlen, port, portlen,
NI_NUMERICHOST | NI_NUMERICSERV))
return false;
#else
/* We need to do the conversion/translation manually. */
{
int res;
uint8_t *addr8 = (uint8_t *) &addr->sin_addr;
uint16_t port16 = ntohs(addr->sin_port);
if (host)
{
res = snprintf(host, hostlen, "%d.%d.%d.%d",
(int) addr8[0], (int) addr8[1],
(int) addr8[2], (int) addr8[3]);
if (res < 0 || res >= hostlen)
return false;
}
if (port)
{
res = snprintf(port, portlen, "%hu", port16);
if (res < 0 || res >= portlen)
return false;
}
}
#endif
return true;
}
bool natt_init(struct natt_discovery *discovery)
{
static const char msearch[] =
"M-SEARCH * HTTP/1.1\r\n"
"HOST: 239.255.255.250:1900\r\n"
@ -60,35 +85,27 @@ bool natt_init(void)
"ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
"MX: 5\r\n"
"\r\n";
static struct sockaddr_in msearch_addr = {0};
#ifdef _WIN32
MIB_IPFORWARDROW ip_forward;
#endif
natt_state_t *st = &natt_st;
struct addrinfo *bind_addr = NULL;
bool ret;
int fd = -1;
struct addrinfo *msearch_addr = NULL;
struct addrinfo *bind_addr = NULL;
struct addrinfo hints = {0};
if (msearch_addr.sin_family != AF_INET)
{
struct addrinfo *addr = NULL;
struct addrinfo hints = {0};
if (!discovery)
return false;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo_retro("239.255.255.250", "1900", &hints, &addr))
return false;
if (!addr)
return false;
memcpy(&msearch_addr, addr->ai_addr, sizeof(msearch_addr));
freeaddrinfo_retro(addr);
}
if (!net_ifinfo_new(&st->interfaces))
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo_retro("239.255.255.250", "1900", &hints, &msearch_addr))
goto failure;
if (!st->interfaces.size)
if (!msearch_addr)
goto failure;
st->fd = socket_init((void **) &bind_addr, 0, NULL, SOCKET_TYPE_DATAGRAM);
if (st->fd < 0)
fd = socket_init((void **) &bind_addr, 0, NULL, SOCKET_TYPE_DATAGRAM);
if (fd < 0)
goto failure;
if (!bind_addr)
goto failure;
@ -127,7 +144,7 @@ bool natt_init(void)
if (ip_addr->dwIndex == index)
{
#ifdef IP_MULTICAST_IF
setsockopt(st->fd, IPPROTO_IP, IP_MULTICAST_IF,
setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
(const char *) &ip_addr->dwAddr, sizeof(ip_addr->dwAddr));
#endif
((struct sockaddr_in *) bind_addr->ai_addr)->sin_addr.s_addr =
@ -146,75 +163,53 @@ bool natt_init(void)
{
#ifdef _WIN32
unsigned long ttl = 2;
if (setsockopt(st->fd, IPPROTO_IP, IP_MULTICAST_TTL,
(const char *)&ttl, sizeof(ttl)) < 0) { }
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
(const char *) &ttl, sizeof(ttl)) < 0) { }
#else
unsigned char ttl = 2;
if (setsockopt(st->fd, IPPROTO_IP, IP_MULTICAST_TTL,
if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
&ttl, sizeof(ttl)) < 0) { }
#endif
}
#endif
if (!socket_bind(st->fd, bind_addr))
if (!socket_bind(fd, bind_addr))
goto failure;
/* Broadcast a discovery request. */
if (sendto(st->fd, msearch, STRLEN_CONST(msearch), 0,
(struct sockaddr *) &msearch_addr,
sizeof(msearch_addr)) != STRLEN_CONST(msearch))
if (sendto(fd, msearch, STRLEN_CONST(msearch), 0,
msearch_addr->ai_addr, msearch_addr->ai_addrlen)
!= STRLEN_CONST(msearch))
goto failure;
if (!socket_nonblock(st->fd))
if (!socket_nonblock(fd))
goto failure;
/* 5 seconds */
st->timeout = cpu_features_get_time_usec() + 5000000;
discovery->fd = fd;
discovery->timeout = cpu_features_get_time_usec() + 5000000;
freeaddrinfo_retro(bind_addr);
ret = true;
return true;
goto done;
failure:
/* Failed to broadcast. */
if (fd >= 0)
socket_close(fd);
discovery->fd = -1;
discovery->timeout = -1;
ret = false;
done:
freeaddrinfo_retro(msearch_addr);
freeaddrinfo_retro(bind_addr);
natt_deinit();
#endif
return false;
return ret;
}
void natt_deinit(void)
bool natt_device_next(struct natt_discovery *discovery,
struct natt_device *device)
{
#ifndef HAVE_SOCKET_LEGACY
natt_state_t *st = &natt_st;
natt_device_end();
natt_interfaces_destroy();
/* This is faster than memsetting the whole thing. */
*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
}
void natt_interfaces_destroy(void)
{
#ifndef HAVE_SOCKET_LEGACY
natt_state_t *st = &natt_st;
net_ifinfo_free(&st->interfaces);
memset(&st->interfaces, 0, sizeof(st->interfaces));
#endif
}
bool natt_device_next(struct natt_device *device)
{
#ifndef HAVE_SOCKET_LEGACY
fd_set fds;
char buf[2048];
ssize_t recvd;
@ -222,32 +217,31 @@ bool natt_device_next(struct natt_device *device)
size_t remaining;
struct timeval tv = {0};
socklen_t addr_size = sizeof(device->addr);
natt_state_t *st = &natt_st;
if (!device)
if (!discovery || !device)
return false;
if (st->fd < 0)
if (discovery->fd < 0)
return false;
/* This is faster than memsetting the whole thing. */
memset(&device->addr, 0, sizeof(device->addr));
memset(&device->ext_addr, 0, sizeof(device->ext_addr));
*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;
/* Check our file descriptor to see if a device sent data to it. */
FD_ZERO(&fds);
FD_SET(st->fd, &fds);
if (socket_select(st->fd + 1, &fds, NULL, NULL, &tv) < 0)
FD_SET(discovery->fd, &fds);
if (socket_select(discovery->fd + 1, &fds, NULL, NULL, &tv) < 0)
return false;
/* If there was no data, check for timeout. */
if (!FD_ISSET(st->fd, &fds))
return cpu_features_get_time_usec() < st->timeout;
if (!FD_ISSET(discovery->fd, &fds))
return cpu_features_get_time_usec() < discovery->timeout;
recvd = recvfrom(st->fd, buf, sizeof(buf), 0,
recvd = recvfrom(discovery->fd, buf, sizeof(buf), 0,
(struct sockaddr *) &device->addr, &addr_size);
if (recvd <= 0)
return false;
@ -281,29 +275,25 @@ bool natt_device_next(struct natt_device *device)
}
}
remaining -= (size_t)lnbreak - (size_t)data;
remaining -= (size_t) lnbreak - (size_t) data;
data = lnbreak;
} while (remaining);
/* This is not a failure.
We just don't yet have a valid device to report. */
return true;
#else
return false;
#endif
}
void natt_device_end(void)
void natt_device_end(struct natt_discovery *discovery)
{
#ifndef HAVE_SOCKET_LEGACY
natt_state_t *st = &natt_st;
if (st->fd >= 0)
if (discovery)
{
socket_close(st->fd);
st->fd = -1;
if (discovery->fd >= 0)
socket_close(discovery->fd);
discovery->fd = -1;
discovery->timeout = -1;
}
#endif
}
static bool build_control_url(rxml_node_t *control_url,
@ -409,6 +399,9 @@ static void natt_query_device_cb(retro_task_t *task, void *task_data,
http_transfer_data_t *data = task_data;
struct natt_device *device = user_data;
*device->control = '\0';
*device->service_type = '\0';
if (error)
goto done;
if (!data || !data->data || !data->len)
@ -441,7 +434,6 @@ done:
bool natt_query_device(struct natt_device *device, bool block)
{
#ifndef HAVE_SOCKET_LEGACY
if (!device)
return false;
@ -450,10 +442,10 @@ bool natt_query_device(struct natt_device *device, bool block)
if (device->busy)
return false;
device->busy = true;
device->busy = true;
if (!task_push_http_transfer(device->desc,
true, NULL, natt_query_device_cb, device))
true, NULL, natt_query_device_cb, device))
{
device->busy = false;
return false;
@ -463,9 +455,6 @@ bool natt_query_device(struct natt_device *device, bool block)
task_queue_wait(NULL, NULL);
return true;
#else
return false;
#endif
}
static bool parse_external_address_node(rxml_node_t *node,
@ -513,6 +502,8 @@ static void natt_external_address_cb(retro_task_t *task, void *task_data,
http_transfer_data_t *data = task_data;
struct natt_device *device = user_data;
memset(&device->ext_addr, 0, sizeof(device->ext_addr));
if (error)
goto done;
if (!data || !data->data || !data->len)
@ -549,8 +540,6 @@ static bool parse_open_port_node(rxml_node_t *node,
if (string_is_equal_case_insensitive(node->name, "u:AddPortMappingResponse"))
{
request->success = true;
memcpy(&request->addr.sin_addr, &request->device->ext_addr.sin_addr,
sizeof(request->addr.sin_addr));
return true;
}
@ -565,10 +554,8 @@ static bool parse_open_port_node(rxml_node_t *node,
if (!ext_port)
return false;
request->success = true;
request->addr.sin_port = htons(ext_port);
memcpy(&request->addr.sin_addr, &request->device->ext_addr.sin_addr,
sizeof(request->addr.sin_addr));
request->success = true;
return true;
}
@ -663,6 +650,9 @@ static bool natt_action(struct natt_device *device,
char headers[512];
void *obj;
if (string_is_empty(device->control))
return false;
snprintf(headers, sizeof(headers), headers_template,
device->service_type, action);
@ -680,7 +670,6 @@ static bool natt_action(struct natt_device *device,
bool natt_external_address(struct natt_device *device, bool block)
{
#ifndef HAVE_SOCKET_LEGACY
static const char template[] =
"<?xml version=\"1.0\"?>"
"<s:Envelope "
@ -696,18 +685,15 @@ bool natt_external_address(struct natt_device *device, bool block)
if (!device)
return false;
if (string_is_empty(device->control))
return false;
if (device->busy)
return false;
device->busy = true;
snprintf(buf, sizeof(buf), template,
device->service_type);
if (device->busy)
return false;
device->busy = true;
if (!natt_action(device, "GetExternalIPAddress", buf,
natt_external_address_cb, NULL))
natt_external_address_cb, NULL))
{
device->busy = false;
return false;
@ -717,16 +703,12 @@ bool natt_external_address(struct natt_device *device, bool block)
task_queue_wait(NULL, NULL);
return true;
#else
return false;
#endif
}
bool natt_open_port(struct natt_device *device,
struct natt_request *request, enum natt_forward_type forward_type,
bool block)
{
#ifndef HAVE_SOCKET_LEGACY
static const char template[] =
"<?xml version=\"1.0\"?>"
"<s:Envelope "
@ -747,42 +729,34 @@ bool natt_open_port(struct natt_device *device,
"</s:Body>"
"</s:Envelope>";
char buf[1280];
const char *action;
const char *action, *protocol;
char host[256], port[6];
if (!device || !request)
return false;
if (string_is_empty(device->control))
return false;
if (device->ext_addr.sin_family != AF_INET)
return false;
if (!request->addr.sin_port)
return false;
if (getnameinfo((struct sockaddr *) &request->addr,
sizeof(request->addr), host, sizeof(host),
port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV))
if (!translate_addr(&request->addr,
host, sizeof(host), port, sizeof(port)))
return false;
action = (forward_type == NATT_FORWARD_TYPE_ANY) ?
"AddAnyPortMapping" : "AddPortMapping";
protocol = (request->proto == SOCKET_PROTOCOL_UDP) ?
"UDP" : "TCP";
snprintf(buf, sizeof(buf), template,
action, device->service_type,
port, protocol, port, host,
action);
if (device->busy)
return false;
device->busy = true;
action = (forward_type == NATT_FORWARD_TYPE_ANY) ?
"AddAnyPortMapping" : "AddPortMapping";
snprintf(buf, sizeof(buf), template,
action, device->service_type, port,
(request->proto == SOCKET_PROTOCOL_UDP) ?
"UDP" : "TCP",
port, host,
action);
if (!natt_action(device, action, buf,
natt_open_port_cb, request))
natt_open_port_cb, request))
{
device->busy = false;
return false;
@ -792,15 +766,11 @@ bool natt_open_port(struct natt_device *device,
task_queue_wait(NULL, NULL);
return true;
#else
return false;
#endif
}
bool natt_close_port(struct natt_device *device,
struct natt_request *request, bool block)
{
#ifndef HAVE_SOCKET_LEGACY
static const char template[] =
"<?xml version=\"1.0\"?>"
"<s:Envelope "
@ -816,36 +786,30 @@ bool natt_close_port(struct natt_device *device,
"</s:Body>"
"</s:Envelope>";
char buf[1024];
const char *protocol;
char port[6];
if (!device || !request)
return false;
if (string_is_empty(device->control))
return false;
if (device->ext_addr.sin_family != AF_INET)
return false;
if (!request->addr.sin_port)
return false;
if (getnameinfo((struct sockaddr *) &request->addr,
sizeof(request->addr), NULL, 0,
port, sizeof(port), NI_NUMERICSERV))
if (!translate_addr(&request->addr,
NULL, 0, port, sizeof(port)))
return false;
protocol = (request->proto == SOCKET_PROTOCOL_UDP) ?
"UDP" : "TCP";
snprintf(buf, sizeof(buf), template,
device->service_type, port, protocol);
if (device->busy)
return false;
device->busy = true;
snprintf(buf, sizeof(buf), template,
device->service_type, port,
(request->proto == SOCKET_PROTOCOL_UDP) ?
"UDP" : "TCP");
if (!natt_action(device, "DeletePortMapping", buf,
natt_close_port_cb, request))
natt_close_port_cb, request))
{
device->busy = false;
return false;
@ -855,7 +819,4 @@ bool natt_close_port(struct natt_device *device,
task_queue_wait(NULL, NULL);
return true;
#else
return false;
#endif
}

View File

@ -25,99 +25,134 @@
#endif
#ifdef HAVE_NETWORKING
#ifndef HAVE_SOCKET_LEGACY
#include <net/net_ifinfo.h>
#endif
#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)
static bool find_local_address(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;
bool ret = false;
/* TODO/FIXME: Find a way to get the network's interface on
HAVE_SOCKET_LEGACY platforms */
#ifndef HAVE_SOCKET_LEGACY
struct net_ifinfo interfaces = {0};
struct addrinfo **addrs = NULL;
uint32_t *scores = NULL;
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++)
if (net_ifinfo_new(&interfaces) && interfaces.size > 0)
{
struct net_ifinfo_entry *entry = &interfaces->entries[i];
struct addrinfo **addr = &addrs[i];
uint32_t *score = &scores[i];
size_t i, j, k;
uint32_t highest_score = 0;
struct addrinfo hints = {0};
uint8_t *dev_addr8 = (uint8_t *) &device->addr.sin_addr;
if (getaddrinfo_retro(entry->host, NULL, &hints, addr))
continue;
addrs = calloc(interfaces.size, sizeof(*addrs));
if (!addrs)
goto done;
scores = calloc(interfaces.size, sizeof(*scores));
if (!scores)
goto done;
if (*addr)
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++)
{
uint8_t *addr8 = (uint8_t *)
&((struct sockaddr_in *) (*addr)->ai_addr)->sin_addr;
struct net_ifinfo_entry *entry = &interfaces.entries[i];
struct addrinfo **addr = &addrs[i];
uint32_t *score = &scores[i];
for (j = 0; j < sizeof(device->addr.sin_addr); j++)
if (getaddrinfo_retro(entry->host, NULL, &hints, addr))
continue;
if (*addr)
{
if (addr8[j] != dev_addr8[j])
break;
(*score)++;
uint8_t *addr8 = (uint8_t *)
&((struct sockaddr_in *) (*addr)->ai_addr)->sin_addr;
bool stop_score = false;
for (j = 0; j < sizeof(device->addr.sin_addr) && !stop_score; j++)
{
uint8_t bits_dev = dev_addr8[j];
uint8_t bits_addr = addr8[j];
for (k = 0; k < 8; k++)
{
/* Each matched bit (from high to low bits)
means +1 to score.
Stop scoring when a bit mismatch. */
uint8_t bit_mask = 0x80 >> k;
uint8_t bit_dev = bits_dev & bit_mask;
uint8_t bit_addr = bits_addr & bit_mask;
if (bit_addr != bit_dev)
{
stop_score = true;
break;
}
(*score)++;
}
}
}
}
}
/* Get the highest scored interface. */
for (j = 0; j < interfaces->size; j++)
{
uint32_t score = scores[j];
if (score > highest_score)
/* Get the highest scored interface. */
for (j = 0; j < interfaces.size; j++)
{
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;
}
uint32_t score = scores[j];
for (i = 0; i < interfaces->size; i++)
freeaddrinfo_retro(addrs[i]);
if (score > highest_score)
{
highest_score = score;
i = j;
}
}
/* Skip a highest score of less than 8. */
if (highest_score >= 8)
{
/* 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);
free(addrs);
net_ifinfo_free(&interfaces);
#endif
return ret;
}
static void task_netplay_nat_traversal_handler(retro_task_t *task)
{
static struct natt_discovery discovery = {-1};
static struct natt_device device = {0};
struct nat_traversal_data *data = task->task_data;
natt_state_t *natt_st = natt_state_get_ptr();
/* Try again on the next call. */
if (natt_st->device.busy)
if (device.busy)
return;
switch (data->status)
{
case NAT_TRAVERSAL_STATUS_DISCOVERY:
{
if (!natt_init())
if (!natt_init(&discovery))
goto finished;
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
@ -126,19 +161,24 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
case NAT_TRAVERSAL_STATUS_SELECT_DEVICE:
{
if (!natt_device_next(&natt_st->device))
if (!natt_device_next(&discovery, &device))
{
natt_deinit();
natt_device_end(&discovery);
goto finished;
}
if (!string_is_empty(natt_st->device.desc))
data->status = NAT_TRAVERSAL_STATUS_QUERY_DEVICE;
if (string_is_empty(device.desc))
break;
if (!find_local_address(&device, &data->request))
break;
data->status = NAT_TRAVERSAL_STATUS_QUERY_DEVICE;
}
break;
case NAT_TRAVERSAL_STATUS_QUERY_DEVICE:
{
if (natt_query_device(&natt_st->device, false))
if (natt_query_device(&device, false))
data->status = NAT_TRAVERSAL_STATUS_EXTERNAL_ADDRESS;
else
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
@ -147,12 +187,13 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
case NAT_TRAVERSAL_STATUS_EXTERNAL_ADDRESS:
{
if (string_is_empty(natt_st->device.service_type))
if (string_is_empty(device.service_type))
{
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
break;
}
if (natt_external_address(&natt_st->device, false))
if (natt_external_address(&device, false))
{
data->forward_type = NATT_FORWARD_TYPE_ANY;
data->status = NAT_TRAVERSAL_STATUS_OPEN;
@ -164,26 +205,15 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
case NAT_TRAVERSAL_STATUS_OPEN:
{
if (natt_st->device.ext_addr.sin_family != AF_INET)
{
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
break;
}
if (!find_local_address(&natt_st->interfaces,
&natt_st->device, &data->request))
if (device.ext_addr.sin_family != AF_INET)
{
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
break;
}
if (natt_open_port(&natt_st->device,
&data->request, data->forward_type, false))
{
if (natt_open_port(&device, &data->request,
data->forward_type, false))
data->status = NAT_TRAVERSAL_STATUS_OPENING;
break;
}
if (data->forward_type == NATT_FORWARD_TYPE_ANY)
data->forward_type = NATT_FORWARD_TYPE_NONE;
else
data->status = NAT_TRAVERSAL_STATUS_SELECT_DEVICE;
}
@ -193,15 +223,18 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
{
if (data->request.success)
{
/* We no longer need these */
natt_device_end();
natt_interfaces_destroy();
natt_device_end(&discovery);
/* Copy the external address into the request. */
memcpy(&data->request.addr.sin_addr,
&device.ext_addr.sin_addr,
sizeof(data->request.addr.sin_addr));
data->status = NAT_TRAVERSAL_STATUS_OPENED;
goto finished;
}
if (data->forward_type == NATT_FORWARD_TYPE_ANY)
else if (data->forward_type == NATT_FORWARD_TYPE_ANY)
{
data->forward_type = NATT_FORWARD_TYPE_NONE;
data->status = NAT_TRAVERSAL_STATUS_OPEN;
@ -211,41 +244,16 @@ static void task_netplay_nat_traversal_handler(retro_task_t *task)
}
break;
default:
break;
}
return;
finished:
task_set_progress(task, 100);
task_set_finished(task, true);
}
static void task_netplay_nat_close_handler(retro_task_t *task)
{
struct nat_traversal_data *data = task->task_data;
natt_state_t *natt_st = natt_state_get_ptr();
/* Try again on the next call. */
if (natt_st->device.busy)
return;
switch (data->status)
{
case NAT_TRAVERSAL_STATUS_CLOSE:
{
natt_close_port(&natt_st->device, &data->request, false);
natt_close_port(&device, &data->request, false);
/* We will deinit NAT whether it succeeds or fails. */
data->status = NAT_TRAVERSAL_STATUS_CLOSING;
}
break;
case NAT_TRAVERSAL_STATUS_CLOSING:
{
natt_deinit();
data->status = NAT_TRAVERSAL_STATUS_CLOSED;
goto finished;
@ -274,8 +282,7 @@ static bool nat_task_finder(retro_task_t *task, void *userdata)
if (!task)
return false;
return task->handler == task_netplay_nat_traversal_handler ||
task->handler == task_netplay_nat_close_handler;
return task->handler == task_netplay_nat_traversal_handler;
}
static bool nat_task_queued(void *data)
@ -297,11 +304,10 @@ bool task_push_netplay_nat_traversal(void *data, uint16_t port)
if (!task)
return false;
natt_deinit();
natt_data->request.addr.sin_family = AF_INET;
natt_data->request.addr.sin_port = htons(port);
natt_data->request.proto = SOCKET_PROTOCOL_TCP;
natt_data->request.device = NULL;
natt_data->status = NAT_TRAVERSAL_STATUS_DISCOVERY;
task->handler = task_netplay_nat_traversal_handler;
@ -321,18 +327,24 @@ bool task_push_netplay_nat_close(void *data)
/* Do not run more than one NAT task at a time. */
task_queue_wait(nat_task_queued, NULL);
if (natt_data->status != NAT_TRAVERSAL_STATUS_OPENED)
return false;
if (natt_data->request.addr.sin_family != AF_INET)
return false;
if (!natt_data->request.addr.sin_port)
return false;
if (natt_data->request.proto != SOCKET_PROTOCOL_TCP)
return false;
if (!natt_data->request.device)
return false;
task = task_init();
if (!task)
return false;
natt_data->request.addr.sin_family = AF_INET;
natt_data->request.proto = SOCKET_PROTOCOL_TCP;
natt_data->status = NAT_TRAVERSAL_STATUS_CLOSE;
natt_data->status = NAT_TRAVERSAL_STATUS_CLOSE;
task->handler = task_netplay_nat_close_handler;
task->handler = task_netplay_nat_traversal_handler;
task->task_data = data;
task_queue_push(task);