diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c8f3502a457..2ee774d6dce7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -709,6 +709,7 @@ add_subdirectory(Externals/semver) include_directories(Externals/semver/include) add_subdirectory(Externals/open-vcdiff) add_subdirectory(Externals/spirv_cross) +add_subdirectory(Externals/libnatpmp) include_directories(Externals) if(ENABLE_VULKAN) diff --git a/Externals/SlippiRustExtensions b/Externals/SlippiRustExtensions index c5643d5d2cb7..af04c6c18787 160000 --- a/Externals/SlippiRustExtensions +++ b/Externals/SlippiRustExtensions @@ -1 +1 @@ -Subproject commit c5643d5d2cb7073b52dcfbdb7836771b264bc33d +Subproject commit af04c6c1878731da5ccc7e1742f355af54755bb5 diff --git a/Externals/libnatpmp/CMakeLists.txt b/Externals/libnatpmp/CMakeLists.txt new file mode 100644 index 000000000000..8ac453652f59 --- /dev/null +++ b/Externals/libnatpmp/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 2.8.12) + +if(POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif() + +project(natpmp) + +set (NATPMP_SOURCES + natpmp.c + getgateway.c +) + +if (WIN32) + set (NATPMP_SOURCES ${NATPMP_SOURCES} wingettimeofday.c) +endif (WIN32) + +# Library itself +add_library(natpmp STATIC ${NATPMP_SOURCES}) +target_include_directories(natpmp PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_compile_definitions(natpmp PRIVATE -DENABLE_STRNATPMPERR) + +if (WIN32) + target_link_libraries(natpmp PUBLIC ws2_32 Iphlpapi) + target_compile_definitions(natpmp PUBLIC -DNATPMP_STATICLIB) +endif (WIN32) +dolphin_disable_warnings_msvc(natpmp) diff --git a/Externals/libnatpmp/Changelog.txt b/Externals/libnatpmp/Changelog.txt new file mode 100644 index 000000000000..959394011b64 --- /dev/null +++ b/Externals/libnatpmp/Changelog.txt @@ -0,0 +1,98 @@ +$Id: Changelog.txt,v 1.34 2023/04/23 10:46:11 nanard Exp $ + +2013/11/26: + enforce strict aliasing rules. + +2013/09/10: + small patch for MSVC >= 16 + rename win32 implementation of gettimeofday() to natpmp_gettimeofday() + +2012/08/21: + Little change in Makefile + removed warnings in testgetgateway.c + Fixed bugs in command line argumentparsing in natpmpc.c + +2011/08/07: + Patch to build on debian/kFreeBSD. + +2011/07/15: + Put 3 clauses BSD licence at the top of source files. + +2011/06/18: + --no-undefined => -Wl,--no-undefined + adding a natpmpc.1 man page + +2011/05/19: + Small fix in libnatpmpmodule.c thanks to Manuel Mausz + +2011/01/03: + Added an argument to initnatpmp() in order to force the gateway to be used + +2011/01/01: + fix in make install + +2010/05/21: + make install now working under MacOSX (and BSD) + +2010/04/12: + cplusplus stuff in natpmp.h + +2010/02/02: + Fixed compilation under Mac OS X + +2009/12/19: + improve and fix building under Windows. + Project files for MS Visual Studio 2008 + More simple (and working) code for Win32. + More checks in the /proc/net/route parsing. Add some comments. + +2009/08/04: + improving getgateway.c for windows + +2009/07/13: + Adding Haiku code in getgateway.c + +2009/06/04: + Adding Python module thanks to David Wu + +2009/03/10: + Trying to have windows get gateway working if not using DHCP + +2009/02/27: + don't include declspec.h if not under WIN32. + +2009/01/23: + Prefixed the libraries name with lib + +2008/10/06: + Fixed a memory leak in getdefaultgateway() (USE_SYSCTL_NET_ROUTE) + +2008/07/03: + Adding WIN32 code from Robbie Hanson + +2008/06/30: + added a Solaris implementation for getgateway(). + added a LICENCE file to the distribution + +2008/05/29: + Anonymous unions are forbidden in ANSI C. That was causing problems with + non-GCC compilers. + +2008/04/28: + introduced strnatpmperr() + improved natpmpc.c sample + make install now install the binary + +2007/12/13: + Fixed getgateway.c for working under OS X ;) + Fixed values for NATPMP_PROTOCOL_TCP and NATPMP_PROTOCOL_UDP + +2007/12/11: + Fixed getgateway.c for compilation under Mac OS X + +2007/12/01: + added some comments in .h + +2007/11/30: + implemented almost everything + diff --git a/Externals/libnatpmp/LICENSE b/Externals/libnatpmp/LICENSE new file mode 100644 index 000000000000..258fecad2dee --- /dev/null +++ b/Externals/libnatpmp/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2007-2022, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Externals/libnatpmp/README b/Externals/libnatpmp/README new file mode 100644 index 000000000000..aa4c2594967e --- /dev/null +++ b/Externals/libnatpmp/README @@ -0,0 +1,8 @@ +libnatpmp (c) 2007-2023 Thomas Bernard +contact : miniupnp@free.fr + +see http://miniupnp.free.fr/libnatpmp.html +or http://miniupnp.tuxfamily.org/libnatpmp.html +for some documentation and code samples. + +github : https://github.com/miniupnp/libnatpmp diff --git a/Externals/libnatpmp/getgateway.c b/Externals/libnatpmp/getgateway.c new file mode 100644 index 000000000000..f4f0305ae4ba --- /dev/null +++ b/Externals/libnatpmp/getgateway.c @@ -0,0 +1,576 @@ +/* $Id: getgateway.c,v 1.26 2023/04/23 10:59:48 nanard Exp $ */ +/* libnatpmp + +Copyright (c) 2007-2014, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#include +#include +#ifndef _WIN32 +#include +#endif +#if !defined(_MSC_VER) +#include +#endif +/* There is no portable method to get the default route gateway. + * So below are four (or five ?) differents functions implementing this. + * Parsing /proc/net/route is for linux. + * sysctl is the way to access such informations on BSD systems. + * Many systems should provide route information through raw PF_ROUTE + * sockets. + * In MS Windows, default gateway is found by looking into the registry + * or by using GetBestRoute(). */ +#ifdef __linux__ +#define USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#if defined(BSD) || defined(__FreeBSD_kernel__) +#undef USE_PROC_NET_ROUTE +#define USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef __APPLE__ +#undef USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#define USE_SYSCTL_NET_ROUTE +#endif + +#if (defined(sun) && defined(__SVR4)) +#undef USE_PROC_NET_ROUTE +#define USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef _WIN32 +#undef USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +//#define USE_WIN32_CODE +#define USE_WIN32_CODE_2 +#endif + +#ifdef __CYGWIN__ +#undef USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#define USE_WIN32_CODE +#include +#include +#include +#include +#endif + +#ifdef __HAIKU__ +#include +#include +#include +#include +#define USE_HAIKU_CODE +#endif + +#ifdef USE_SYSCTL_NET_ROUTE +#include +#include +#include +#include +#endif +#ifdef USE_SOCKET_ROUTE +#include +#include +#include +#include +#include +#endif + +#ifdef USE_WIN32_CODE +#include +#include +#define MAX_KEY_LENGTH 255 +#define MAX_VALUE_LENGTH 16383 +#endif + +#ifdef USE_WIN32_CODE_2 +#include +#include +#include +#endif + +#include "getgateway.h" + +#ifndef _WIN32 +#define SUCCESS (0) +#define FAILED (-1) +#endif + +#if defined(USE_PROC_NET_ROUTE) + +/* + parse /proc/net/route which is as follow : + +Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT +wlan0 0001A8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0 +eth0 0000FEA9 00000000 0001 0 0 0 0000FFFF 0 0 0 +wlan0 00000000 0101A8C0 0003 0 0 0 00000000 0 0 0 +eth0 00000000 00000000 0001 0 0 1000 00000000 0 0 0 + + One header line, and then one line by route by route table entry. +*/ +int getdefaultgateway(in_addr_t * addr) +{ + unsigned long d, g; + char buf[256]; + int line = 0; + FILE * f; + char * p; + f = fopen("/proc/net/route", "r"); + if(!f) + return FAILED; + while(fgets(buf, sizeof(buf), f)) { + if(line > 0) { /* skip the first line */ + p = buf; + /* skip the interface name */ + while(*p && !isspace(*p)) + p++; + while(*p && isspace(*p)) + p++; + if(sscanf(p, "%lx%lx", &d, &g)==2) { + if(d == 0 && g != 0) { /* default */ + *addr = g; + fclose(f); + return SUCCESS; + } + } + } + line++; + } + /* default route not found ! */ + if(f) + fclose(f); + return FAILED; +} + +#elif defined(USE_SYSCTL_NET_ROUTE) + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +int getdefaultgateway(in_addr_t * addr) +{ +#if 0 + /* net.route.0.inet.dump.0.0 ? */ + int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, + NET_RT_DUMP, 0, 0/*tableid*/}; +#endif + /* net.route.0.inet.flags.gateway */ + int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, + NET_RT_FLAGS, RTF_GATEWAY}; + size_t l; + char * buf, * p; + struct rt_msghdr * rt; + struct sockaddr * sa; + struct sockaddr * sa_tab[RTAX_MAX]; + int i; + int r = FAILED; + if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) { + return FAILED; + } + if(l>0) { + buf = malloc(l); + if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) { + free(buf); + return FAILED; + } + for(p=buf; prtm_msglen) { + rt = (struct rt_msghdr *)p; + sa = (struct sockaddr *)(rt + 1); + for(i=0; irtm_addrs & (1 << i)) { + sa_tab[i] = sa; + sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len)); + } else { + sa_tab[i] = NULL; + } + } + if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY)) + && sa_tab[RTAX_DST]->sa_family == AF_INET + && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) { + if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) { + *addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr; + r = SUCCESS; + } + } + } + free(buf); + } + return r; +} + +#elif defined(USE_SOCKET_ROUTE) + +/* Thanks to Darren Kenny for this code */ +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\ + } + +#define rtm m_rtmsg.m_rtm + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +int getdefaultgateway(in_addr_t *addr) +{ + int s, seq, l, rtm_addrs, i; + pid_t pid; + struct sockaddr so_dst, so_mask; + char *cp = m_rtmsg.m_space; + struct sockaddr *gate = NULL, *sa; + struct rt_msghdr *msg_hdr; + + pid = getpid(); + seq = 0; + rtm_addrs = RTA_DST | RTA_NETMASK; + + memset(&so_dst, 0, sizeof(so_dst)); + memset(&so_mask, 0, sizeof(so_mask)); + memset(&rtm, 0, sizeof(struct rt_msghdr)); + + rtm.rtm_type = RTM_GET; + rtm.rtm_flags = RTF_UP | RTF_GATEWAY; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + + so_dst.sa_family = AF_INET; + so_dst.sa_len = sizeof(struct sockaddr); + so_mask.sa_family = AF_INET; + so_mask.sa_len = sizeof(struct sockaddr); + + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_NETMASK, so_mask); + + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + + s = socket(PF_ROUTE, SOCK_RAW, 0); + + if (write(s, (char *)&m_rtmsg, l) < 0) { + close(s); + return FAILED; + } + + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + + close(s); + + msg_hdr = &rtm; + + cp = ((char *)(msg_hdr + 1)); + if (msg_hdr->rtm_addrs) { + for (i = 1; i; i <<= 1) + if (i & msg_hdr->rtm_addrs) { + sa = (struct sockaddr *)cp; + if (i == RTA_GATEWAY ) + gate = sa; + + cp += sizeof(struct sockaddr); + } + } else { + return FAILED; + } + + + if (gate != NULL ) { + *addr = ((struct sockaddr_in *)gate)->sin_addr.s_addr; + return SUCCESS; + } else { + return FAILED; + } +} + +#elif defined(USE_WIN32_CODE) + +int getdefaultgateway(in_addr_t * addr) +{ + HKEY networkCardsKey; + HKEY networkCardKey; + HKEY interfacesKey; + HKEY interfaceKey; + DWORD i = 0; + DWORD numSubKeys = 0; + TCHAR keyName[MAX_KEY_LENGTH]; + DWORD keyNameLength = MAX_KEY_LENGTH; + TCHAR keyValue[MAX_VALUE_LENGTH]; + DWORD keyValueLength = MAX_VALUE_LENGTH; + DWORD keyValueType = REG_SZ; + TCHAR gatewayValue[MAX_VALUE_LENGTH]; + DWORD gatewayValueLength = MAX_VALUE_LENGTH; + DWORD gatewayValueType = REG_MULTI_SZ; + int done = 0; + + //const char * networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; + //const char * interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"; +#ifdef UNICODE + LPCTSTR networkCardsPath = L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; + LPCTSTR interfacesPath = L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"; +#define STR_SERVICENAME L"ServiceName" +#define STR_DHCPDEFAULTGATEWAY L"DhcpDefaultGateway" +#define STR_DEFAULTGATEWAY L"DefaultGateway" +#else + LPCTSTR networkCardsPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"; + LPCTSTR interfacesPath = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"; +#define STR_SERVICENAME "ServiceName" +#define STR_DHCPDEFAULTGATEWAY "DhcpDefaultGateway" +#define STR_DEFAULTGATEWAY "DefaultGateway" +#endif + // The windows registry lists its primary network devices in the following location: + // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards + // + // Each network device has its own subfolder, named with an index, with various properties: + // -NetworkCards + // -5 + // -Description = Broadcom 802.11n Network Adapter + // -ServiceName = {E35A72F8-5065-4097-8DFE-C7790774EE4D} + // -8 + // -Description = Marvell Yukon 88E8058 PCI-E Gigabit Ethernet Controller + // -ServiceName = {86226414-5545-4335-A9D1-5BD7120119AD} + // + // The above service name is the name of a subfolder within: + // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces + // + // There may be more subfolders in this interfaces path than listed in the network cards path above: + // -Interfaces + // -{3a539854-6a70-11db-887c-806e6f6e6963} + // -DhcpIPAddress = 0.0.0.0 + // -[more] + // -{E35A72F8-5065-4097-8DFE-C7790774EE4D} + // -DhcpIPAddress = 10.0.1.4 + // -DhcpDefaultGateway = 10.0.1.1 + // -[more] + // -{86226414-5545-4335-A9D1-5BD7120119AD} + // -DhcpIpAddress = 10.0.1.5 + // -DhcpDefaultGateay = 10.0.1.1 + // -[more] + // + // In order to extract this information, we enumerate each network card, and extract the ServiceName value. + // This is then used to open the interface subfolder, and attempt to extract a DhcpDefaultGateway value. + // Once one is found, we're done. + // + // It may be possible to simply enumerate the interface folders until we find one with a DhcpDefaultGateway value. + // However, the technique used is the technique most cited on the web, and we assume it to be more correct. + + if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predifined key + networkCardsPath, // Name of registry subkey to open + 0, // Reserved - must be zero + KEY_READ, // Mask - desired access rights + &networkCardsKey)) // Pointer to output key + { + // Unable to open network cards keys + return -1; + } + + if(ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, // Open registry key or predefined key + interfacesPath, // Name of registry subkey to open + 0, // Reserved - must be zero + KEY_READ, // Mask - desired access rights + &interfacesKey)) // Pointer to output key + { + // Unable to open interfaces key + RegCloseKey(networkCardsKey); + return -1; + } + + // Figure out how many subfolders are within the NetworkCards folder + RegQueryInfoKey(networkCardsKey, NULL, NULL, NULL, &numSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + //printf( "Number of subkeys: %u\n", (unsigned int)numSubKeys); + + // Enumrate through each subfolder within the NetworkCards folder + for(i = 0; i < numSubKeys && !done; i++) + { + keyNameLength = MAX_KEY_LENGTH; + if(ERROR_SUCCESS == RegEnumKeyEx(networkCardsKey, // Open registry key + i, // Index of subkey to retrieve + keyName, // Buffer that receives the name of the subkey + &keyNameLength, // Variable that receives the size of the above buffer + NULL, // Reserved - must be NULL + NULL, // Buffer that receives the class string + NULL, // Variable that receives the size of the above buffer + NULL)) // Variable that receives the last write time of subkey + { + if(RegOpenKeyEx(networkCardsKey, keyName, 0, KEY_READ, &networkCardKey) == ERROR_SUCCESS) + { + keyValueLength = MAX_VALUE_LENGTH; + if(ERROR_SUCCESS == RegQueryValueEx(networkCardKey, // Open registry key + STR_SERVICENAME, // Name of key to query + NULL, // Reserved - must be NULL + &keyValueType, // Receives value type + (LPBYTE)keyValue, // Receives value + &keyValueLength)) // Receives value length in bytes + { +// printf("keyValue: %s\n", keyValue); + if(RegOpenKeyEx(interfacesKey, keyValue, 0, KEY_READ, &interfaceKey) == ERROR_SUCCESS) + { + gatewayValueLength = MAX_VALUE_LENGTH; + if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey, // Open registry key + STR_DHCPDEFAULTGATEWAY, // Name of key to query + NULL, // Reserved - must be NULL + &gatewayValueType, // Receives value type + (LPBYTE)gatewayValue, // Receives value + &gatewayValueLength)) // Receives value length in bytes + { + // Check to make sure it's a string + if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1)) + { + //printf("gatewayValue: %s\n", gatewayValue); + done = 1; + } + } + else if(ERROR_SUCCESS == RegQueryValueEx(interfaceKey, // Open registry key + STR_DEFAULTGATEWAY, // Name of key to query + NULL, // Reserved - must be NULL + &gatewayValueType, // Receives value type + (LPBYTE)gatewayValue,// Receives value + &gatewayValueLength)) // Receives value length in bytes + { + // Check to make sure it's a string + if((gatewayValueType == REG_MULTI_SZ || gatewayValueType == REG_SZ) && (gatewayValueLength > 1)) + { + //printf("gatewayValue: %s\n", gatewayValue); + done = 1; + } + } + RegCloseKey(interfaceKey); + } + } + RegCloseKey(networkCardKey); + } + } + } + + RegCloseKey(interfacesKey); + RegCloseKey(networkCardsKey); + + if(done) + { +#if UNICODE + char tmp[32]; + for(i = 0; i < 32; i++) { + tmp[i] = (char)gatewayValue[i]; + if(!tmp[i]) + break; + } + tmp[31] = '\0'; + *addr = inet_addr(tmp); +#else + *addr = inet_addr(gatewayValue); +#endif + return 0; + } + + return -1; +} + +#elif defined(USE_WIN32_CODE_2) + +int getdefaultgateway(in_addr_t *addr) +{ + MIB_IPFORWARDROW ip_forward; + memset(&ip_forward, 0, sizeof(ip_forward)); + if(GetBestRoute(inet_addr("0.0.0.0"), 0, &ip_forward) != NO_ERROR) + return -1; + *addr = ip_forward.dwForwardNextHop; + return 0; +} + +#elif defined(USE_HAIKU_CODE) + +int getdefaultgateway(in_addr_t *addr) +{ + int fd, ret = -1; + struct ifconf config; + void *buffer = NULL; + struct ifreq *interface; + + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + return -1; + } + if (ioctl(fd, SIOCGRTSIZE, &config, sizeof(config)) != 0) { + goto fail; + } + if (config.ifc_value < 1) { + goto fail; /* No routes */ + } + if ((buffer = malloc(config.ifc_value)) == NULL) { + goto fail; + } + config.ifc_len = config.ifc_value; + config.ifc_buf = buffer; + if (ioctl(fd, SIOCGRTTABLE, &config, sizeof(config)) != 0) { + goto fail; + } + for (interface = buffer; + (uint8_t *)interface < (uint8_t *)buffer + config.ifc_len; ) { + struct route_entry route = interface->ifr_route; + int intfSize; + if (route.flags & (RTF_GATEWAY | RTF_DEFAULT)) { + *addr = ((struct sockaddr_in *)route.gateway)->sin_addr.s_addr; + ret = 0; + break; + } + intfSize = sizeof(route) + IF_NAMESIZE; + if (route.destination != NULL) { + intfSize += route.destination->sa_len; + } + if (route.mask != NULL) { + intfSize += route.mask->sa_len; + } + if (route.gateway != NULL) { + intfSize += route.gateway->sa_len; + } + interface = (struct ifreq *)((uint8_t *)interface + intfSize); + } +fail: + free(buffer); + close(fd); + return ret; +} + +#else /* fallback */ + +int getdefaultgateway(in_addr_t * addr) +{ + (void)addr; + return -1; +} + +#endif diff --git a/Externals/libnatpmp/getgateway.h b/Externals/libnatpmp/getgateway.h new file mode 100644 index 000000000000..07a0cc23a11a --- /dev/null +++ b/Externals/libnatpmp/getgateway.h @@ -0,0 +1,49 @@ +/* $Id: getgateway.h,v 1.9 2023/04/23 10:59:17 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2014, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __GETGATEWAY_H__ +#define __GETGATEWAY_H__ + +#ifdef _WIN32 +#if !defined(_MSC_VER) || _MSC_VER >= 1600 +#include +#else +typedef unsigned long uint32_t; +typedef unsigned short uint16_t; +#endif +#define in_addr_t uint32_t +#endif +/* #include "declspec.h" */ + +/* getdefaultgateway() : + * return value : + * 0 : success + * -1 : failure */ +/* NATPMP_LIBSPEC */int getdefaultgateway(in_addr_t * addr); + +#endif diff --git a/Externals/libnatpmp/natpmp.c b/Externals/libnatpmp/natpmp.c new file mode 100644 index 000000000000..214456b1bc42 --- /dev/null +++ b/Externals/libnatpmp/natpmp.c @@ -0,0 +1,396 @@ +/* $Id: natpmp.c,v 1.21 2023/04/23 10:50:11 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2018, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifdef __linux__ +#define _DEFAULT_SOURCE 1 +#endif +#include +#include +#if !defined(_MSC_VER) +#include +#endif +#ifdef _WIN32 +#include +#include +#include +#include +#ifdef EWOULDBLOCK +#undef EWOULDBLOCK +#endif +#ifdef ECONNREFUSED +#undef ECONNREFUSED +#endif +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ECONNREFUSED WSAECONNREFUSED +#include "wingettimeofday.h" +#define gettimeofday natpmp_gettimeofday +#else +#include +#include +#include +#include +#include +#define closesocket close +#endif +#include "natpmp.h" +#include "getgateway.h" +#include + +NATPMP_LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw) +{ +#ifdef _WIN32 + u_long ioctlArg = 1; +#else + int flags; +#endif + struct sockaddr_in addr; + if(!p) + return NATPMP_ERR_INVALIDARGS; + memset(p, 0, sizeof(natpmp_t)); + p->s = socket(PF_INET, SOCK_DGRAM, 0); + if(p->s < 0) + return NATPMP_ERR_SOCKETERROR; +#ifdef _WIN32 + if(ioctlsocket(p->s, FIONBIO, &ioctlArg) == SOCKET_ERROR) + return NATPMP_ERR_FCNTLERROR; +#else + if((flags = fcntl(p->s, F_GETFL, 0)) < 0) + return NATPMP_ERR_FCNTLERROR; + if(fcntl(p->s, F_SETFL, flags | O_NONBLOCK) < 0) + return NATPMP_ERR_FCNTLERROR; +#endif + + if(forcegw) { + p->gateway = forcedgw; + } else { + if(getdefaultgateway(&(p->gateway)) < 0) + return NATPMP_ERR_CANNOTGETGATEWAY; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(NATPMP_PORT); + addr.sin_addr.s_addr = p->gateway; + if(connect(p->s, (struct sockaddr *)&addr, sizeof(addr)) < 0) + return NATPMP_ERR_CONNECTERR; + return 0; +} + +NATPMP_LIBSPEC int closenatpmp(natpmp_t * p) +{ + if(!p) + return NATPMP_ERR_INVALIDARGS; + if(closesocket(p->s) < 0) + return NATPMP_ERR_CLOSEERR; + return 0; +} + +int sendpendingrequest(natpmp_t * p) +{ + int r; +/* struct sockaddr_in addr;*/ + if(!p) + return NATPMP_ERR_INVALIDARGS; +/* memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(NATPMP_PORT); + addr.sin_addr.s_addr = p->gateway; + r = (int)sendto(p->s, p->pending_request, p->pending_request_len, 0, + (struct sockaddr *)&addr, sizeof(addr));*/ + r = (int)send(p->s, p->pending_request, p->pending_request_len, 0); + return (r<0) ? NATPMP_ERR_SENDERR : r; +} + +int sendnatpmprequest(natpmp_t * p) +{ + int n; + if(!p) + return NATPMP_ERR_INVALIDARGS; + /* TODO : check if no request is already pending */ + p->has_pending_request = 1; + p->try_number = 1; + n = sendpendingrequest(p); + gettimeofday(&p->retry_time, NULL); // check errors ! + p->retry_time.tv_usec += 250000; /* add 250ms */ + if(p->retry_time.tv_usec >= 1000000) { + p->retry_time.tv_usec -= 1000000; + p->retry_time.tv_sec++; + } + return n; +} + +NATPMP_LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout) +{ + struct timeval now; + if(!p || !timeout) + return NATPMP_ERR_INVALIDARGS; + if(!p->has_pending_request) + return NATPMP_ERR_NOPENDINGREQ; + if(gettimeofday(&now, NULL) < 0) + return NATPMP_ERR_GETTIMEOFDAYERR; + timeout->tv_sec = p->retry_time.tv_sec - now.tv_sec; + timeout->tv_usec = p->retry_time.tv_usec - now.tv_usec; + if(timeout->tv_usec < 0) { + timeout->tv_usec += 1000000; + timeout->tv_sec--; + } + return 0; +} + +NATPMP_LIBSPEC int sendpublicaddressrequest(natpmp_t * p) +{ + if(!p) + return NATPMP_ERR_INVALIDARGS; + //static const unsigned char request[] = { 0, 0 }; + p->pending_request[0] = 0; + p->pending_request[1] = 0; + p->pending_request_len = 2; + // TODO: return 0 instead of sizeof(request) ?? + return sendnatpmprequest(p); +} + +NATPMP_LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol, + uint16_t privateport, uint16_t publicport, + uint32_t lifetime) +{ + if(!p || (protocol!=NATPMP_PROTOCOL_TCP && protocol!=NATPMP_PROTOCOL_UDP)) + return NATPMP_ERR_INVALIDARGS; + p->pending_request[0] = 0; + p->pending_request[1] = protocol; + p->pending_request[2] = 0; + p->pending_request[3] = 0; + /* break strict-aliasing rules : + *((uint16_t *)(p->pending_request + 4)) = htons(privateport); */ + p->pending_request[4] = (privateport >> 8) & 0xff; + p->pending_request[5] = privateport & 0xff; + /* break stric-aliasing rules : + *((uint16_t *)(p->pending_request + 6)) = htons(publicport); */ + p->pending_request[6] = (publicport >> 8) & 0xff; + p->pending_request[7] = publicport & 0xff; + /* break stric-aliasing rules : + *((uint32_t *)(p->pending_request + 8)) = htonl(lifetime); */ + p->pending_request[8] = (lifetime >> 24) & 0xff; + p->pending_request[9] = (lifetime >> 16) & 0xff; + p->pending_request[10] = (lifetime >> 8) & 0xff; + p->pending_request[11] = lifetime & 0xff; + p->pending_request_len = 12; + return sendnatpmprequest(p); +} + +NATPMP_LIBSPEC int readnatpmpresponse(natpmp_t * p, natpmpresp_t * response) +{ + unsigned char buf[16]; + struct sockaddr_in addr; + socklen_t addrlen = sizeof(addr); +#ifdef _WIN32 + int n; +#else + ssize_t n; +#endif + if(!p) + return NATPMP_ERR_INVALIDARGS; + n = recvfrom(p->s, buf, sizeof(buf), 0, + (struct sockaddr *)&addr, &addrlen); + if(n<0) +#ifdef _WIN32 + switch(WSAGetLastError()) { +#else + switch(errno) { +#endif + /*case EAGAIN:*/ + case EWOULDBLOCK: + n = NATPMP_TRYAGAIN; + break; + case ECONNREFUSED: + n = NATPMP_ERR_NOGATEWAYSUPPORT; + break; + default: + n = NATPMP_ERR_RECVFROM; + } + /* check that addr is correct (= gateway) */ + else if(addr.sin_addr.s_addr != p->gateway) + n = NATPMP_ERR_WRONGPACKETSOURCE; + else { + response->resultcode = ntohs(*((uint16_t *)(buf + 2))); + response->epoch = ntohl(*((uint32_t *)(buf + 4))); + if(buf[0] != 0) + n = NATPMP_ERR_UNSUPPORTEDVERSION; + else if(buf[1] < 128 || buf[1] > 130) + n = NATPMP_ERR_UNSUPPORTEDOPCODE; + else if(response->resultcode != 0) { + switch(response->resultcode) { + case 1: + n = NATPMP_ERR_UNSUPPORTEDVERSION; + break; + case 2: + n = NATPMP_ERR_NOTAUTHORIZED; + break; + case 3: + n = NATPMP_ERR_NETWORKFAILURE; + break; + case 4: + n = NATPMP_ERR_OUTOFRESOURCES; + break; + case 5: + n = NATPMP_ERR_UNSUPPORTEDOPCODE; + break; + default: + n = NATPMP_ERR_UNDEFINEDERROR; + } + } else { + response->type = buf[1] & 0x7f; + if(buf[1] == 128) { + //response->publicaddress.addr = *((uint32_t *)(buf + 8)); + response->pnu.publicaddress.addr.s_addr = *((uint32_t *)(buf + 8)); + n = 0; + } else { + /* check that the private port matches our current request */ + if(*(uint16_t*)(p->pending_request + 4) == *((uint16_t*)(buf + 8))) { + response->pnu.newportmapping.privateport = ntohs(*((uint16_t*)(buf + 8))); + response->pnu.newportmapping.mappedpublicport = ntohs(*((uint16_t*)(buf + 10))); + response->pnu.newportmapping.lifetime = ntohl(*((uint32_t*)(buf + 12))); + n = 0; + } else { + /* ignore the old response and continue waiting */ + n = NATPMP_TRYAGAIN; + } + } + } + } + return n; +} + +NATPMP_LIBSPEC int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response) +{ + int n; + if(!p || !response) + return NATPMP_ERR_INVALIDARGS; + if(!p->has_pending_request) + return NATPMP_ERR_NOPENDINGREQ; + n = readnatpmpresponse(p, response); + if(n<0) { + if(n==NATPMP_TRYAGAIN) { + struct timeval now; + gettimeofday(&now, NULL); // check errors ! + if(timercmp(&now, &p->retry_time, >=)) { + int delay, r; + if(p->try_number >= NATPMP_MAX_RETRIES) { + return NATPMP_ERR_NOGATEWAYSUPPORT; + } + /*printf("retry! %d\n", p->try_number);*/ + delay = 250 * (1<try_number); // ms + /*for(i=0; itry_number; i++) + delay += delay;*/ + p->retry_time.tv_sec += (delay / 1000); + p->retry_time.tv_usec += (delay % 1000) * 1000; + if(p->retry_time.tv_usec >= 1000000) { + p->retry_time.tv_usec -= 1000000; + p->retry_time.tv_sec++; + } + p->try_number++; + r = sendpendingrequest(p); + if(r<0) + return r; + } + } + } else { + p->has_pending_request = 0; + } + return n; +} + +#ifdef ENABLE_STRNATPMPERR +NATPMP_LIBSPEC const char * strnatpmperr(int r) +{ + const char * s; + switch(r) { + case NATPMP_ERR_INVALIDARGS: + s = "invalid arguments"; + break; + case NATPMP_ERR_SOCKETERROR: + s = "socket() failed"; + break; + case NATPMP_ERR_CANNOTGETGATEWAY: + s = "cannot get default gateway ip address"; + break; + case NATPMP_ERR_CLOSEERR: +#ifdef _WIN32 + s = "closesocket() failed"; +#else + s = "close() failed"; +#endif + break; + case NATPMP_ERR_RECVFROM: + s = "recvfrom() failed"; + break; + case NATPMP_ERR_NOPENDINGREQ: + s = "no pending request"; + break; + case NATPMP_ERR_NOGATEWAYSUPPORT: + s = "the gateway does not support nat-pmp"; + break; + case NATPMP_ERR_CONNECTERR: + s = "connect() failed"; + break; + case NATPMP_ERR_WRONGPACKETSOURCE: + s = "packet not received from the default gateway"; + break; + case NATPMP_ERR_SENDERR: + s = "send() failed"; + break; + case NATPMP_ERR_FCNTLERROR: + s = "fcntl() failed"; + break; + case NATPMP_ERR_GETTIMEOFDAYERR: + s = "gettimeofday() failed"; + break; + case NATPMP_ERR_UNSUPPORTEDVERSION: + s = "unsupported nat-pmp version error from server"; + break; + case NATPMP_ERR_UNSUPPORTEDOPCODE: + s = "unsupported nat-pmp opcode error from server"; + break; + case NATPMP_ERR_UNDEFINEDERROR: + s = "undefined nat-pmp server error"; + break; + case NATPMP_ERR_NOTAUTHORIZED: + s = "not authorized"; + break; + case NATPMP_ERR_NETWORKFAILURE: + s = "network failure"; + break; + case NATPMP_ERR_OUTOFRESOURCES: + s = "nat-pmp server out of resources"; + break; + default: + s = "Unknown libnatpmp error"; + } + return s; +} +#endif + diff --git a/Externals/libnatpmp/natpmp.h b/Externals/libnatpmp/natpmp.h new file mode 100644 index 000000000000..56422551a868 --- /dev/null +++ b/Externals/libnatpmp/natpmp.h @@ -0,0 +1,222 @@ +/* $Id: natpmp.h,v 1.22 2023/04/23 10:50:11 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2014, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __NATPMP_H__ +#define __NATPMP_H__ + +/* NAT-PMP Port as defined by the NAT-PMP draft */ +#define NATPMP_PORT (5351) + +#include +#if !defined(_MSC_VER) +#include +#endif /* !defined(_MSC_VER) */ + +#ifdef _WIN32 +#include +#if !defined(_MSC_VER) || _MSC_VER >= 1600 +#include +#else /* !defined(_MSC_VER) || _MSC_VER >= 1600 */ +typedef unsigned long uint32_t; +typedef unsigned short uint16_t; +#endif /* !defined(_MSC_VER) || _MSC_VER >= 1600 */ +#define in_addr_t uint32_t +#else /* _WIN32 */ +#include +#endif /* _WIN32 */ +#include "natpmp_declspec.h" + +/* Set to 9 by https://tools.ietf.org/html/rfc6886#section-3.1 which leads to a + * maximum timeout of 127.75 seconds, due to the initial 250 ms timeout doubling + * each time, so we allow a compile-time modification here.*/ +#ifndef NATPMP_MAX_RETRIES +#define NATPMP_MAX_RETRIES (9) +#endif + +typedef struct { + int s; /* socket */ + in_addr_t gateway; /* default gateway (IPv4) */ + int has_pending_request; + unsigned char pending_request[12]; + int pending_request_len; + int try_number; + struct timeval retry_time; +} natpmp_t; + +typedef struct { + uint16_t type; /* NATPMP_RESPTYPE_* */ + uint16_t resultcode; /* NAT-PMP response code */ + uint32_t epoch; /* Seconds since start of epoch */ + union { + struct { + //in_addr_t addr; + struct in_addr addr; + } publicaddress; + struct { + uint16_t privateport; + uint16_t mappedpublicport; + uint32_t lifetime; + } newportmapping; + } pnu; +} natpmpresp_t; + +/* possible values for type field of natpmpresp_t */ +#define NATPMP_RESPTYPE_PUBLICADDRESS (0) +#define NATPMP_RESPTYPE_UDPPORTMAPPING (1) +#define NATPMP_RESPTYPE_TCPPORTMAPPING (2) + +/* Values to pass to sendnewportmappingrequest() */ +#define NATPMP_PROTOCOL_UDP (1) +#define NATPMP_PROTOCOL_TCP (2) + +/* return values */ +/* NATPMP_ERR_INVALIDARGS : invalid arguments passed to the function */ +#define NATPMP_ERR_INVALIDARGS (-1) +/* NATPMP_ERR_SOCKETERROR : socket() failed. check errno for details */ +#define NATPMP_ERR_SOCKETERROR (-2) +/* NATPMP_ERR_CANNOTGETGATEWAY : can't get default gateway IP */ +#define NATPMP_ERR_CANNOTGETGATEWAY (-3) +/* NATPMP_ERR_CLOSEERR : close() failed. check errno for details */ +#define NATPMP_ERR_CLOSEERR (-4) +/* NATPMP_ERR_RECVFROM : recvfrom() failed. check errno for details */ +#define NATPMP_ERR_RECVFROM (-5) +/* NATPMP_ERR_NOPENDINGREQ : readnatpmpresponseorretry() called while + * no NAT-PMP request was pending */ +#define NATPMP_ERR_NOPENDINGREQ (-6) +/* NATPMP_ERR_NOGATEWAYSUPPORT : the gateway does not support NAT-PMP */ +#define NATPMP_ERR_NOGATEWAYSUPPORT (-7) +/* NATPMP_ERR_CONNECTERR : connect() failed. check errno for details */ +#define NATPMP_ERR_CONNECTERR (-8) +/* NATPMP_ERR_WRONGPACKETSOURCE : packet not received from the network gateway */ +#define NATPMP_ERR_WRONGPACKETSOURCE (-9) +/* NATPMP_ERR_SENDERR : send() failed. check errno for details */ +#define NATPMP_ERR_SENDERR (-10) +/* NATPMP_ERR_FCNTLERROR : fcntl() failed. check errno for details */ +#define NATPMP_ERR_FCNTLERROR (-11) +/* NATPMP_ERR_GETTIMEOFDAYERR : gettimeofday() failed. check errno for details */ +#define NATPMP_ERR_GETTIMEOFDAYERR (-12) + +/* */ +#define NATPMP_ERR_UNSUPPORTEDVERSION (-14) +#define NATPMP_ERR_UNSUPPORTEDOPCODE (-15) + +/* Errors from the server : */ +#define NATPMP_ERR_UNDEFINEDERROR (-49) +#define NATPMP_ERR_NOTAUTHORIZED (-51) +#define NATPMP_ERR_NETWORKFAILURE (-52) +#define NATPMP_ERR_OUTOFRESOURCES (-53) + +/* NATPMP_TRYAGAIN : no data available for the moment. try again later */ +#define NATPMP_TRYAGAIN (-100) + +#ifdef __cplusplus +extern "C" { +#endif + +/* initnatpmp() + * initialize a natpmp_t object + * With forcegw=1 the gateway is not detected automaticaly. + * Return values : + * 0 = OK + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_SOCKETERROR + * NATPMP_ERR_FCNTLERROR + * NATPMP_ERR_CANNOTGETGATEWAY + * NATPMP_ERR_CONNECTERR */ +NATPMP_LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw); + +/* closenatpmp() + * close resources associated with a natpmp_t object + * Return values : + * 0 = OK + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_CLOSEERR */ +NATPMP_LIBSPEC int closenatpmp(natpmp_t * p); + +/* sendpublicaddressrequest() + * send a public address NAT-PMP request to the network gateway + * Return values : + * 2 = OK (size of the request) + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_SENDERR */ +NATPMP_LIBSPEC int sendpublicaddressrequest(natpmp_t * p); + +/* sendnewportmappingrequest() + * send a new port mapping NAT-PMP request to the network gateway + * Arguments : + * protocol is either NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP, + * lifetime is in seconds. + * To remove a port mapping, set lifetime to zero. + * To remove all port mappings to the host, set lifetime and both ports + * to zero. + * Return values : + * 12 = OK (size of the request) + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_SENDERR */ +NATPMP_LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol, + uint16_t privateport, uint16_t publicport, + uint32_t lifetime); + +/* getnatpmprequesttimeout() + * fills the timeval structure with the timeout duration of the + * currently pending NAT-PMP request. + * Return values : + * 0 = OK + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_GETTIMEOFDAYERR + * NATPMP_ERR_NOPENDINGREQ */ +NATPMP_LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout); + +/* readnatpmpresponseorretry() + * fills the natpmpresp_t structure if possible + * Return values : + * 0 = OK + * NATPMP_TRYAGAIN + * NATPMP_ERR_INVALIDARGS + * NATPMP_ERR_NOPENDINGREQ + * NATPMP_ERR_NOGATEWAYSUPPORT + * NATPMP_ERR_RECVFROM + * NATPMP_ERR_WRONGPACKETSOURCE + * NATPMP_ERR_UNSUPPORTEDVERSION + * NATPMP_ERR_UNSUPPORTEDOPCODE + * NATPMP_ERR_NOTAUTHORIZED + * NATPMP_ERR_NETWORKFAILURE + * NATPMP_ERR_OUTOFRESOURCES + * NATPMP_ERR_UNSUPPORTEDOPCODE + * NATPMP_ERR_UNDEFINEDERROR */ +NATPMP_LIBSPEC int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response); + +#ifdef ENABLE_STRNATPMPERR +NATPMP_LIBSPEC const char * strnatpmperr(int t); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Externals/libnatpmp/natpmp_declspec.h b/Externals/libnatpmp/natpmp_declspec.h new file mode 100644 index 000000000000..49ce27f933ca --- /dev/null +++ b/Externals/libnatpmp/natpmp_declspec.h @@ -0,0 +1,21 @@ +#ifndef NATPMP_DECLSPEC_H_INCLUDED +#define NATPMP_DECLSPEC_H_INCLUDED + +#if defined(_WIN32) && !defined(NATPMP_STATICLIB) + /* for windows dll */ + #ifdef NATPMP_EXPORTS + #define NATPMP_LIBSPEC __declspec(dllexport) + #else + #define NATPMP_LIBSPEC __declspec(dllimport) + #endif +#else + #if defined(__GNUC__) && __GNUC__ >= 4 + /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */ + #define NATPMP_LIBSPEC __attribute__ ((visibility ("default"))) + #else + #define NATPMP_LIBSPEC + #endif +#endif + +#endif + diff --git a/Externals/libnatpmp/wingettimeofday.c b/Externals/libnatpmp/wingettimeofday.c new file mode 100644 index 000000000000..538223351123 --- /dev/null +++ b/Externals/libnatpmp/wingettimeofday.c @@ -0,0 +1,60 @@ +/* $Id: wingettimeofday.c,v 1.7 2023/04/23 10:46:42 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2013, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifdef _WIN32 +#if defined(_MSC_VER) +struct timeval { + long tv_sec; + long tv_usec; +}; +#else +#include +#endif + +typedef struct _FILETIME { + unsigned long dwLowDateTime; + unsigned long dwHighDateTime; +} FILETIME; + +void __stdcall GetSystemTimeAsFileTime(FILETIME*); + +int natpmp_gettimeofday(struct timeval* p, void* tz /* IGNORED */) { + union { + long long ns100; /*time since 1 Jan 1601 in 100ns units */ + FILETIME ft; + } _now; + + if(!p) + return -1; + GetSystemTimeAsFileTime( &(_now.ft) ); + p->tv_usec =(long)((_now.ns100 / 10LL) % 1000000LL ); + p->tv_sec = (long)((_now.ns100-(116444736000000000LL))/10000000LL); + return 0; +} +#endif + diff --git a/Externals/libnatpmp/wingettimeofday.h b/Externals/libnatpmp/wingettimeofday.h new file mode 100644 index 000000000000..3143e8c209a1 --- /dev/null +++ b/Externals/libnatpmp/wingettimeofday.h @@ -0,0 +1,39 @@ +/* $Id: wingettimeofday.h,v 1.6 2023/04/23 10:46:42 nanard Exp $ */ +/* libnatpmp +Copyright (c) 2007-2013, Thomas BERNARD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef __WINGETTIMEOFDAY_H__ +#define __WINGETTIMEOFDAY_H__ +#ifdef _WIN32 +#if defined(_MSC_VER) +#include +#else +#include +#endif +int natpmp_gettimeofday(struct timeval* p, void* tz /* IGNORED */); +#endif +#endif diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index 0ff3357b9690..726aa612b044 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -94,6 +94,8 @@ add_library(common MsgHandler.h NandPaths.cpp NandPaths.h + NATPMP.cpp + NATPMP.h Network.cpp Network.h PcapFile.cpp @@ -154,7 +156,8 @@ PUBLIC fmt::fmt ${MBEDTLS_LIBRARIES} minizip-ng - slippi-rust-extensions + natpmp + slippi_rust_extensions PRIVATE ${CURL_LIBRARIES} diff --git a/Source/Core/Common/NATPMP.cpp b/Source/Core/Common/NATPMP.cpp new file mode 100644 index 000000000000..a408c48d85f4 --- /dev/null +++ b/Source/Core/Common/NATPMP.cpp @@ -0,0 +1,145 @@ +#include "Common/NATPMP.h" +#include "Common/Logging/Log.h" +#include +#include + +#ifndef _WIN32 +#include +#endif + +static u16 s_mapped = 0; +static natpmp_t s_natpmp; +static std::thread s_thread; + +// called from ---NATPMP--- thread +static int GetNatpmpResponse(natpmpresp_t* response) +{ + int result; + int i = 0; + do + { + fd_set fds; + struct timeval timeout; + FD_ZERO(&fds); + FD_SET(s_natpmp.s, &fds); + result = getnatpmprequesttimeout(&s_natpmp, &timeout); + if (result != 0) + break; + + select(FD_SETSIZE, &fds, NULL, NULL, &timeout); + result = readnatpmpresponseorretry(&s_natpmp, response); + i++; + } while (i < 3 && result == NATPMP_TRYAGAIN); + // 3 tries takes 1750ms. Doesn't seem good to wait longer than that. + + return result; +} + +// called from ---NATPMP--- thread +// discovers the NAT-PMP/PCP gateway +static bool InitNATPMP() +{ + static bool s_natpmpInited = false; + static bool s_natpmpError = false; + + // Don't init if already inited + if (s_natpmpInited) + return true; + + // Don't init if it failed before + if (s_natpmpError) + return false; + + int result = initnatpmp(&s_natpmp, /* forcegw */ 0, /* forcedgw */ 0); + if (result != 0) + { + WARN_LOG_FMT(NETPLAY, "[NAT-PMP] initnatpmp failed: {}", result); + s_natpmpError = true; + return false; + } + result = sendpublicaddressrequest(&s_natpmp); + if (result != 2) + { + WARN_LOG_FMT(NETPLAY, "[NAT-PMP] sendpublicaddressrequest failed: {}", result); + s_natpmpError = true; + return false; + } + natpmpresp_t response; + result = GetNatpmpResponse(&response); + if (result != 0) + { + WARN_LOG_FMT(NETPLAY, "[NAT-PMP] publicaddress error: {}", result); + s_natpmpError = true; + return false; + } + + WARN_LOG_FMT(NETPLAY, "[NAT-PMP] Inited"); + s_natpmpInited = true; + return true; +} + +// called from ---NATPMP--- thread +static void UnmapPort() +{ + sendnewportmappingrequest(&s_natpmp, NATPMP_PROTOCOL_UDP, s_mapped, s_mapped, 0); + natpmpresp_t response; + GetNatpmpResponse(&response); + s_mapped = 0; +} + +// called from ---NATPMP--- thread +static bool MapPort(const u16 port) +{ + if (s_mapped > 0 && s_mapped != port) + UnmapPort(); + + int result = sendnewportmappingrequest(&s_natpmp, NATPMP_PROTOCOL_UDP, port, port, 604800); + if (result != 12) + { + WARN_LOG_FMT(NETPLAY, "[NAT-PMP] sendnewportmappingrequest failed: {}", result); + return false; + } + natpmpresp_t response; + result = GetNatpmpResponse(&response); + if (result != 0) + { + WARN_LOG_FMT(NETPLAY, "[NAT-PMP] portmapping error: {}", result); + return false; + } + + s_mapped = port; + return true; +} + +static void MapPortThread(const u16 port) +{ + if (InitNATPMP() && MapPort(port)) + { + NOTICE_LOG_FMT(NETPLAY, "[NAT-PMP] Successfully mapped port {}.", port); + return; + } + + WARN_LOG_FMT(NETPLAY, "[NAT-PMP] Failed to map port {}.", port); +} + +static void UnmapPortThread() +{ + if (s_mapped > 0) + UnmapPort(); +} + +void NATPMP::TryPortmappingBlocking(u16 port) +{ + if (s_thread.joinable()) + s_thread.join(); + s_thread = std::thread(&MapPortThread, port); + s_thread.join(); +} + +void NATPMP::StopPortmapping() +{ + if (s_thread.joinable()) + s_thread.join(); + s_thread = std::thread(&UnmapPortThread); + s_thread.join(); +} diff --git a/Source/Core/Common/NATPMP.h b/Source/Core/Common/NATPMP.h new file mode 100644 index 000000000000..ee662ad8699a --- /dev/null +++ b/Source/Core/Common/NATPMP.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Common/CommonTypes.h" + +namespace NATPMP +{ +void TryPortmappingBlocking(u16 port); +void StopPortmapping(); +} diff --git a/Source/Core/Common/UPnP.cpp b/Source/Core/Common/UPnP.cpp index 9552236e69ec..0bdc4947e207 100644 --- a/Source/Core/Common/UPnP.cpp +++ b/Source/Core/Common/UPnP.cpp @@ -54,11 +54,11 @@ static bool InitUPnP() { if (upnperror == UPNPDISCOVER_SUCCESS) { - WARN_LOG_FMT(NETPLAY, "No UPnP devices could be found."); + WARN_LOG_FMT(NETPLAY, "[UPnP] No UPnP devices could be found."); } else { - WARN_LOG_FMT(NETPLAY, "An error occurred trying to discover UPnP devices: {}", + WARN_LOG_FMT(NETPLAY, "[UPnP] An error occurred trying to discover UPnP devices: {}", strupnperror(upnperror)); } @@ -91,17 +91,17 @@ static bool InitUPnP() GetUPNPUrls(&s_urls, &s_data, dev->descURL, 0); found_valid_igd = true; - NOTICE_LOG_FMT(NETPLAY, "Got info from IGD at {}.", dev->descURL); + NOTICE_LOG_FMT(NETPLAY, "[UPnP] Got info from IGD at {}.", dev->descURL); break; } else { - WARN_LOG_FMT(NETPLAY, "Error getting info from IGD at {}.", dev->descURL); + WARN_LOG_FMT(NETPLAY, "[UPnP] Error getting info from IGD at {}.", dev->descURL); } } if (!found_valid_igd) - WARN_LOG_FMT(NETPLAY, "Could not find a valid IGD in the discovered UPnP devices."); + WARN_LOG_FMT(NETPLAY, "[UPnP] Could not find a valid IGD in the discovered UPnP devices."); s_inited = true; @@ -150,11 +150,11 @@ static void MapPortThread(const u16 port) { if (InitUPnP() && MapPort(s_our_ip.data(), port)) { - NOTICE_LOG_FMT(NETPLAY, "Successfully mapped port {} to {}.", port, s_our_ip.data()); + NOTICE_LOG_FMT(NETPLAY, "[UPnP] Successfully mapped port {} to {}.", port, s_our_ip.data()); return; } - WARN_LOG_FMT(NETPLAY, "Failed to map port {} to {}.", port, s_our_ip.data()); + WARN_LOG_FMT(NETPLAY, "[UPnP] Failed to map port {} to {}.", port, s_our_ip.data()); } // UPnP thread: try to unmap a port @@ -171,6 +171,12 @@ void UPnP::TryPortmapping(u16 port) s_thread = std::thread(&MapPortThread, port); } +void UPnP::TryPortmappingBlocking(u16 port) +{ + TryPortmapping(port); + s_thread.join(); +} + void UPnP::StopPortmapping() { if (s_thread.joinable()) diff --git a/Source/Core/Common/UPnP.h b/Source/Core/Common/UPnP.h index 3013cf56d28f..998b8e99553f 100644 --- a/Source/Core/Common/UPnP.h +++ b/Source/Core/Common/UPnP.h @@ -10,6 +10,7 @@ namespace UPnP { void TryPortmapping(u16 port); +void TryPortmappingBlocking(u16 port); void StopPortmapping(); } // namespace UPnP diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 382e13fa5ec6..f91d9a384875 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -52,6 +52,8 @@ const Info SLIPPI_ENABLE_FRAME_INDEX{{System::Main, "Slippi", "EnableFrame const Info SLIPPI_BLOCKING_PIPES{{System::Main, "Slippi", "BlockingPipes"}, false}; const Info SLIPPI_ENABLE_JUKEBOX{{System::Main, "Slippi", "EnableJukebox"}, true}; const Info SLIPPI_JUKEBOX_VOLUME{{System::Main, "Slippi", "JukeboxVolume"}, 100}; +const Info SLIPPI_PORT_MAPPING{{System::Main, "Slippi", "PortMapping"}, + Slippi::PortMapping::OFF}; // Playback Settings const Info SLIPPI_ENABLE_SEEK{{System::Main, "Slippi", "EnableSeek"}, true}; diff --git a/Source/Core/Core/Config/MainSettings.h b/Source/Core/Core/Config/MainSettings.h index 8a870843a2d4..a2dcc5a5feb9 100644 --- a/Source/Core/Core/Config/MainSettings.h +++ b/Source/Core/Core/Config/MainSettings.h @@ -69,6 +69,7 @@ extern const Info SLIPPI_ENABLE_FRAME_INDEX; extern const Info SLIPPI_BLOCKING_PIPES; extern const Info SLIPPI_ENABLE_JUKEBOX; extern const Info SLIPPI_JUKEBOX_VOLUME; +extern const Info SLIPPI_PORT_MAPPING; // Playback Settings extern const Info SLIPPI_ENABLE_SEEK; diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp index e8435c1713a4..3313c2d74c19 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp @@ -11,9 +11,11 @@ #include "Common/Logging/Log.h" #include "Common/MemoryUtil.h" #include "Common/MsgHandler.h" +#include "Common/NATPMP.h" #include "Common/StringUtil.h" #include "Common/Thread.h" #include "Common/Version.h" +#include "Common/UPnP.h" #include "AudioCommon/AudioCommon.h" @@ -313,6 +315,13 @@ CEXISlippi::~CEXISlippi() // you'd have to be kinda dumb to do that sequence of stuff anyway so maybe it's nbd if (is_enet_initialized) enet_deinitialize(); + + // Unconditionally stop port mapping since it's possible to change the setting mid-game (after + // we've started port mapping). + NATPMP::StopPortmapping(); +#ifdef USE_UPNP + UPnP::StopPortmapping(); +#endif } void CEXISlippi::configureCommands(u8* payload, u8 length) diff --git a/Source/Core/Core/Slippi/SlippiConfig.h b/Source/Core/Core/Slippi/SlippiConfig.h index 7b8467c770b7..6cf89ce56767 100644 --- a/Source/Core/Core/Slippi/SlippiConfig.h +++ b/Source/Core/Core/Slippi/SlippiConfig.h @@ -21,6 +21,13 @@ enum class Chat OFF }; +enum class PortMapping +{ + OFF, + UPNP, + NATPMP +}; + struct Config { Melee::Version melee_version; diff --git a/Source/Core/Core/Slippi/SlippiMatchmaking.cpp b/Source/Core/Core/Slippi/SlippiMatchmaking.cpp index 98f4ac965880..b600ef188287 100644 --- a/Source/Core/Core/Slippi/SlippiMatchmaking.cpp +++ b/Source/Core/Core/Slippi/SlippiMatchmaking.cpp @@ -4,8 +4,10 @@ #include "Common/Common.h" #include "Common/ENetUtil.h" #include "Common/Logging/Log.h" +#include "Common/NATPMP.h" #include "Common/StringUtil.h" #include "Common/Version.h" +#include "Common/UPnP.h" #include "Core/Config/MainSettings.h" #include "Core/ConfigManager.h" @@ -310,6 +312,18 @@ void SlippiMatchmaking::startMatchmaking() return; } + Slippi::PortMapping portMapping = Config::Get(Config::SLIPPI_PORT_MAPPING); + if (portMapping == Slippi::PortMapping::NATPMP) + { + NATPMP::TryPortmappingBlocking(m_host_port); + } +#ifdef USE_UPNP + else if (portMapping == Slippi::PortMapping::UPNP) + { + UPnP::TryPortmappingBlocking(m_host_port); + } +#endif + ENetAddress addr; enet_address_set_host(&addr, MM_HOST.c_str()); addr.port = MM_PORT; diff --git a/Source/Core/DolphinQt/Settings/SlippiPane.cpp b/Source/Core/DolphinQt/Settings/SlippiPane.cpp index 470d5cc21581..304b8d08ef03 100644 --- a/Source/Core/DolphinQt/Settings/SlippiPane.cpp +++ b/Source/Core/DolphinQt/Settings/SlippiPane.cpp @@ -83,6 +83,15 @@ void SlippiPane::CreateLayout() } online_settings_layout->addRow(tr("Quick Chat:"), m_netplay_quick_chat_combo); + m_port_mapping_combo = new QComboBox(); + for (const auto& item : { tr("Off"), tr("UPnP"), tr("NAT-PMP") }) + { + m_port_mapping_combo->addItem(item); + } + m_port_mapping_combo->setToolTip(tr("Enabling this may improve connecting to other players if UPnP or NAT-PMP is available on your router. " + "Do not use if you've already configured DMZ or port forwarding on your router.")); + online_settings_layout->addRow(tr("Port Mapping: "), m_port_mapping_combo); + m_netplay_port = new QSpinBox(); m_netplay_port->setFixedSize(60, 25); QSizePolicy sp_retain = m_netplay_port->sizePolicy(); @@ -206,6 +215,8 @@ void SlippiPane::LoadConfig() m_netplay_quick_chat_combo->setCurrentIndex( static_cast(Config::Get(Config::SLIPPI_ENABLE_QUICK_CHAT))); + m_port_mapping_combo->setCurrentIndex(static_cast(Config::Get(Config::SLIPPI_PORT_MAPPING))); + auto force_netplay_port = Config::Get(Config::SLIPPI_FORCE_NETPLAY_PORT); m_force_netplay_port->setChecked(force_netplay_port); m_netplay_port->setValue(Config::Get(Config::SLIPPI_NETPLAY_PORT)); @@ -241,6 +252,10 @@ void SlippiPane::ConnectLayout() [](int index) { Config::SetBase(Config::SLIPPI_ENABLE_QUICK_CHAT, static_cast(index)); }); + connect(m_port_mapping_combo, qOverload(&QComboBox::currentIndexChanged), this, + [](int index) { + Config::SetBase(Config::SLIPPI_PORT_MAPPING, static_cast(index)); + }); connect(m_force_netplay_port, &QCheckBox::toggled, this, &SlippiPane::SetForceNetplayPort); connect(m_netplay_port, qOverload(&QSpinBox::valueChanged), this, [](int port) { Config::SetBase(Config::SLIPPI_NETPLAY_PORT, port); }); diff --git a/Source/Core/DolphinQt/Settings/SlippiPane.h b/Source/Core/DolphinQt/Settings/SlippiPane.h index 438d612c7a81..5038fadef056 100644 --- a/Source/Core/DolphinQt/Settings/SlippiPane.h +++ b/Source/Core/DolphinQt/Settings/SlippiPane.h @@ -47,6 +47,7 @@ class SlippiPane final : public QWidget // Online Settings QSpinBox* m_delay_spin; QComboBox* m_netplay_quick_chat_combo; + QComboBox* m_port_mapping_combo; QCheckBox* m_force_netplay_port; QSpinBox* m_netplay_port;