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:
Gregor Richards 2016-11-29 21:00:53 -05:00
parent 951f3f3e19
commit 65355994b3
5 changed files with 249 additions and 0 deletions

View File

@ -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)),)

View 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);

View 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

View File

@ -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'

View File

@ -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