mirror of
https://github.com/CTCaer/RetroArch.git
synced 2025-02-21 03:50:28 +00:00
NAT traversal
Adding NAT traveral interface to libretro-common, with (currently) a backend support MiniUPNPC. Sensible future backends would be libupnp and a direct implementation of PCP/NAT-PMP.
This commit is contained in:
parent
951f3f3e19
commit
65355994b3
@ -1086,6 +1086,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
OBJ += $(LIBRETRO_COMM_DIR)/net/net_compat.o \
|
||||
$(LIBRETRO_COMM_DIR)/net/net_http.o \
|
||||
$(LIBRETRO_COMM_DIR)/net/net_socket.o \
|
||||
$(LIBRETRO_COMM_DIR)/net/net_natt.o \
|
||||
network/net_http_special.o \
|
||||
tasks/task_http.o
|
||||
|
||||
@ -1123,6 +1124,10 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
OBJ += input/input_remote.o \
|
||||
cores/libretro-net-retropad/net_retropad_core.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_MINIUPNPC), 1)
|
||||
LIBS += -lminiupnpc
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(findstring Win32,$(OS)),)
|
||||
|
47
libretro-common/include/net/net_natt.h
Normal file
47
libretro-common/include/net/net_natt.h
Normal file
@ -0,0 +1,47 @@
|
||||
#include <net/net_compat.h>
|
||||
|
||||
struct natt_status {
|
||||
/** The fdset to be selected upon to check for responses */
|
||||
fd_set fds;
|
||||
|
||||
/** True if we've resolved an external IPv4 address */
|
||||
bool have_inet4;
|
||||
|
||||
/** External IPv4 address */
|
||||
struct sockaddr_in ext_inet4_addr;
|
||||
|
||||
/** True if we've resolved an external IPv6 address */
|
||||
bool have_inet6;
|
||||
|
||||
#ifdef AF_INET6
|
||||
/** External IPv6 address */
|
||||
struct sockaddr_in6 ext_inet6_addr;
|
||||
#endif
|
||||
|
||||
/** Internal status (currently unused) */
|
||||
void *internal;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize global NAT traversal structures (must be called once to use other
|
||||
* functions) */
|
||||
void natt_init(void);
|
||||
|
||||
/** Initialize a NAT traversal status object */
|
||||
bool natt_new(struct natt_status *status);
|
||||
|
||||
/** Free a NAT traversal status object */
|
||||
void natt_free(struct natt_status *status);
|
||||
|
||||
/**
|
||||
* Make a port forwarding request. This may finish immediately or just send a
|
||||
* request to the network. */
|
||||
bool natt_open_port(struct natt_status *status, struct sockaddr *addr, socklen_t addrlen);
|
||||
|
||||
/**
|
||||
* Make a port forwarding request when only the port is known. Forwards any
|
||||
* address it can find. */
|
||||
bool natt_open_port_any(struct natt_status *status, uint16_t port);
|
||||
|
||||
/** Check for port forwarding responses */
|
||||
bool natt_read(struct natt_status *status);
|
192
libretro-common/net/net_natt.c
Normal file
192
libretro-common/net/net_natt.c
Normal file
@ -0,0 +1,192 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <net/net_compat.h>
|
||||
#include <net/net_ifinfo.h>
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#include <net/net_natt.h>
|
||||
|
||||
#if HAVE_MINIUPNPC
|
||||
#include <miniupnpc/miniwget.h>
|
||||
#include <miniupnpc/miniupnpc.h>
|
||||
#include <miniupnpc/upnpcommands.h>
|
||||
|
||||
static struct UPNPUrls urls;
|
||||
static struct IGDdatas data;
|
||||
#endif
|
||||
|
||||
void natt_init(void)
|
||||
{
|
||||
#if HAVE_MINIUPNPC
|
||||
struct UPNPDev * devlist;
|
||||
struct UPNPDev * dev;
|
||||
char * descXML;
|
||||
int descXMLsize = 0;
|
||||
int upnperror = 0;
|
||||
memset(&urls, 0, sizeof(struct UPNPUrls));
|
||||
memset(&data, 0, sizeof(struct IGDdatas));
|
||||
devlist = upnpDiscover(2000, NULL, NULL, 0, 0, &upnperror);
|
||||
if (devlist)
|
||||
{
|
||||
dev = devlist;
|
||||
while (dev)
|
||||
{
|
||||
if (strstr (dev->st, "InternetGatewayDevice"))
|
||||
break;
|
||||
dev = dev->pNext;
|
||||
}
|
||||
if (!dev)
|
||||
dev = devlist;
|
||||
|
||||
descXML = miniwget(dev->descURL, &descXMLsize, 0);
|
||||
if (descXML)
|
||||
{
|
||||
parserootdesc (descXML, descXMLsize, &data);
|
||||
free (descXML); descXML = 0;
|
||||
GetUPNPUrls (&urls, &data, dev->descURL, 0);
|
||||
}
|
||||
freeUPNPDevlist(devlist);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool natt_new(struct natt_status *status)
|
||||
{
|
||||
memset(status, 0, sizeof(struct natt_status));
|
||||
return true;
|
||||
}
|
||||
|
||||
void natt_free(struct natt_status *status)
|
||||
{
|
||||
/* Nothing */
|
||||
}
|
||||
|
||||
bool natt_open_port(struct natt_status *status, struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
#if HAVE_MINIUPNPC
|
||||
char host[PATH_MAX_LENGTH], ext_host[PATH_MAX_LENGTH],
|
||||
port_str[6], ext_port_str[6];
|
||||
struct addrinfo hints = {0}, *ext_addrinfo;
|
||||
int r;
|
||||
|
||||
/* if NAT traversal is uninitialized or unavailable, oh well */
|
||||
if (!urls.controlURL[0])
|
||||
return false;
|
||||
|
||||
/* figure out the internal info */
|
||||
if (getnameinfo(addr, addrlen, host, PATH_MAX_LENGTH, port_str, 6, 0) != 0)
|
||||
return false;
|
||||
|
||||
/* add the port mapping */
|
||||
r = UPNP_AddAnyPortMapping(urls.controlURL, data.first.servicetype, port_str,
|
||||
port_str, host, "retroarch", "TCP", NULL, "3600", ext_port_str);
|
||||
if (r == 501 /* Action Failed */)
|
||||
{
|
||||
/* try the older AddPortMapping */
|
||||
memcpy(ext_port_str, port_str, 6);
|
||||
r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port_str,
|
||||
port_str, host, "retroarch", "TCP", NULL, "3600");
|
||||
}
|
||||
fprintf(stderr, "ERROR: %d\n", r);
|
||||
if (r != 0)
|
||||
return false;
|
||||
|
||||
/* get the external IP */
|
||||
r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, ext_host);
|
||||
if (r != 0)
|
||||
return false;
|
||||
|
||||
/* update the status */
|
||||
if (getaddrinfo_retro(ext_host, ext_port_str, &hints, &ext_addrinfo) != 0)
|
||||
return false;
|
||||
|
||||
if (ext_addrinfo->ai_family == AF_INET &&
|
||||
ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in))
|
||||
{
|
||||
status->have_inet4 = true;
|
||||
status->ext_inet4_addr = *((struct sockaddr_in *) ext_addrinfo->ai_addr);
|
||||
}
|
||||
#ifdef AF_INET6
|
||||
else if (ext_addrinfo->ai_family == AF_INET6 &&
|
||||
ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in6))
|
||||
{
|
||||
status->have_inet6 = true;
|
||||
status->ext_inet6_addr = *((struct sockaddr_in6 *) ext_addrinfo->ai_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
freeaddrinfo_retro(ext_addrinfo);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool natt_open_port_any(struct natt_status *status, uint16_t port)
|
||||
{
|
||||
struct net_ifinfo list;
|
||||
bool ret = false;
|
||||
size_t i;
|
||||
struct addrinfo hints = {0}, *addr;
|
||||
char port_str[6];
|
||||
|
||||
sprintf(port_str, "%hu", port);
|
||||
|
||||
/* get our interfaces */
|
||||
if (!net_ifinfo_new(&list))
|
||||
return false;
|
||||
|
||||
/* loop through them */
|
||||
for (i = 0; i < list.size; i++)
|
||||
{
|
||||
struct net_ifinfo_entry *entry = list.entries + i;
|
||||
|
||||
/* ignore localhost */
|
||||
if (!strcmp(entry->host, "127.0.0.1") || !strcmp(entry->host, "::1"))
|
||||
continue;
|
||||
|
||||
/* make a request for this host */
|
||||
if (getaddrinfo_retro(entry->host, port_str, &hints, &addr) == 0)
|
||||
{
|
||||
ret = natt_open_port(status, addr->ai_addr, addr->ai_addrlen) || ret;
|
||||
freeaddrinfo_retro(addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* BUGS! net_ifinfo_free(&list); */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool natt_read(struct natt_status *status)
|
||||
{
|
||||
/* MiniUPNPC is always synchronous, so there's nothing to read here.
|
||||
* Reserved for future backends. */
|
||||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* If we want to remove redirects in the future, this is a sample of how to do
|
||||
* that */
|
||||
void upnp_rem_redir (int port)
|
||||
{
|
||||
char port_str[16];
|
||||
int t;
|
||||
printf("TB : upnp_rem_redir (%d)\n", port);
|
||||
if(urls.controlURL[0] == '\0')
|
||||
{
|
||||
printf("TB : the init was not done !\n");
|
||||
return;
|
||||
}
|
||||
sprintf(port_str, "%d", port);
|
||||
UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port_str, "TCP", NULL);
|
||||
}
|
||||
#endif
|
@ -182,6 +182,10 @@ if [ "$HAVE_NETWORKING" = 'yes' ]; then
|
||||
fi
|
||||
HAVE_NETWORK_CMD=yes
|
||||
HAVE_NETWORKGAMEPAD=yes
|
||||
|
||||
if [ "$HAVE_MINIUPNPC" != "no" ]; then
|
||||
check_lib MINIUPNPC "-lminiupnpc"
|
||||
fi
|
||||
else
|
||||
echo "Warning: All networking features have been disabled."
|
||||
HAVE_NETWORK_CMD='no'
|
||||
|
@ -28,6 +28,7 @@ HAVE_DYLIB=auto # Dynamic loading support
|
||||
HAVE_NETWORKING=auto # Networking features (recommended)
|
||||
HAVE_NETWORKGAMEPAD=auto # Networked game pad (plus baked-in core)
|
||||
C89_NETWORKGAMEPAD=no
|
||||
HAVE_MINIUPNPC=auto # Mini UPnP client library (for NAT traversal)
|
||||
HAVE_D3D9=yes # Direct3D 9 support
|
||||
HAVE_OPENGL=auto # OpenGL support
|
||||
HAVE_MALI_FBDEV=no # Mali fbdev context support
|
||||
|
Loading…
x
Reference in New Issue
Block a user