mirror of
https://github.com/libretro/RetroArch.git
synced 2024-11-27 10:10:57 +00:00
(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:
parent
eaf32daf25
commit
1a7e8e6628
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user