diff --git a/Externals/miniupnpc/CMakeLists.txt b/Externals/miniupnpc/CMakeLists.txt index 9d441b269d..27afc7575a 100644 --- a/Externals/miniupnpc/CMakeLists.txt +++ b/Externals/miniupnpc/CMakeLists.txt @@ -1,39 +1,318 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) -project(miniupnpc C) -set(MINIUPNPC_VERSION 1.9) -set(MINIUPNPC_API_VERSION 14) +project (miniupnpc + VERSION 2.3.2 + DESCRIPTION "UPnP IGD client lightweight library" + HOMEPAGE_URL https://miniupnp.tuxfamily.org/ + LANGUAGES C) -if(UNIX) - add_definitions(-DMINIUPNPC_SET_SOCKET_TIMEOUT) - add_definitions(-D_BSD_SOURCE -D_POSIX_C_SOURCE=1) -elseif(WIN32) - add_definitions(-D_WIN32_WINNT=0x0501) - find_library(WINSOCK2_LIBRARY NAMES ws2_32 WS2_32 Ws2_32) - find_library(IPHLPAPI_LIBRARY NAMES iphlpapi) - set(LDLIBS ${WINSOCK2_LIBRARY} ${IPHLPAPI_LIBRARY} ${LDLIBS}) +set (MINIUPNPC_API_VERSION 20) + +option (UPNPC_BUILD_STATIC "Build static library" TRUE) +option (UPNPC_BUILD_SHARED "Build shared library" FALSE) +option (UPNPC_BUILD_TESTS "Build test executables" FALSE) +option (UPNPC_BUILD_SAMPLE "Build sample executables" FALSE) +option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE) +option (UPNPC_NO_INSTALL "Disable installation" FALSE) + +if (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED) + message (FATAL "Both shared and static libraries are disabled!") +endif () + +include(GNUInstallDirs) + +# Interface library for compile definitions, flags and option +add_library(miniupnpc-private INTERFACE) + +if (NO_GETADDRINFO) + target_compile_definitions(miniupnpc-private INTERFACE NO_GETADDRINFO) +endif () + +if (NOT WIN32) + target_compile_definitions(miniupnpc-private INTERFACE + MINIUPNPC_SET_SOCKET_TIMEOUT + MINIUPNPC_GET_SRC_ADDR + _BSD_SOURCE _DEFAULT_SOURCE) + if (NOT APPLE AND NOT CMAKE_SYSTEM_NAME MATCHES ".*BSD" AND NOT CMAKE_SYSTEM_NAME STREQUAL "SunOS") + # add_definitions (-D_POSIX_C_SOURCE=200112L) + target_compile_definitions(miniupnpc-private INTERFACE _XOPEN_SOURCE=600) + endif () + if (CMAKE_SYSTEM_NAME STREQUAL "NetBSD") + target_compile_definitions(miniupnpc-private INTERFACE _NETBSD_SOURCE) + endif () +else () + set (MINIUPNPC_TARGET_WINDOWS_VERSION "0x0501" CACHE STRING "Minimum target Windows version as hex string") # XP or higher for getnameinfo and friends + if (MINIUPNPC_TARGET_WINDOWS_VERSION) + target_compile_definitions(miniupnpc-private INTERFACE _WIN32_WINNT=${MINIUPNPC_TARGET_WINDOWS_VERSION}) + endif () +endif () + +if (APPLE) + target_compile_definitions(miniupnpc-private INTERFACE _DARWIN_C_SOURCE) +endif () + +# Set compiler specific build flags +if (CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + target_compile_options(miniupnpc-private INTERFACE -Wall) +endif () + +# Suppress noise warnings +if (MSVC) + target_compile_definitions(miniupnpc-private INTERFACE _CRT_SECURE_NO_WARNINGS _WINSOCK_DEPRECATED_NO_WARNINGS) endif() -if(APPLE) - add_definitions(-DMACOSX -D_DARWIN_C_SOURCE) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/miniupnpcstrings.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h) +target_include_directories(miniupnpc-private INTERFACE $) + +set (MINIUPNPC_SOURCES + src/igd_desc_parse.c + src/miniupnpc.c + src/minixml.c + src/minisoap.c + src/minissdpc.c + src/miniwget.c + src/upnpcommands.c + src/upnpdev.c + src/upnpreplyparse.c + src/upnperrors.c + src/connecthostport.c + src/portlistingparse.c + src/receivedata.c + src/addr_is_reserved.c + ${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h +) + +if (WIN32) + target_link_libraries(miniupnpc-private INTERFACE ws2_32 iphlpapi) +elseif (CMAKE_SYSTEM_NAME STREQUAL "SunOS") + target_link_libraries(miniupnpc-private INTERFACE socket nsl resolv) + find_library (SOCKET_LIBRARY NAMES socket) + find_library (NSL_LIBRARY NAMES nsl) + find_library (RESOLV_LIBRARY NAMES resolv) + set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS}) +elseif (HAIKU) + target_link_libraries(miniupnpc-private INTERFACE network) + find_library (SOCKET_LIBRARY NAMES network) + find_library (NSL_LIBRARY NAMES network) + find_library (RESOLV_LIBRARY NAMES network) + set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS}) +endif () + + +if (UPNPC_BUILD_STATIC) + add_library (libminiupnpc-static STATIC ${MINIUPNPC_SOURCES}) + target_include_directories (libminiupnpc-static PUBLIC + $ + $) + if (NOT UPNPC_BUILD_SHARED) + add_library (miniupnpc::miniupnpc ALIAS libminiupnpc-static) + endif() + set_target_properties (libminiupnpc-static PROPERTIES EXPORT_NAME miniupnpc) + if (WIN32 AND NOT MINGW) + set_target_properties (libminiupnpc-static PROPERTIES OUTPUT_NAME "libminiupnpc") + else() + set_target_properties (libminiupnpc-static PROPERTIES OUTPUT_NAME "miniupnpc") + endif() + target_link_libraries (libminiupnpc-static PRIVATE miniupnpc-private) + target_include_directories(libminiupnpc-static INTERFACE $) + target_compile_definitions(libminiupnpc-static PUBLIC MINIUPNP_STATICLIB) + + if (NOT UPNPC_NO_INSTALL) + install (TARGETS miniupnpc-private EXPORT miniupnpc-private) + + install (EXPORT miniupnpc-private + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc" + NAMESPACE miniupnpc::) + + install (TARGETS libminiupnpc-static + EXPORT libminiupnpc-static + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + install (EXPORT libminiupnpc-static + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc" + NAMESPACE miniupnpc::) + endif() + + if (UPNPC_BUILD_SAMPLE) + add_executable (upnpc-static src/upnpc.c) + target_link_libraries (upnpc-static PRIVATE libminiupnpc-static) + target_include_directories(upnpc-static PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + + add_executable (upnp-listdevices-static src/listdevices.c) + target_link_libraries (upnp-listdevices-static PRIVATE libminiupnpc-static) + target_include_directories(upnp-listdevices-static PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + + if (NOT UPNPC_NO_INSTALL) + install (TARGETS upnpc-static upnp-listdevices-static + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + endif () +endif () + +if (UPNPC_BUILD_SHARED) + add_library (libminiupnpc-shared SHARED ${MINIUPNPC_SOURCES}) + target_include_directories (libminiupnpc-shared PUBLIC + $ + $) + add_library (miniupnpc::miniupnpc ALIAS libminiupnpc-shared) + set_target_properties (libminiupnpc-shared PROPERTIES EXPORT_NAME miniupnpc) + set_target_properties (libminiupnpc-shared PROPERTIES OUTPUT_NAME "miniupnpc") + set_target_properties (libminiupnpc-shared PROPERTIES VERSION ${PROJECT_VERSION}) + set_target_properties (libminiupnpc-shared PROPERTIES SOVERSION ${MINIUPNPC_API_VERSION}) + target_link_libraries (libminiupnpc-shared PRIVATE miniupnpc-private) + target_compile_definitions(libminiupnpc-shared PRIVATE MINIUPNP_EXPORTS) + + target_include_directories(libminiupnpc-shared INTERFACE $) + if (WIN32) + target_link_libraries(libminiupnpc-shared INTERFACE ws2_32 iphlpapi) + endif() + + if (NOT UPNPC_NO_INSTALL) + install (TARGETS libminiupnpc-shared + EXPORT libminiupnpc-shared + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + install (EXPORT libminiupnpc-shared + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc" + NAMESPACE miniupnpc::) + endif() + + if (UPNPC_BUILD_SAMPLE) + add_executable (upnpc-shared src/upnpc.c) + target_link_libraries (upnpc-shared PRIVATE libminiupnpc-shared) + target_include_directories(upnpc-shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + + add_executable (upnp-listdevices-shared src/listdevices.c) + target_link_libraries (upnp-listdevices-shared PRIVATE libminiupnpc-shared) + target_include_directories(upnp-listdevices-shared PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + + if (NOT UPNPC_NO_INSTALL) + install (TARGETS upnpc-shared upnp-listdevices-shared + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() + endif () +endif () + +if (UPNPC_BUILD_TESTS) + add_library(miniupnpc-tests INTERFACE) + target_link_libraries(miniupnpc-tests INTERFACE miniupnpc-private) + target_compile_definitions(miniupnpc-tests INTERFACE MINIUPNP_STATICLIB) + + add_executable (testminixml src/testminixml.c src/minixml.c src/igd_desc_parse.c) + target_include_directories (testminixml PRIVATE + $) + target_link_libraries (testminixml PRIVATE miniupnpc-tests) + + add_executable (minixmlvalid src/minixmlvalid.c src/minixml.c) + target_link_libraries (minixmlvalid PRIVATE miniupnpc-tests) + + add_executable (testupnpreplyparse src/testupnpreplyparse.c + src/minixml.c src/upnpreplyparse.c) + target_include_directories (testupnpreplyparse PRIVATE + $) + target_link_libraries (testupnpreplyparse PRIVATE miniupnpc-tests) + + add_executable (testigddescparse src/testigddescparse.c + src/igd_desc_parse.c src/minixml.c src/miniupnpc.c src/miniwget.c src/minissdpc.c + src/upnpcommands.c src/upnpreplyparse.c src/minisoap.c src/connecthostport.c + src/portlistingparse.c src/receivedata.c src/addr_is_reserved.c + ) + target_include_directories (testigddescparse PRIVATE + $) + target_link_libraries (testigddescparse PRIVATE miniupnpc-tests) + + add_executable (testminiwget src/testminiwget.c + src/miniwget.c src/miniupnpc.c src/minisoap.c src/upnpcommands.c src/minissdpc.c + src/upnpreplyparse.c src/minixml.c src/igd_desc_parse.c src/connecthostport.c + src/portlistingparse.c src/receivedata.c src/addr_is_reserved.c + ) + target_include_directories (testminiwget PRIVATE + $) + target_link_libraries (testminiwget PRIVATE miniupnpc-tests) + + add_executable (testaddr_is_reserved src/testaddr_is_reserved.c + src/addr_is_reserved.c + ) + target_link_libraries (testaddr_is_reserved PRIVATE miniupnpc-tests) + + add_executable (testportlistingparse src/testportlistingparse.c + src/minixml.c src/portlistingparse.c) + target_include_directories (testportlistingparse PRIVATE + $) + target_link_libraries (testportlistingparse PRIVATE miniupnpc-tests) + + if (NOT WIN32) + add_executable (minihttptestserver src/minihttptestserver.c) + endif() + +# set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} testminixml minixmlvalid testupnpreplyparse testigddescparse testminiwget) + include(CTest) + add_test(NAME validateminixml + COMMAND minixmlvalid) + add_test(NAME validateminiwget + COMMAND ${CMAKE_SOURCE_DIR}/testminiwget.sh) + if (NOT WIN32) + set_property(TEST validateminiwget + PROPERTY ENVIRONMENT + TESTSERVER=${CMAKE_BINARY_DIR}/minihttptestserver + TESTMINIWGET=${CMAKE_BINARY_DIR}/testminiwget) + endif() + add_test(NAME validateupnpreplyparse + COMMAND ${CMAKE_SOURCE_DIR}/testupnpreplyparse.sh + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + set_property(TEST validateupnpreplyparse + PROPERTY ENVIRONMENT + TESTUPNPREPLYPARSE=${CMAKE_BINARY_DIR}/testupnpreplyparse) + add_test(NAME validateportlistingparse + COMMAND testportlistingparse) + add_test(NAME validateigddescparse1 + COMMAND testigddescparse new_LiveBox_desc.xml new_LiveBox_desc.values + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdesc) + add_test(NAME validateigddescparse2 + COMMAND testigddescparse linksys_WAG200G_desc.xml linksys_WAG200G_desc.values + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/testdesc) + add_test(NAME validateaddr_is_reserved + COMMAND testaddr_is_reserved) + +endif () + +configure_file(miniupnpc.pc.in miniupnpc.pc @ONLY) + +if (NOT UPNPC_NO_INSTALL) + install (FILES + include/miniupnpc.h + include/miniwget.h + include/upnpcommands.h + include/igd_desc_parse.h + include/upnpreplyparse.h + include/upnperrors.h + include/upnpdev.h + include/miniupnpctypes.h + include/portlistingparse.h + include/miniupnpc_declspec.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/miniupnpc + ) + + install(FILES miniupnpc-config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/miniupnpc + ) + + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/miniupnpc.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + ) + + install(FILES man3/miniupnpc.3 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man3 + ) + + install(FILES external-ip.sh + TYPE BIN + ) endif() -set(SRCS src/igd_desc_parse.c - src/miniupnpc.c - src/minixml.c - src/minisoap.c - src/minissdpc.c - src/miniwget.c - src/upnpcommands.c - src/upnpdev.c - src/upnpreplyparse.c - src/upnperrors.c - src/connecthostport.c - src/portlistingparse.c - src/receivedata.c) - -add_library(miniupnpc STATIC ${SRCS}) -dolphin_disable_warnings(miniupnpc) -target_include_directories(miniupnpc PUBLIC src) - -add_library(Miniupnpc::miniupnpc ALIAS miniupnpc) +# vim: ts=2:sw=2:expandtab diff --git a/Externals/miniupnpc/Changelog.txt b/Externals/miniupnpc/Changelog.txt index 3a634cc3e2..79aa5c3bef 100644 --- a/Externals/miniupnpc/Changelog.txt +++ b/Externals/miniupnpc/Changelog.txt @@ -1,6 +1,197 @@ -$Id: Changelog.txt,v 1.215 2015/10/01 09:26:11 nanard Exp $ +$Id: Changelog.txt,v 1.276 2025/03/05 10:29:50 nanard Exp $ miniUPnP client Changelog. +VERSION 2.3.2 : released 2025/03/05 + +2025/02/24: + remove STRTOUI from public headers + remove parseURL() from public headers + remove struct NameValue from upnpreplyparse.h and use flexible array member + Increments API_VERSION to 20 + +VERSION 2.3.1 : released 2025/02/23 + +2025/02/23: + allocate argument arrays on stack instead of heap in upnpcommand.c + +2025/02/09: + doxygen documentation of the public headers + +VERSION 2.3.0 : released 2025/01/11 + +2025/01/10: + Change simpleUPnPcommand() prototype to remove unused first argument + Increments API_VERSION to 19 + +2024/07/27: + Add #define for UPNP_GetValidIGD() return values + +VERSION 2.2.8 : released 2024/06/09 + +2024/05/16: + IPv6: try site-local before link-local + +2024/05/08: + upnpc.c: Add -f option to upnpc program (delete multiple port redirections) + UPNP_GetValidIGD(): distinguish between not connected and connected to a + "private" network (with a reserved IP address). + Increments API_VERSION to 18 + +VERSION 2.2.7 : released 2024/03/20 + +2024/01/15: + listdevices.c: exit with status code 1 if no UPNP device is found + +2024/01/07: + upnpc.c: reformat Usage + +VERSION 2.2.6 : released 2024/01/04 + +2024/01/04: + includes charset="utf-8" in Content-Type + fix memory allocation error in minissdpc.c + +2023/06/15: + Make User-Agent compliant. + listdevices => upnp-listdevices + +VERSION 2.2.5 : released 2023/06/12 + +2023/06/05: + GetListOfPortMappings NewStartPort 0 => 1 + +2023/05/30: + CheckPinholeWorking is optional + add 60x errors from UPnP Device Architecture + +2023/01/04: + cmake: install binaries, man pages and external-ip.sh + +VERSION 2.2.4 : released 2022/10/21 + +2022/02/20: + upnpc: use of @ to replace local lan address + +2021/11/09: + python module : Allow to specify the root description url + +VERSION 2.2.3 : released 2021/09/28 + +2021/08/13: + Change directory structure : include/ and src/ directories. + +VERSION 2.2.2 : released 2021/03/03 + +2021/01/15: + miniupnpcmodule.c: throw an exception in UPnP_discover() + +2020/12/30: + Fix usage of IP_MULTICAST_IF with struct ip_mreqn + +VERSION 2.2.1 : released 2020/12/20 + +2020/11/30: + Add miniupnpc.rc for .dll description + +VERSION 2.2.0 : released 2020/11/09 + +2020/09/24: + Check properly for reserved IP addresses + +2020/09/23: + prevent infinite loop in upnpDiscover() + +2020/02/16: + Add Haiku support + +2019/10/22: + testminiwget.sh can use either "ip addr" or "ifconfig -a + +2019/10/13: + fix UPNP_GetValidIGD() when several devices are found + which are reachable from != local address + +2019/08/24: + Allow Remote Host on upnpc command line + fix error 708 description in strupnperror() + +2019/04/05: + Fix memory leak in upnpreplyparse.c with NewPortListing element + +2019/03/10: + connecthostport.c: Code simplification, error trace fix + +2019/01/23: + set timeout for select() in connecthostport() + +2018/10/31: + miniupnpcmodule.c: check return of WSAStartup() + +2018/07/14: + Fix and improve MSVC project : + Add Dll configurations + improve genminiupnpcstrings.vbs + +2018/06/18: + Fixes for windows 64-bits. + +VERSION 2.1 : released 2018/05/07 + +2018/05/07: + CMake Modernize and cleanup CMakeLists.txt + Update MS Visual Studio projects + +2018/04/30: + listdevices: show devices sorted by XML desc URL + +2018/04/26: + Small fix in miniupnpcmodule.c (python module) + Support cross compiling in Makefile.mingw + +2018/04/06: + Use SOCKET type instead of int (for Win64 compilation) + Increments API_VERSION to 17 + +2018/02/22: + Disable usage of MiniSSDPd when using -m option + +2017/12/11: + Fix buffer over run in minixml.c + Fix uninitialized variable access in upnpreplyparse.c + +2017/05/05: + Fix CVE-2017-8798 Thanks to tin/Team OSTStrom + +2016/11/11: + check strlen before memcmp in XML parsing portlistingparse.c + fix build under SOLARIS and CYGWIN + +2016/10/11: + Add python 3 compatibility to IGD test + +VERSION 2.0 : released 2016/04/19 + +2016/01/24: + change miniwget to return HTTP status code + increments API_VERSION to 16 + +2016/01/22: + Improve UPNPIGD_IsConnected() to check if WAN address is not private. + parse HTTP response status line in miniwget.c + +2015/10/26: + snprintf() overflow check. check overflow in simpleUPnPcommand2() + +2015/10/25: + fix compilation with old macs + fix compilation with mingw32 (for Appveyor) + fix python module for python <= 2.3 + +2015/10/08: + Change sameport to localport + see https://github.com/miniupnp/miniupnp/pull/120 + increments API_VERSION to 15 + 2015/09/15: Fix buffer overflow in igd_desc_parse.c/IGDstartelt() Discovered by Aleksandar Nikolic of Cisco Talos @@ -565,7 +756,7 @@ VERSION 1.0 : upnpc now displays external ip address with -s or -l 2007/04/11: - changed MINIUPNPC_URL_MAXSIZE to 128 to accomodate the "BT Voyager 210" + changed MINIUPNPC_URL_MAXSIZE to 128 to accommodate the "BT Voyager 210" 2007/03/19: cleanup in miniwget.c diff --git a/Externals/miniupnpc/LICENSE b/Externals/miniupnpc/LICENSE index cb5a060443..ea5b60640b 100644 --- a/Externals/miniupnpc/LICENSE +++ b/Externals/miniupnpc/LICENSE @@ -1,27 +1 @@ -MiniUPnPc -Copyright (c) 2005-2015, 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. - +../LICENSE \ No newline at end of file diff --git a/Externals/miniupnpc/README b/Externals/miniupnpc/README index b23478de97..903b82ada0 100644 --- a/Externals/miniupnpc/README +++ b/Externals/miniupnpc/README @@ -1,23 +1,19 @@ Project: miniupnp -Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ +Project web page: http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ github: https://github.com/miniupnp/miniupnp -freecode: http://freecode.com/projects/miniupnp Author: Thomas Bernard -Copyright (c) 2005-2012 Thomas Bernard +Copyright (c) 2005-2025 Thomas Bernard This software is subject to the conditions detailed in the LICENSE file provided within this distribution. -For the comfort of Win32 users, bsdqueue.h is included in the distribution. -Its licence is included in the header of the file. -bsdqueue.h is a copy of the sys/queue.h of an OpenBSD system. - - * miniUPnP Client - miniUPnPc * To compile, simply run 'gmake' (could be 'make' on your system). Under win32, to compile with MinGW, type "mingw32make.bat". MS Visual C solution and project files are supplied in the msvc/ subdirectory. +The miniupnpc library is available as a static library or as a DLL : +define MINIUPNP_STATICLIB if you want to link against the static library. The compilation is known to work under linux, FreeBSD, OpenBSD, MacOS X, AmigaOS and cygwin. @@ -37,6 +33,7 @@ To use the libminiupnpc in your application, link it with libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h, upnpcommands.h and miniwget.h : - upnpDiscover() +- UPNP_GetValidIGD() - miniwget() - parserootdesc() - GetUPNPUrls() @@ -62,5 +59,34 @@ If you are using libminiupnpc in your application, please send me an email ! For any question, you can use the web forum : -http://miniupnp.tuxfamily.org/forum/ +https://miniupnp.tuxfamily.org/forum/ +Bugs should be reported on GitHub : +https://github.com/miniupnp/miniupnp/issues + +* Linux firewall configuration for UPnP clients * + +Due to how UPnP protocol is designed, unicast responses to UPnP multicast client +requests are not tracked by Linux netfilter. And therefore netfilter executes +default action for them (which is in most cases DROP response packet). + +To workaround this limitation, custom ipset hash table can be used. It is +supported since Linux kernel >= 2.6.39. + +Rules for IPv4: +$ ipset create upnp hash:ip,port timeout 3 +$ iptables -A OUTPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j SET --add-set upnp src,src --exist +$ iptables -A INPUT -p udp -m set --match-set upnp dst,dst -j ACCEPT +$ iptables -A INPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j ACCEPT + +Rules for IPv6: +$ ipset create upnp6 hash:ip,port timeout 3 family inet6 +$ ip6tables -A OUTPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist +$ ip6tables -A OUTPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j SET --add-set upnp6 src,src --exist +$ ip6tables -A INPUT -p udp -m set --match-set upnp6 dst,dst -j ACCEPT +$ ip6tables -A INPUT -d ff02::c/128 -p udp -m udp --dport 1900 -j ACCEPT +$ ip6tables -A INPUT -d ff05::c/128 -p udp -m udp --dport 1900 -j ACCEPT + +Detailed description is available on: +https://serverfault.com/a/911286 +https://unix.stackexchange.com/a/444804 diff --git a/Externals/miniupnpc/VERSION b/Externals/miniupnpc/VERSION index 2e0e38c63a..f90b1afc08 100644 --- a/Externals/miniupnpc/VERSION +++ b/Externals/miniupnpc/VERSION @@ -1 +1 @@ -1.9 +2.3.2 diff --git a/Externals/miniupnpc/apiversions.txt b/Externals/miniupnpc/apiversions.txt index dea5dff310..e41f6e6543 100644 --- a/Externals/miniupnpc/apiversions.txt +++ b/Externals/miniupnpc/apiversions.txt @@ -1,7 +1,45 @@ -$Id: apiversions.txt,v 1.7 2015/07/23 20:40:08 nanard Exp $ +$Id: apiversions.txt,v 1.14 2025/03/05 10:29:51 nanard Exp $ Differences in API between miniUPnPc versions +API version 20 + cleanup : remove STRTOUI and parseURL() from public headers + remove struct NameValue from upnpreplyparse.h + updated macro : + #define MINIUPNPC_API_VERSION 20 + This more a cleanup than a real API change. Things that were removed + from public headers were not supposed to be used and/or not + exposed in the dll export list. + +API version 19 + change simpleUPnPcommand() prototype + updated macro : + #define MINIUPNPC_API_VERSION 19 + +API version 18 + change UPNP_GetValidIGD() prototype and return values + updated macro : + #define MINIUPNPC_API_VERSION 18 + +API version 17 + change struct UPNPDev + move getHTTPResponse() to miniwget_private.h + updated macro : + #define MINIUPNPC_API_VERSION 17 + +API version 16 + added "status_code" argument to getHTTPResponse(), miniwget() and miniwget_getaddr() + updated macro : + #define MINIUPNPC_API_VERSION 16 + +API version 15 + changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice() + to "localport". When 0 or 1, behaviour is not changed, but it can take + any other value between 2 and 65535 + Existing programs should be compatible + updated macro : + #define MINIUPNPC_API_VERSION 15 + API version 14 miniupnpc.h add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice() @@ -9,6 +47,8 @@ miniupnpc.h getDevicesFromMiniSSDPD() : connectToMiniSSDPD() / disconnectFromMiniSSDPD() requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD() + updated macro : + #define MINIUPNPC_API_VERSION 14 API version 13 miniupnpc.h: diff --git a/Externals/miniupnpc/include/igd_desc_parse.h b/Externals/miniupnpc/include/igd_desc_parse.h new file mode 100644 index 0000000000..52a4ab3e31 --- /dev/null +++ b/Externals/miniupnpc/include/igd_desc_parse.h @@ -0,0 +1,75 @@ +/* $Id: igd_desc_parse.h,v 1.14 2025/02/08 23:15:16 nanard Exp $ */ +/* Project : miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas Bernard + * Copyright (c) 2005-2025 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#ifndef IGD_DESC_PARSE_H_INCLUDED +#define IGD_DESC_PARSE_H_INCLUDED + +/*! \file igd_desc_parse.h + * \brief API to parse UPNP device description XML + * \todo should not be exposed in the public API + */ + +/*! \brief maximum lenght of URLs */ +#define MINIUPNPC_URL_MAXSIZE (128) + +/*! \brief Structure to store the result of the parsing of UPnP + * descriptions of Internet Gateway Devices services */ +struct IGDdatas_service { + /*! \brief controlURL for the service */ + char controlurl[MINIUPNPC_URL_MAXSIZE]; + /*! \brief eventSubURL for the service */ + char eventsuburl[MINIUPNPC_URL_MAXSIZE]; + /*! \brief SCPDURL for the service */ + char scpdurl[MINIUPNPC_URL_MAXSIZE]; + /*! \brief serviceType */ + char servicetype[MINIUPNPC_URL_MAXSIZE]; + /*char devicetype[MINIUPNPC_URL_MAXSIZE];*/ +}; + +/*! \brief Structure to store the result of the parsing of UPnP + * descriptions of Internet Gateway Devices */ +struct IGDdatas { + /*! \brief current element name */ + char cureltname[MINIUPNPC_URL_MAXSIZE]; + /*! \brief URLBase */ + char urlbase[MINIUPNPC_URL_MAXSIZE]; + /*! \brief presentationURL */ + char presentationurl[MINIUPNPC_URL_MAXSIZE]; + /*! \brief depth into the XML tree */ + int level; + /*! \brief "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */ + struct IGDdatas_service CIF; + /*! \brief first of "urn:schemas-upnp-org:service:WANIPConnection:1" + * or "urn:schemas-upnp-org:service:WANPPPConnection:1" */ + struct IGDdatas_service first; + /*! \brief second of "urn:schemas-upnp-org:service:WANIPConnection:1" + * or "urn:schemas-upnp-org:service:WANPPPConnection:1" */ + struct IGDdatas_service second; + /*! \brief "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */ + struct IGDdatas_service IPv6FC; + /*! \brief currently parsed service */ + struct IGDdatas_service tmp; +}; + +/*! + * \brief XML start element handler + */ +void IGDstartelt(void *, const char *, int); +/*! + * \brief XML end element handler + */ +void IGDendelt(void *, const char *, int); +/*! + * \brief XML characted data handler + */ +void IGDdata(void *, const char *, int); +#ifdef DEBUG +void printIGD(struct IGDdatas *); +#endif /* DEBUG */ + +#endif /* IGD_DESC_PARSE_H_INCLUDED */ diff --git a/Externals/miniupnpc/include/miniupnpc.h b/Externals/miniupnpc/include/miniupnpc.h new file mode 100644 index 0000000000..b6ee168f3e --- /dev/null +++ b/Externals/miniupnpc/include/miniupnpc.h @@ -0,0 +1,303 @@ +/* $Id: miniupnpc.h,v 1.76 2025/03/05 10:35:57 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project: miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2025 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef MINIUPNPC_H_INCLUDED +#define MINIUPNPC_H_INCLUDED + +/*! \file miniupnpc.h + * \brief Main C API for MiniUPnPc + * + * Contains functions to discover devices and check for device validity + * or connectivity. + * + * \mainpage MiniUPnPc API documentation + * MiniUPnPc (MiniUPnP client) is a library implementing a UPnP + * Internet Gateway Device (IGD) control point. + * + * It should be used by applications that needs to listen to incoming + * traffic from the internet which are running on a LAN where a + * UPnP IGD is running on the router (or gateway). + * + * See more documentation on the website http://miniupnp.free.fr + * or https://miniupnp.tuxfamily.org/ or GitHub : + * https://github.com/miniupnp/miniupnp/tree/master/miniupnpc + */ +#include "miniupnpc_declspec.h" +#include "igd_desc_parse.h" +#include "upnpdev.h" + +/* error codes : */ +/*! \brief value for success */ +#define UPNPDISCOVER_SUCCESS (0) +/*! \brief value for unknown error */ +#define UPNPDISCOVER_UNKNOWN_ERROR (-1) +/*! \brief value for a socket error */ +#define UPNPDISCOVER_SOCKET_ERROR (-101) +/*! \brief value for a memory allocation error */ +#define UPNPDISCOVER_MEMORY_ERROR (-102) + +/*! \brief software version */ +#define MINIUPNPC_VERSION "2.3.2" +/*! \brief C API version */ +#define MINIUPNPC_API_VERSION 20 + +/*! \brief any (ie system chosen) port */ +#define UPNP_LOCAL_PORT_ANY 0 +/*! \brief Use as an alias for 1900 for backwards compatibility */ +#define UPNP_LOCAL_PORT_SAME 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structures definitions : */ + +/*! + * \brief UPnP method argument + */ +struct UPNParg { + const char * elt; /*!< \brief UPnP argument name */ + const char * val; /*!< \brief UPnP argument value */ +}; + +/*! + * \brief execute a UPnP method (SOAP action) + * + * \param[in] url Control URL for the service + * \param[in] service service to use + * \param[in] action action to call + * \param[in] args action arguments + * \param[out] bufsize the size of the returned buffer + * \return NULL in case of error or the raw XML response + */ +char * +simpleUPnPcommand(const char * url, const char * service, + const char * action, const struct UPNParg * args, + int * bufsize); + +/*! + * \brief Discover UPnP IGD on the network. + * + * The discovered devices are returned as a chained list. + * It is up to the caller to free the list with freeUPNPDevlist(). + * If available, device list will be obtained from MiniSSDPd. + * + * \param[in] delay (in millisecond) maximum time for waiting any device + * response + * \param[in] multicastif If not NULL, used instead of the default + * multicast interface for sending SSDP discover packets + * \param[in] minissdpdsock Path to minissdpd socket, default is used if + * NULL + * \param[in] localport Source port to send SSDP packets. + * #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port) + * #UPNP_LOCAL_PORT_ANY to let system assign a source port + * \param[in] ipv6 0 for IPv4, 1 of IPv6 + * \param[in] ttl should default to 2 as advised by UDA 1.1 + * \param[out] error error code when NULL is returned + * \return NULL or a linked list + */ +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscover(int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error); + +/*! + * \brief Discover all UPnP devices on the network + * + * search for "ssdp:all" + * \param[in] delay (in millisecond) maximum time for waiting any device + * response + * \param[in] multicastif If not NULL, used instead of the default + * multicast interface for sending SSDP discover packets + * \param[in] minissdpdsock Path to minissdpd socket, default is used if + * NULL + * \param[in] localport Source port to send SSDP packets. + * #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port) + * #UPNP_LOCAL_PORT_ANY to let system assign a source port + * \param[in] ipv6 0 for IPv4, 1 of IPv6 + * \param[in] ttl should default to 2 as advised by UDA 1.1 + * \param[out] error error code when NULL is returned + * \return NULL or a linked list + */ +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverAll(int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error); + +/*! + * \brief Discover one type of UPnP devices + * + * \param[in] device device type to search + * \param[in] delay (in millisecond) maximum time for waiting any device + * response + * \param[in] multicastif If not NULL, used instead of the default + * multicast interface for sending SSDP discover packets + * \param[in] minissdpdsock Path to minissdpd socket, default is used if + * NULL + * \param[in] localport Source port to send SSDP packets. + * #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port) + * #UPNP_LOCAL_PORT_ANY to let system assign a source port + * \param[in] ipv6 0 for IPv4, 1 of IPv6 + * \param[in] ttl should default to 2 as advised by UDA 1.1 + * \param[out] error error code when NULL is returned + * \return NULL or a linked list + */ +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverDevice(const char * device, int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error); + +/*! + * \brief Discover one or several type of UPnP devices + * + * \param[in] deviceTypes array of device types to search (ending with NULL) + * \param[in] delay (in millisecond) maximum time for waiting any device + * response + * \param[in] multicastif If not NULL, used instead of the default + * multicast interface for sending SSDP discover packets + * \param[in] minissdpdsock Path to minissdpd socket, default is used if + * NULL + * \param[in] localport Source port to send SSDP packets. + * #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port) + * #UPNP_LOCAL_PORT_ANY to let system assign a source port + * \param[in] ipv6 0 for IPv4, 1 of IPv6 + * \param[in] ttl should default to 2 as advised by UDA 1.1 + * \param[out] error error code when NULL is returned + * \param[in] searchalltypes 0 to stop with the first type returning results + * \return NULL or a linked list + */ +MINIUPNP_LIBSPEC struct UPNPDev * +upnpDiscoverDevices(const char * const deviceTypes[], + int delay, const char * multicastif, + const char * minissdpdsock, int localport, + int ipv6, unsigned char ttl, + int * error, + int searchalltypes); + +/*! + * \brief parse root XML description of a UPnP device + * + * fill the IGDdatas structure. + * \param[in] buffer character buffer containing the XML description + * \param[in] bufsize size in bytes of the buffer + * \param[out] data IGDdatas structure to fill + */ +MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data); + +/*! + * \brief structure used to get fast access to urls + */ +struct UPNPUrls { + /*! \brief controlURL of the WANIPConnection */ + char * controlURL; + /*! \brief url of the description of the WANIPConnection */ + char * ipcondescURL; + /*! \brief controlURL of the WANCommonInterfaceConfig */ + char * controlURL_CIF; + /*! \brief controlURL of the WANIPv6FirewallControl */ + char * controlURL_6FC; + /*! \brief url of the root description */ + char * rootdescURL; +}; + +/*! \brief NO IGD found */ +#define UPNP_NO_IGD (0) +/*! \brief valid and connected IGD */ +#define UPNP_CONNECTED_IGD (1) +/*! \brief valid and connected IGD but with a reserved address + * (non routable) */ +#define UPNP_PRIVATEIP_IGD (2) +/*! \brief valid but not connected IGD */ +#define UPNP_DISCONNECTED_IGD (3) +/*! \brief UPnP device not recognized as an IGD */ +#define UPNP_UNKNOWN_DEVICE (4) + +/*! + * \brief look for a valid and possibly connected IGD in the list + * + * In any non zero return case, the urls and data structures + * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to + * free allocated memory. + * \param[in] devlist A device list obtained with upnpDiscover() / + * upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices() + * \param[out] urls Urls for the IGD found + * \param[out] data datas for the IGD found + * \param[out] lanaddr buffer to copy the local address of the host to reach the IGD + * \param[in] lanaddrlen size of the lanaddr buffer + * \param[out] wanaddr buffer to copy the public address of the IGD + * \param[in] wanaddrlen size of the wanaddr buffer + * \return #UPNP_NO_IGD / #UPNP_CONNECTED_IGD / #UPNP_PRIVATEIP_IGD / + * #UPNP_DISCONNECTED_IGD / #UPNP_UNKNOWN_DEVICE + */ +MINIUPNP_LIBSPEC int +UPNP_GetValidIGD(struct UPNPDev * devlist, + struct UPNPUrls * urls, + struct IGDdatas * data, + char * lanaddr, int lanaddrlen, + char * wanaddr, int wanaddrlen); + +/*! + * \brief Get IGD URLs and data for URL + * + * Used when skipping the discovery process. + * \param[in] rootdescurl Root description URL of the device + * \param[out] urls Urls for the IGD found + * \param[out] data datas for the IGD found + * \param[out] lanaddr buffer to copy the local address of the host to reach the IGD + * \param[in] lanaddrlen size of the lanaddr buffer + * \return 0 Not ok / 1 OK + */ +MINIUPNP_LIBSPEC int +UPNP_GetIGDFromUrl(const char * rootdescurl, + struct UPNPUrls * urls, + struct IGDdatas * data, + char * lanaddr, int lanaddrlen); + +/*! + * \brief Prepare the URLs for usage + * + * build absolute URLs from the root description + * \param[out] urls URL structure to initialize + * \param[in] data datas for the IGD + * \param[in] descURL root description URL for the IGD + * \param[in] scope_id if not 0, add the scope to the linklocal IPv6 + * addresses in URLs + */ +MINIUPNP_LIBSPEC void +GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data, + const char * descURL, unsigned int scope_id); + +/*! + * \brief free the members of a UPNPUrls struct + * + * All URLs buffers are freed and zeroed + * \param[out] urls + */ +MINIUPNP_LIBSPEC void +FreeUPNPUrls(struct UPNPUrls * urls); + +/*! + * \brief check the current connection status of an IGD + * + * it uses UPNP_GetStatusInfo() + * \param[in] urls IGD URLs + * \param[in] data IGD data + * \return 1 Connected / 0 Disconnected + */ +MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Externals/miniupnpc/include/miniupnpc_declspec.h b/Externals/miniupnpc/include/miniupnpc_declspec.h new file mode 100644 index 0000000000..e0e9d2b3f7 --- /dev/null +++ b/Externals/miniupnpc/include/miniupnpc_declspec.h @@ -0,0 +1,22 @@ +#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED +#define MINIUPNPC_DECLSPEC_H_INCLUDED + +/*! \file miniupnpc_declspec.h + * \brief define #MINIUPNP_LIBSPEC for dll exports and imports */ +#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB) + /* for windows dll */ + #ifdef MINIUPNP_EXPORTS + #define MINIUPNP_LIBSPEC __declspec(dllexport) + #else + #define MINIUPNP_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 MINIUPNP_LIBSPEC __attribute__ ((visibility ("default"))) + #else + #define MINIUPNP_LIBSPEC + #endif +#endif + +#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */ diff --git a/Externals/miniupnpc/include/miniupnpctypes.h b/Externals/miniupnpc/include/miniupnpctypes.h new file mode 100644 index 0000000000..c832e01720 --- /dev/null +++ b/Externals/miniupnpc/include/miniupnpctypes.h @@ -0,0 +1,27 @@ +/* $Id: miniupnpctypes.h,v 1.4 2025/02/08 23:15:16 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org + * Author: Thomas Bernard + * Copyright (c) 2021-2025 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided within this distribution */ +#ifndef MINIUPNPCTYPES_H_INCLUDED +#define MINIUPNPCTYPES_H_INCLUDED + +/*! \file miniupnpctypes.h + * \brief type definitions + * + * Use unsigned long long when available : + * strtoull is C99 + * + * \def UNSIGNED_INTEGER + * \brief `unsigned long long` or `unsigned int` + * \todo int can be 16 bits, so it should be `unsigned long` + */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define UNSIGNED_INTEGER unsigned long long +#else +#define UNSIGNED_INTEGER unsigned int +#endif + +#endif diff --git a/Externals/miniupnpc/include/miniwget.h b/Externals/miniupnpc/include/miniwget.h new file mode 100644 index 0000000000..ed5364c2f1 --- /dev/null +++ b/Externals/miniupnpc/include/miniwget.h @@ -0,0 +1,54 @@ +/* $Id: miniwget.h,v 1.14 2025/02/08 23:15:17 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Copyright (c) 2005-2025 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#ifndef MINIWGET_H_INCLUDED +#define MINIWGET_H_INCLUDED + +/*! \file miniwget.h + * \brief Lightweight HTTP client API + */ +#include "miniupnpc_declspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \brief perform HTTP GET on an URL + * + * \param[in] url HTTP URL to GET + * \param[out] size length of the returned buffer. -1 in case of memory + * allocation error + * \param[in] scope_id interface id for IPv6 to use if not specified in the URL + * \param[out] status_code HTTP response status code (200, 404, etc.) + * \return the body of the HTTP response + */ +MINIUPNP_LIBSPEC void * miniwget(const char * url, int * size, + unsigned int scope_id, int * status_code); + +/*! \brief perform HTTP GET on an URL + * + * Also get the local address used to reach the HTTP server + * + * \param[in] url HTTP URL to GET + * \param[out] size length of the returned buffer. -1 in case of memory + * allocation error + * \param[out] addr local address used to connect to the server + * \param[in] addrlen size of the addr buffer + * \param[in] scope_id interface id for IPv6 to use if not specified in the URL + * \param[out] status_code HTTP response status code (200, 404, etc.) + * \return the body of the HTTP response + */ +MINIUPNP_LIBSPEC void * miniwget_getaddr(const char * url, int * size, + char * addr, int addrlen, + unsigned int scope_id, int * status_code); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Externals/miniupnpc/include/portlistingparse.h b/Externals/miniupnpc/include/portlistingparse.h new file mode 100644 index 0000000000..6b4e415aa7 --- /dev/null +++ b/Externals/miniupnpc/include/portlistingparse.h @@ -0,0 +1,91 @@ +/* $Id: portlistingparse.h,v 1.12 2025/02/08 23:15:17 nanard Exp $ */ +/* MiniUPnP project + * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * (c) 2011-2025 Thomas Bernard + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution */ +#ifndef PORTLISTINGPARSE_H_INCLUDED +#define PORTLISTINGPARSE_H_INCLUDED + +/*! \file portlistingparse.h + * \brief Parsing of the list of port mappings + * + * As returned by GetListOfPortMappings. + * Sample of PortMappingEntry : + * + * + * 202.233.2.1 + * 2345 + * TCP + * 2345 + * 192.168.1.137 + * 1 + * dooom + * 345 + * + */ +#include "miniupnpc_declspec.h" +/* for the definition of UNSIGNED_INTEGER */ +#include "miniupnpctypes.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \brief enum of all XML elements + */ +typedef enum { PortMappingEltNone, + PortMappingEntry, NewRemoteHost, + NewExternalPort, NewProtocol, + NewInternalPort, NewInternalClient, + NewEnabled, NewDescription, + NewLeaseTime } portMappingElt; + +/*! + * \brief linked list of port mappings + */ +struct PortMapping { + struct PortMapping * l_next; /*!< \brief next list element */ + UNSIGNED_INTEGER leaseTime; /*!< \brief in seconds */ + unsigned short externalPort; /*!< \brief external port */ + unsigned short internalPort; /*!< \brief internal port */ + char remoteHost[64]; /*!< \brief empty for wildcard */ + char internalClient[64]; /*!< \brief internal IP address */ + char description[64]; /*!< \brief description */ + char protocol[4]; /*!< \brief `TCP` or `UDP` */ + unsigned char enabled; /*!< \brief 0 (false) or 1 (true) */ +}; + +/*! + * \brief structure for ParsePortListing() + */ +struct PortMappingParserData { + struct PortMapping * l_head; /*!< \brief list head */ + portMappingElt curelt; /*!< \brief currently parsed element */ +}; + +/*! + * \brief parse the NewPortListing part of GetListOfPortMappings response + * + * \param[in] buffer XML data + * \param[in] bufsize length of XML data + * \param[out] pdata Parsed data + */ +MINIUPNP_LIBSPEC void +ParsePortListing(const char * buffer, int bufsize, + struct PortMappingParserData * pdata); + +/*! + * \brief free parsed data structure + * + * \param[in] pdata Parsed data to free + */ +MINIUPNP_LIBSPEC void +FreePortListing(struct PortMappingParserData * pdata); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Externals/miniupnpc/include/upnpcommands.h b/Externals/miniupnpc/include/upnpcommands.h new file mode 100644 index 0000000000..2ede06ef17 --- /dev/null +++ b/Externals/miniupnpc/include/upnpcommands.h @@ -0,0 +1,568 @@ +/* $Id: upnpcommands.h,v 1.34 2025/02/08 23:15:17 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project: miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2025 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided within this distribution */ +#ifndef UPNPCOMMANDS_H_INCLUDED +#define UPNPCOMMANDS_H_INCLUDED + +/*! \file upnpcommands.h + * \brief Internet Gateway Device methods + * + * See the documentation for both IGD v1 and IGD v2 : + * - https://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf + * - https://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v2-Device.pdf + * + * The methods are from WANIPConnection:1 or 2, WANCommonInterfaceConfig:1, + * and WANIPv6FirewallControl:1 + * + */ + +#include "miniupnpc_declspec.h" +#include "miniupnpctypes.h" + +/* MiniUPnPc return codes : */ +/*! \brief value for success */ +#define UPNPCOMMAND_SUCCESS (0) +/*! \brief value for unknown error */ +#define UPNPCOMMAND_UNKNOWN_ERROR (-1) +/*! \brief error while checking the arguments */ +#define UPNPCOMMAND_INVALID_ARGS (-2) +/*! \brief HTTP communication error */ +#define UPNPCOMMAND_HTTP_ERROR (-3) +/*! \brief The response contains invalid values */ +#define UPNPCOMMAND_INVALID_RESPONSE (-4) +/*! \brief Memory allocation error */ +#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5) + +#ifdef __cplusplus +extern "C" { +#endif + +struct PortMappingParserData; + +/*! \brief WANCommonInterfaceConfig:GetTotalBytesSent + * + * Note: this is a 32bits unsigned value and rolls over to 0 after reaching + * the maximum value + * + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of + * a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + */ +MINIUPNP_LIBSPEC UNSIGNED_INTEGER +UPNP_GetTotalBytesSent(const char * controlURL, + const char * servicetype); + +/*! \brief WANCommonInterfaceConfig:GetTotalBytesReceived + * + * Note: this is a 32bits unsigned value and rolls over to 0 after reaching + * the maximum value + * + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + */ +MINIUPNP_LIBSPEC UNSIGNED_INTEGER +UPNP_GetTotalBytesReceived(const char * controlURL, + const char * servicetype); + +/*! \brief WANCommonInterfaceConfig:GetTotalPacketsSent + * + * Note: this is a 32bits unsigned value and rolls over to 0 after reaching + * the maximum value + * + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + */ +MINIUPNP_LIBSPEC UNSIGNED_INTEGER +UPNP_GetTotalPacketsSent(const char * controlURL, + const char * servicetype); + +/*! \brief WANCommonInterfaceConfig:GetTotalBytesReceived + * + * Note: this is a 32bits unsigned value and rolls over to 0 after reaching + * the maximum value + * + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + */ +MINIUPNP_LIBSPEC UNSIGNED_INTEGER +UPNP_GetTotalPacketsReceived(const char * controlURL, + const char * servicetype); + +/*! \brief WANIPConnection:GetStatusInfo() + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[out] status 64 bytes buffer : `Unconfigured`, `Connecting`, + * `Connected`, `PendingDisconnect`, `Disconnecting`, `Disconnected` + * \param[out] uptime time in seconds + * \param[out] lastconnerror 64 bytes buffer : `ERROR_NONE`, + * `ERROR_COMMAND_ABORTED`, `ERROR_NOT_ENABLED_FOR_INTERNET`, + * `ERROR_USER_DISCONNECT`, `ERROR_ISP_DISCONNECT`, + * `ERROR_IDLE_DISCONNECT`, `ERROR_FORCED_DISCONNECT`, + * `ERROR_NO_CARRIER`, `ERROR_IP_CONFIGURATION`, `ERROR_UNKNOWN` + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error code + */ +MINIUPNP_LIBSPEC int +UPNP_GetStatusInfo(const char * controlURL, + const char * servicetype, + char * status, + unsigned int * uptime, + char * lastconnerror); + +/*! \brief WANIPConnection:GetConnectionTypeInfo() + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[out] connectionType 64 characters buffer : `Unconfigured`, + * `IP_Routed`, `IP_Bridged` + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error code + */ +MINIUPNP_LIBSPEC int +UPNP_GetConnectionTypeInfo(const char * controlURL, + const char * servicetype, + char * connectionType); + +/*! \brief WANIPConnection:GetExternalIPAddress() + * + * possible UPnP Errors : + * - 402 Invalid Args - See UPnP Device Architecture section on Control. + * - 501 Action Failed - See UPnP Device Architecture section on Control. + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[out] extIpAdd 16 bytes buffer + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_UNKNOWN_ERROR, + * #UPNPCOMMAND_INVALID_ARGS, #UPNPCOMMAND_HTTP_ERROR or an + * UPnP error code + */ +MINIUPNP_LIBSPEC int +UPNP_GetExternalIPAddress(const char * controlURL, + const char * servicetype, + char * extIpAdd); + +/*! \brief UPNP_GetLinkLayerMaxBitRates() + * call `WANCommonInterfaceConfig:GetCommonLinkProperties` + * + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + * \param[out] bitrateDown bits per second + * \param[out] bitrateUp bits per second + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error Code. + */ +MINIUPNP_LIBSPEC int +UPNP_GetLinkLayerMaxBitRates(const char* controlURL, + const char* servicetype, + unsigned int * bitrateDown, + unsigned int * bitrateUp); + +/*! \brief WANIPConnection:AddPortMapping() + * + * List of possible UPnP errors for AddPortMapping : + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ----------------- + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 501 Action Failed | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 715 WildCardNotPermittedInSrcIP | The source IP address cannot be wild-carded + * 716 WildCardNotPermittedInExtPort | The external port cannot be wild-carded + * 718 ConflictInMappingEntry | The port mapping entry specified conflicts with a mapping assigned previously to another client + * 724 SamePortValuesRequired | Internal and External port values must be the same + * 725 OnlyPermanentLeasesSupported | The NAT implementation only supports permanent lease times on port mappings + * 726 RemoteHostOnlySupportsWildcard | RemoteHost must be a wildcard and cannot be a specific IP address or DNS name + * 727 ExternalPortOnlySupportsWildcard | ExternalPort must be a wildcard and cannot be a specific port value + * 728 NoPortMapsAvailable | There are not enough free ports available to complete port mapping. + * 729 ConflictWithOtherMechanisms | Attempted port mapping is not allowed due to conflict with other mechanisms. + * 732 WildCardNotPermittedInIntPort | The internal port cannot be wild-carded + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[in] extPort External port + * \param[in] inPort Internal port + * \param[in] inClient IP of Internal client. + * \param[in] desc Port Mapping description. if NULL, defaults to + * "libminiupnpc" + * \param[in] proto `TCP` or `UDP` + * \param[in] remoteHost IP or empty string for wildcard. Most IGD don't + * support it + * \param[in] leaseDuration between 0 and 604800 + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_MEM_ALLOC_ERROR, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP error code. + */ +MINIUPNP_LIBSPEC int +UPNP_AddPortMapping(const char * controlURL, const char * servicetype, + const char * extPort, + const char * inPort, + const char * inClient, + const char * desc, + const char * proto, + const char * remoteHost, + const char * leaseDuration); + +/*! \brief WANIPConnection:AddAnyPortMapping() + * + * Only in WANIPConnection:2 + * + * List of possible UPnP errors for AddPortMapping : + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 501 Action Failed | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 715 WildCardNotPermittedInSrcIP | The source IP address cannot be wild-carded + * 716 WildCardNotPermittedInExtPort | The external port cannot be wild-carded + * 728 NoPortMapsAvailable | There are not enough free ports available to complete port mapping. + * 729 ConflictWithOtherMechanisms | Attempted port mapping is not allowed due to conflict with other mechanisms. + * 732 WildCardNotPermittedInIntPort | The internal port cannot be wild-carded + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:2 + * \param[in] extPort External port + * \param[in] inPort Internal port + * \param[in] inClient IP of Internal client. + * \param[in] desc Port Mapping description. if NULL, defaults to + * "libminiupnpc" + * \param[in] proto `TCP` or `UDP` + * \param[in] remoteHost IP or empty string for wildcard. Most IGD don't + * support it + * \param[in] leaseDuration between 0 and 604800 + * \param[out] reservedPort 6 bytes buffer + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_MEM_ALLOC_ERROR, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_INVALID_RESPONSE, #UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP error code. + */ +MINIUPNP_LIBSPEC int +UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, + const char * extPort, + const char * inPort, + const char * inClient, + const char * desc, + const char * proto, + const char * remoteHost, + const char * leaseDuration, + char * reservedPort); + +/*! \brief WANIPConnection:DeletePortMapping() + * + * Use same argument values as what was used for UPNP_AddPortMapping() + * + * List of possible UPnP errors for UPNP_DeletePortMapping() : + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 714 NoSuchEntryInArray | The specified value does not exist in the array + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[in] extPort External port + * \param[in] proto `TCP` or `UDP` + * \param[in] remoteHost IP or empty string for wildcard. Most IGD don't + * support it + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_MEM_ALLOC_ERROR, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP error code. + */ +MINIUPNP_LIBSPEC int +UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, + const char * extPort, const char * proto, + const char * remoteHost); + +/*! \brief WANIPConnection:DeletePortRangeMapping() + * + * Only in WANIPConnection:2 + * Use same argument values as what was used for AddPortMapping(). + * remoteHost is usually NULL because IGD don't support it. + * Return Values : + * 0 : SUCCESS + * NON ZERO : error. Either an UPnP error code or an undefined error. + * + * List of possible UPnP errors for DeletePortMapping : + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 730 PortMappingNotFound | This error message is returned if no port mapping is found in the specified range. + * 733 InconsistentParameters | NewStartPort and NewEndPort values are not consistent. + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:2 + * \param[in] extPortStart External port range start + * \param[in] extPortEnd External port range end + * \param[in] proto `TCP` or `UDP` + * \param[in] manage `0` to remove only the port mappings of this IGD, + * `1` to remove port mappings also for other clients + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_MEM_ALLOC_ERROR, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP error code. + */ +MINIUPNP_LIBSPEC int +UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype, + const char * extPortStart, const char * extPortEnd, + const char * proto, + const char * manage); + +/*! \brief WANIPConnection:GetPortMappingNumberOfEntries() + * + * not supported by all routers + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[out] numEntries Port mappings count + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP error code. + */ +MINIUPNP_LIBSPEC int +UPNP_GetPortMappingNumberOfEntries(const char * controlURL, + const char * servicetype, + unsigned int * numEntries); + +/*! \brief retrieves an existing port mapping for a port:protocol + * + * List of possible UPnP errors for UPNP_GetSpecificPortMappingEntry() : + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 501 Action Failed | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 714 NoSuchEntryInArray | The specified value does not exist in the array. + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[in] extPort External port + * \param[in] proto `TCP` or `UDP` + * \param[in] remoteHost IP or empty string for wildcard. Most IGD don't + * support it + * \param[out] intClient 16 bytes buffer + * \param[out] intPort 6 bytes buffer + * \param[out] desc 80 bytes buffer + * \param[out] enabled 4 bytes buffer + * \param[out] leaseDuration 16 bytes + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error Code. + */ +MINIUPNP_LIBSPEC int +UPNP_GetSpecificPortMappingEntry(const char * controlURL, + const char * servicetype, + const char * extPort, + const char * proto, + const char * remoteHost, + char * intClient, + char * intPort, + char * desc, + char * enabled, + char * leaseDuration); + +/*! \brief WANIPConnection:GetGenericPortMappingEntry() + * + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 713 SpecifiedArrayIndexInvalid | The specified array index is out of bounds + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[in] index + * \param[out] extPort 6 bytes buffer + * \param[out] intClient 16 bytes buffer + * \param[out] intPort 6 bytes buffer + * \param[out] protocol 4 bytes buffer + * \param[out] desc 80 bytes buffer + * \param[out] enabled 4 bytes buffer + * \param[out] rHost 64 bytes buffer + * \param[out] duration 16 bytes buffer + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error Code. + */ +MINIUPNP_LIBSPEC int +UPNP_GetGenericPortMappingEntry(const char * controlURL, + const char * servicetype, + const char * index, + char * extPort, + char * intClient, + char * intPort, + char * protocol, + char * desc, + char * enabled, + char * rHost, + char * duration); + +/*! \brief retrieval of a list of existing port mappings + * + * Available in IGD v2 : WANIPConnection:GetListOfPortMappings() + * + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 730 PortMappingNotFound | no port mapping is found in the specified range. + * 733 InconsistantParameters | NewStartPort and NewEndPort values are not consistent. + * + * \param[in] controlURL controlURL of the WANIPConnection of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:2 + * \param[in] startPort port interval start + * \param[in] endPort port interval end + * \param[in] protocol `TCP` or `UDP` + * \param[in] numberOfPorts size limit of the list returned. `0` to request + * all port mappings + * \param[out] data port mappings list + */ +MINIUPNP_LIBSPEC int +UPNP_GetListOfPortMappings(const char * controlURL, + const char * servicetype, + const char * startPort, + const char * endPort, + const char * protocol, + const char * numberOfPorts, + struct PortMappingParserData * data); + +/*! \brief GetFirewallStatus() retrieves whether the firewall is enabled + * and pinhole can be created through UPnP + * + * IGD:2, functions for service WANIPv6FirewallControl:1 + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[out] firewallEnabled false (0) or true (1) + * \param[out] inboundPinholeAllowed false (0) or true (1) + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ +MINIUPNP_LIBSPEC int +UPNP_GetFirewallStatus(const char * controlURL, + const char * servicetype, + int * firewallEnabled, + int * inboundPinholeAllowed); + +/*! \brief retrieve default value after which automatically created pinholes + * expire + * + * The returned value may be specific to the \p proto, \p remoteHost, + * \p remotePort, \p intClient and \p intPort, but this behavior depends + * on the implementation of the firewall. + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] remoteHost + * \param[in] remotePort + * \param[in] intClient + * \param[in] intPort + * \param[in] proto `TCP` or `UDP` + * \param[out] opTimeout lifetime in seconds of an inbound "automatic" + * firewall pinhole created by an outbound traffic initiation. + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ +MINIUPNP_LIBSPEC int +UPNP_GetOutboundPinholeTimeout(const char * controlURL, + const char * servicetype, + const char * remoteHost, + const char * remotePort, + const char * intClient, + const char * intPort, + const char * proto, + int * opTimeout); + +/*! \brief create a new pinhole that allows incoming traffic to pass + * through the firewall + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] remoteHost literal presentation of IPv6 address or domain name. + * empty string for wildcard + * \param[in] remotePort remote host port. Likely 0 (for wildcard) + * \param[in] intClient IP address of internal client. cannot be wildcarded + * \param[in] intPort client port. 0 for wildcard + * \param[in] proto IP protocol integer (6 for TCP, 17 for UDP, etc.) + * 65535 for wildcard. + * \param[in] leaseTime in seconds + * \param[out] uniqueID 8 bytes buffer + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ +MINIUPNP_LIBSPEC int +UPNP_AddPinhole(const char * controlURL, const char * servicetype, + const char * remoteHost, + const char * remotePort, + const char * intClient, + const char * intPort, + const char * proto, + const char * leaseTime, + char * uniqueID); + +/*! \brief update a pinhole’s lease time + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] uniqueID value obtained through UPNP_AddPinhole() + * \param[in] leaseTime in seconds + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ +MINIUPNP_LIBSPEC int +UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, + const char * uniqueID, + const char * leaseTime); + +/*! \brief remove a pinhole + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] uniqueID value obtained through UPNP_AddPinhole() + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ +MINIUPNP_LIBSPEC int +UPNP_DeletePinhole(const char * controlURL, + const char * servicetype, + const char * uniqueID); + +/*! \brief checking if a certain pinhole allows traffic to pass through the firewall + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] uniqueID value obtained through UPNP_AddPinhole() + * \param[out] isWorking `0` for false, `1` for true + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ +MINIUPNP_LIBSPEC int +UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, + const char * uniqueID, int * isWorking); + +/*! \brief get the total number of IP packets which have been going through + * the specified pinhole + * \todo \p packets should be #UNSIGNED_INTEGER + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] uniqueID value obtained through UPNP_AddPinhole() + * \param[out] packets how many IP packets have been going through the + * specified pinhole + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ +MINIUPNP_LIBSPEC int +UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, + const char * uniqueID, int * packets); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/Externals/miniupnpc/include/upnpdev.h b/Externals/miniupnpc/include/upnpdev.h new file mode 100644 index 0000000000..1a3f036072 --- /dev/null +++ b/Externals/miniupnpc/include/upnpdev.h @@ -0,0 +1,58 @@ +/* $Id: upnpdev.h,v 1.6 2025/02/08 23:15:17 nanard Exp $ */ +/* Project : miniupnp + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas BERNARD + * copyright (c) 2005-2025 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENSE file. */ +#ifndef UPNPDEV_H_INCLUDED +#define UPNPDEV_H_INCLUDED + +/*! \file upnpdev.h + * \brief UPNPDev device linked-list structure + * \todo could be merged into miniupnpc.h + */ +#include "miniupnpc_declspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \brief UPnP device linked-list + */ +struct UPNPDev { + /*! \brief pointer to the next element */ + struct UPNPDev * pNext; + /*! \brief root description URL */ + char * descURL; + /*! \brief ST: as advertised */ + char * st; + /*! \brief USN: as advertised */ + char * usn; + /*! \brief IPv6 scope id of the network interface */ + unsigned int scope_id; +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + /* C99 flexible array member */ + /*! \brief buffer for descURL, st and usn */ + char buffer[]; +#elif defined(__GNUC__) + char buffer[0]; +#else + /* Fallback to a hack */ + char buffer[1]; +#endif +}; + +/*! \brief free list returned by upnpDiscover() + * \param[in] devlist linked list to free + */ +MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist); + + +#ifdef __cplusplus +} +#endif + + +#endif /* UPNPDEV_H_INCLUDED */ diff --git a/Externals/miniupnpc/include/upnperrors.h b/Externals/miniupnpc/include/upnperrors.h new file mode 100644 index 0000000000..e1f36bfb3e --- /dev/null +++ b/Externals/miniupnpc/include/upnperrors.h @@ -0,0 +1,36 @@ +/* $Id: upnperrors.h,v 1.8 2025/02/08 23:15:17 nanard Exp $ */ +/* (c) 2007-2025 Thomas Bernard + * All rights reserved. + * MiniUPnP Project. + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * This software is subjet to the conditions detailed in the + * provided LICENCE file. */ +#ifndef UPNPERRORS_H_INCLUDED +#define UPNPERRORS_H_INCLUDED + +/*! \file upnperrors.h + * \brief code to string API for errors + */ +#include "miniupnpc_declspec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * \brief convert error code to string + * + * Work for both MiniUPnPc specific errors and UPnP standard defined + * errors. + * + * \param[in] err numerical error code + * \return a string description of the error code + * or NULL for undefinded errors + */ +MINIUPNP_LIBSPEC const char * strupnperror(int err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Externals/miniupnpc/include/upnpreplyparse.h b/Externals/miniupnpc/include/upnpreplyparse.h new file mode 100644 index 0000000000..8d4ca46430 --- /dev/null +++ b/Externals/miniupnpc/include/upnpreplyparse.h @@ -0,0 +1,82 @@ +/* $Id: upnpreplyparse.h,v 1.20 2025/02/08 23:15:17 nanard Exp $ */ +/* MiniUPnP project + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * (c) 2006-2025 Thomas Bernard + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution */ + +#ifndef UPNPREPLYPARSE_H_INCLUDED +#define UPNPREPLYPARSE_H_INCLUDED + +/*! \file upnpreplyparse.h + * \brief Parsing of UPnP SOAP responses + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/*! \brief Name/Value linked list + * not exposed in the public API + */ +struct NameValue; + +/*! \brief data structure for parsing */ +struct NameValueParserData { + /*! \brief name/value linked list */ + struct NameValue * l_head; + /*! \brief current element name */ + char curelt[64]; + /*! \brief port listing array */ + char * portListing; + /*! \brief port listing array length */ + int portListingLength; + /*! \brief flag indicating the current element is */ + int topelt; + /*! \brief top element character data */ + const char * cdata; + /*! \brief top element character data length */ + int cdatalen; +}; + +/*! + * \brief Parse XML and fill the structure + * + * \param[in] buffer XML data + * \param[in] bufsize buffer length + * \param[out] data structure to fill + */ +void +ParseNameValue(const char * buffer, int bufsize, + struct NameValueParserData * data); + +/*! + * \brief free memory + * + * \param[in,out] pdata data structure + */ +void +ClearNameValueList(struct NameValueParserData * pdata); + +/*! + * \brief get a value from the parsed data + * + * \param[in] pdata data structure + * \param[in] name name + * \return the value or NULL if not found + */ +char * +GetValueFromNameValueList(struct NameValueParserData * pdata, + const char * name); + +/* DisplayNameValueList() */ +#ifdef DEBUG +void +DisplayNameValueList(char * buffer, int bufsize); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Externals/miniupnpc/miniupnpc.vcxproj b/Externals/miniupnpc/miniupnpc.vcxproj index 44cd1b3c45..f117998703 100644 --- a/Externals/miniupnpc/miniupnpc.vcxproj +++ b/Externals/miniupnpc/miniupnpc.vcxproj @@ -1,63 +1,249 @@  - - - + + + + Debug Dll + Win32 + + + Debug + Win32 + + + Release Dll + Win32 + + + Release + Win32 + + + Release + x64 + + - {31643FDB-1BB8-4965-9DE7-000FC88D35AE} + {D28CE435-CB33-4BAE-8A52-C6EF915956F5} + miniupnpc + Win32Proj - + + StaticLibrary + v143 + Unicode + true + + + StaticLibrary + v143 + Unicode + true + + + DynamicLibrary + v143 + Unicode + true + + + StaticLibrary + v143 + Unicode + + + DynamicLibrary + v143 + Unicode + - - + + + + + + + + + + + + + + + - - + + <_ProjectFileVersion>14.0.25123.0 + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + + + + Disabled + _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + Level3 + EditAndContinue + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + Disabled + _CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;DEBUG;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies) + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + MaxSpeed + true + _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + MaxSpeed + true + _CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level3 + ProgramDatabase + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + + + + MaxSpeed + true + _CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + ..;..\include;%(AdditionalIncludeDirectories) + + + genminiupnpcstrings.vbs + + + ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies) + + + INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions) + + - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - + - - - + diff --git a/Externals/miniupnpc/src/addr_is_reserved.c b/Externals/miniupnpc/src/addr_is_reserved.c new file mode 100644 index 0000000000..86d8c4915c --- /dev/null +++ b/Externals/miniupnpc/src/addr_is_reserved.c @@ -0,0 +1,89 @@ +/* $Id: addr_is_reserved.c,v 1.7 2025/01/12 15:47:17 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas BERNARD + * copyright (c) 2005-2025 Thomas Bernard + * This software is subjet to the conditions detailed in the + * provided LICENSE file. */ +#ifdef _WIN32 +/* Win32 Specific includes and defines */ +#include +#include +#if !defined(_MSC_VER) +#include +#else /* !defined(_MSC_VER) */ +typedef unsigned long uint32_t; +#endif /* !defined(_MSC_VER) */ +#if !defined(_WIN32_WINNT_VISTA) +#define _WIN32_WINNT_VISTA 0x0600 +#endif +#else /* _WIN32 */ +#include +#include +#include +#include +#endif /* _WIN32 */ +#ifdef DEBUG +#include +#endif + +/* List of IP address blocks which are private / reserved and therefore not suitable for public external IP addresses */ +#define IP(a, b, c, d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) +#define MSK(m) (32-(m)) +static const struct { uint32_t address; uint32_t rmask; } reserved[] = { + { IP( 0, 0, 0, 0), MSK( 8) }, /* RFC1122 "This host on this network" */ + { IP( 10, 0, 0, 0), MSK( 8) }, /* RFC1918 Private-Use */ + { IP(100, 64, 0, 0), MSK(10) }, /* RFC6598 Shared Address Space */ + { IP(127, 0, 0, 0), MSK( 8) }, /* RFC1122 Loopback */ + { IP(169, 254, 0, 0), MSK(16) }, /* RFC3927 Link-Local */ + { IP(172, 16, 0, 0), MSK(12) }, /* RFC1918 Private-Use */ + { IP(192, 0, 0, 0), MSK(24) }, /* RFC6890 IETF Protocol Assignments */ + { IP(192, 0, 2, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-1) */ + { IP(192, 31, 196, 0), MSK(24) }, /* RFC7535 AS112-v4 */ + { IP(192, 52, 193, 0), MSK(24) }, /* RFC7450 AMT */ + { IP(192, 88, 99, 0), MSK(24) }, /* RFC7526 6to4 Relay Anycast */ + { IP(192, 168, 0, 0), MSK(16) }, /* RFC1918 Private-Use */ + { IP(192, 175, 48, 0), MSK(24) }, /* RFC7534 Direct Delegation AS112 Service */ + { IP(198, 18, 0, 0), MSK(15) }, /* RFC2544 Benchmarking */ + { IP(198, 51, 100, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-2) */ + { IP(203, 0, 113, 0), MSK(24) }, /* RFC5737 Documentation (TEST-NET-3) */ + { IP(224, 0, 0, 0), MSK( 4) }, /* RFC1112 Multicast */ + { IP(240, 0, 0, 0), MSK( 4) }, /* RFC1112 Reserved for Future Use + RFC919 Limited Broadcast */ +}; +#undef IP +#undef MSK + +/** + * @return 1 or 0 + */ +int addr_is_reserved(const char * addr_str) +{ + uint32_t addr_n, address; + size_t i; + +#if defined(_WIN32) && (_WIN32_WINNT < _WIN32_WINNT_VISTA) + addr_n = inet_addr(addr_str); + if (addr_n == INADDR_NONE) + return 1; +#else + /* was : addr_n = inet_addr(addr_str); */ + if (inet_pton(AF_INET, addr_str, &addr_n) <= 0) { + /* error */ + return 1; + } +#endif + + address = ntohl(addr_n); + + for (i = 0; i < sizeof(reserved)/sizeof(reserved[0]); ++i) { + if ((address >> reserved[i].rmask) == (reserved[i].address >> reserved[i].rmask)) { +#ifdef DEBUG + printf("IP address %s is reserved\n", addr_str); +#endif + return 1; + } + } + + return 0; +} diff --git a/Externals/miniupnpc/src/addr_is_reserved.h b/Externals/miniupnpc/src/addr_is_reserved.h new file mode 100644 index 0000000000..f8b5d66a09 --- /dev/null +++ b/Externals/miniupnpc/src/addr_is_reserved.h @@ -0,0 +1,14 @@ +/* $Id: $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project: miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2020 Thomas Bernard + * This software is subjects to the conditions detailed + * in the LICENCE file provided within this distribution */ +#ifndef ADDR_IS_RESERVED_H_INCLUDED +#define ADDR_IS_RESERVED_H_INCLUDED + +int addr_is_reserved(const char * addr_str); + +#endif /* ADDR_IS_RESERVED_H_INCLUDED */ diff --git a/Externals/miniupnpc/src/connecthostport.c b/Externals/miniupnpc/src/connecthostport.c index d66ae315f4..79f832b8db 100644 --- a/Externals/miniupnpc/src/connecthostport.c +++ b/Externals/miniupnpc/src/connecthostport.c @@ -1,7 +1,8 @@ -/* $Id: connecthostport.c,v 1.13 2014/03/31 12:36:36 nanard Exp $ */ -/* Project : miniupnp +/* $Id: connecthostport.c,v 1.24 2020/11/09 19:26:53 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2010-2014 Thomas Bernard + * Copyright (c) 2010-2020 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -18,11 +19,15 @@ #include #include #define MAXHOSTNAMELEN 64 -#define snprintf _snprintf +#include "win32_snprintf.h" #define herror #define socklen_t int #else /* #ifdef _WIN32 */ #include +#include +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT +#include +#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ #include #include #include @@ -32,20 +37,10 @@ /* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions * during the connect() call */ #define MINIUPNPC_IGNORE_EINTR -#ifndef USE_GETHOSTBYNAME -#include #include #include -#endif /* #ifndef USE_GETHOSTBYNAME */ #endif /* #else _WIN32 */ -/* definition of PRINT_SOCKET_ERROR */ -#ifdef _WIN32 -#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); -#else -#define PRINT_SOCKET_ERROR(x) perror(x) -#endif - #if defined(__amigaos__) || defined(__amigaos4__) #define herror(A) printf("%s\n", A) #endif @@ -59,10 +54,11 @@ /* connecthostport() * return a socket connected (TCP) to the host and port * or -1 in case of error */ -int connecthostport(const char * host, unsigned short port, - unsigned int scope_id) +SOCKET connecthostport(const char * host, unsigned short port, + unsigned int scope_id) { - int s, n; + SOCKET s; + int n; #ifdef USE_GETHOSTBYNAME struct sockaddr_in dest; struct hostent *hp; @@ -81,15 +77,15 @@ int connecthostport(const char * host, unsigned short port, if(hp == NULL) { herror(host); - return -1; + return INVALID_SOCKET; } memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr)); memset(dest.sin_zero, 0, sizeof(dest.sin_zero)); s = socket(PF_INET, SOCK_STREAM, 0); - if(s < 0) + if(ISINVALID(s)) { PRINT_SOCKET_ERROR("socket"); - return -1; + return INVALID_SOCKET; } #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT /* setting a 3 seconds timeout for the connect() call */ @@ -97,13 +93,13 @@ int connecthostport(const char * host, unsigned short port, timeout.tv_usec = 0; if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0) { - PRINT_SOCKET_ERROR("setsockopt"); + PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO"); } timeout.tv_sec = 3; timeout.tv_usec = 0; if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0) { - PRINT_SOCKET_ERROR("setsockopt"); + PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO"); } #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ dest.sin_family = AF_INET; @@ -113,22 +109,36 @@ int connecthostport(const char * host, unsigned short port, /* EINTR The system call was interrupted by a signal that was caught * EINPROGRESS The socket is nonblocking and the connection cannot * be completed immediately. */ - while(n < 0 && (errno == EINTR || errno = EINPROGRESS)) + while(n < 0 && (errno == EINTR || errno == EINPROGRESS)) { socklen_t len; fd_set wset; int err; FD_ZERO(&wset); FD_SET(s, &wset); - if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR) +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT + timeout.tv_sec = 3; + timeout.tv_usec = 0; + n = select(s + 1, NULL, &wset, NULL, &timeout); +#else + n = select(s + 1, NULL, &wset, NULL, NULL); +#endif + if(n == -1 && errno == EINTR) continue; +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT + if(n == 0) { + errno = ETIMEDOUT; + n = -1; + break; + } +#endif /*len = 0;*/ /*n = getpeername(s, NULL, &len);*/ len = sizeof(err); if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { PRINT_SOCKET_ERROR("getsockopt"); closesocket(s); - return -1; + return INVALID_SOCKET; } if(err != 0) { errno = err; @@ -140,7 +150,7 @@ int connecthostport(const char * host, unsigned short port, { PRINT_SOCKET_ERROR("connect"); closesocket(s); - return -1; + return INVALID_SOCKET; } #else /* #ifdef USE_GETHOSTBYNAME */ /* use getaddrinfo() instead of gethostbyname() */ @@ -160,7 +170,7 @@ int connecthostport(const char * host, unsigned short port, for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++) { tmp_host[i] = host[j]; - if(0 == memcmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */ + if(0 == strncmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */ j+=2; /* skip "25" */ } tmp_host[i] = '\0'; @@ -178,13 +188,19 @@ int connecthostport(const char * host, unsigned short port, #else fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n)); #endif - return -1; + return INVALID_SOCKET; } - s = -1; + s = INVALID_SOCKET; for(p = ai; p; p = p->ai_next) { + if(!ISINVALID(s)) + closesocket(s); +#ifdef DEBUG + printf("ai_family=%d ai_socktype=%d ai_protocol=%d (PF_INET=%d, PF_INET6=%d)\n", + p->ai_family, p->ai_socktype, p->ai_protocol, PF_INET, PF_INET6); +#endif s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if(s < 0) + if(ISINVALID(s)) continue; if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) { struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr; @@ -205,7 +221,7 @@ int connecthostport(const char * host, unsigned short port, PRINT_SOCKET_ERROR("setsockopt"); } #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ - n = connect(s, p->ai_addr, p->ai_addrlen); + n = connect(s, p->ai_addr, MSC_CAST_INT p->ai_addrlen); #ifdef MINIUPNPC_IGNORE_EINTR /* EINTR The system call was interrupted by a signal that was caught * EINPROGRESS The socket is nonblocking and the connection cannot @@ -217,8 +233,22 @@ int connecthostport(const char * host, unsigned short port, int err; FD_ZERO(&wset); FD_SET(s, &wset); - if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR) +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT + timeout.tv_sec = 3; + timeout.tv_usec = 0; + n = select(s + 1, NULL, &wset, NULL, &timeout); +#else + n = select(s + 1, NULL, &wset, NULL, NULL); +#endif + if(n == -1 && errno == EINTR) continue; +#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT + if(n == 0) { + errno = ETIMEDOUT; + n = -1; + break; + } +#endif /*len = 0;*/ /*n = getpeername(s, NULL, &len);*/ len = sizeof(err); @@ -226,7 +256,7 @@ int connecthostport(const char * host, unsigned short port, PRINT_SOCKET_ERROR("getsockopt"); closesocket(s); freeaddrinfo(ai); - return -1; + return INVALID_SOCKET; } if(err != 0) { errno = err; @@ -234,28 +264,21 @@ int connecthostport(const char * host, unsigned short port, } } #endif /* #ifdef MINIUPNPC_IGNORE_EINTR */ - if(n < 0) - { - closesocket(s); - continue; - } - else - { + if(n >= 0) /* connect() was successful */ break; - } } freeaddrinfo(ai); - if(s < 0) + if(ISINVALID(s)) { PRINT_SOCKET_ERROR("socket"); - return -1; + return INVALID_SOCKET; } if(n < 0) { PRINT_SOCKET_ERROR("connect"); - return -1; + closesocket(s); + return INVALID_SOCKET; } #endif /* #ifdef USE_GETHOSTBYNAME */ return s; } - diff --git a/Externals/miniupnpc/src/connecthostport.h b/Externals/miniupnpc/src/connecthostport.h index f3b2d2a842..701816b5b6 100644 --- a/Externals/miniupnpc/src/connecthostport.h +++ b/Externals/miniupnpc/src/connecthostport.h @@ -2,17 +2,19 @@ /* Project: miniupnp * http://miniupnp.free.fr/ * Author: Thomas Bernard - * Copyright (c) 2010-2012 Thomas Bernard + * Copyright (c) 2010-2018 Thomas Bernard * This software is subjects to the conditions detailed * in the LICENCE file provided within this distribution */ #ifndef CONNECTHOSTPORT_H_INCLUDED #define CONNECTHOSTPORT_H_INCLUDED +#include "miniupnpc_socketdef.h" + /* connecthostport() * return a socket connected (TCP) to the host and port - * or -1 in case of error */ -int connecthostport(const char * host, unsigned short port, - unsigned int scope_id); + * or INVALID_SOCKET in case of error */ +SOCKET connecthostport(const char * host, unsigned short port, + unsigned int scope_id); #endif diff --git a/Externals/miniupnpc/src/igd_desc_parse.h b/Externals/miniupnpc/src/igd_desc_parse.h index 0de546b697..52a4ab3e31 100644 --- a/Externals/miniupnpc/src/igd_desc_parse.h +++ b/Externals/miniupnpc/src/igd_desc_parse.h @@ -1,46 +1,72 @@ -/* $Id: igd_desc_parse.h,v 1.12 2014/11/17 17:19:13 nanard Exp $ */ +/* $Id: igd_desc_parse.h,v 1.14 2025/02/08 23:15:16 nanard Exp $ */ /* Project : miniupnp - * http://miniupnp.free.fr/ + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas Bernard - * Copyright (c) 2005-2014 Thomas Bernard + * Copyright (c) 2005-2025 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * */ #ifndef IGD_DESC_PARSE_H_INCLUDED #define IGD_DESC_PARSE_H_INCLUDED -/* Structure to store the result of the parsing of UPnP - * descriptions of Internet Gateway Devices */ +/*! \file igd_desc_parse.h + * \brief API to parse UPNP device description XML + * \todo should not be exposed in the public API + */ + +/*! \brief maximum lenght of URLs */ #define MINIUPNPC_URL_MAXSIZE (128) + +/*! \brief Structure to store the result of the parsing of UPnP + * descriptions of Internet Gateway Devices services */ struct IGDdatas_service { + /*! \brief controlURL for the service */ char controlurl[MINIUPNPC_URL_MAXSIZE]; + /*! \brief eventSubURL for the service */ char eventsuburl[MINIUPNPC_URL_MAXSIZE]; + /*! \brief SCPDURL for the service */ char scpdurl[MINIUPNPC_URL_MAXSIZE]; + /*! \brief serviceType */ char servicetype[MINIUPNPC_URL_MAXSIZE]; /*char devicetype[MINIUPNPC_URL_MAXSIZE];*/ }; +/*! \brief Structure to store the result of the parsing of UPnP + * descriptions of Internet Gateway Devices */ struct IGDdatas { + /*! \brief current element name */ char cureltname[MINIUPNPC_URL_MAXSIZE]; + /*! \brief URLBase */ char urlbase[MINIUPNPC_URL_MAXSIZE]; + /*! \brief presentationURL */ char presentationurl[MINIUPNPC_URL_MAXSIZE]; + /*! \brief depth into the XML tree */ int level; - /*int state;*/ - /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */ + /*! \brief "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */ struct IGDdatas_service CIF; - /* "urn:schemas-upnp-org:service:WANIPConnection:1" - * "urn:schemas-upnp-org:service:WANPPPConnection:1" */ + /*! \brief first of "urn:schemas-upnp-org:service:WANIPConnection:1" + * or "urn:schemas-upnp-org:service:WANPPPConnection:1" */ struct IGDdatas_service first; - /* if both WANIPConnection and WANPPPConnection are present */ + /*! \brief second of "urn:schemas-upnp-org:service:WANIPConnection:1" + * or "urn:schemas-upnp-org:service:WANPPPConnection:1" */ struct IGDdatas_service second; - /* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */ + /*! \brief "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */ struct IGDdatas_service IPv6FC; - /* tmp */ + /*! \brief currently parsed service */ struct IGDdatas_service tmp; }; +/*! + * \brief XML start element handler + */ void IGDstartelt(void *, const char *, int); +/*! + * \brief XML end element handler + */ void IGDendelt(void *, const char *, int); +/*! + * \brief XML characted data handler + */ void IGDdata(void *, const char *, int); #ifdef DEBUG void printIGD(struct IGDdatas *); diff --git a/Externals/miniupnpc/src/listdevices.c b/Externals/miniupnpc/src/listdevices.c new file mode 100644 index 0000000000..751c6547ee --- /dev/null +++ b/Externals/miniupnpc/src/listdevices.c @@ -0,0 +1,198 @@ +/* $Id: listdevices.c,v 1.6 2015/07/23 20:40:08 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2013-2015 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. */ + +#include +#include +#include +#ifdef _WIN32 +#include +#endif /* _WIN32 */ +#include "miniupnpc.h" + +struct upnp_dev_list { + struct upnp_dev_list * next; + char * descURL; + struct UPNPDev * * array; + size_t count; + size_t allocated_count; +}; + +#define ADD_DEVICE_COUNT_STEP 16 + +void add_device(struct upnp_dev_list * * list_head, struct UPNPDev * dev) +{ + struct upnp_dev_list * elt; + size_t i; + + if(dev == NULL) + return; + for(elt = *list_head; elt != NULL; elt = elt->next) { + if(strcmp(elt->descURL, dev->descURL) == 0) { + for(i = 0; i < elt->count; i++) { + if (strcmp(elt->array[i]->st, dev->st) == 0 && strcmp(elt->array[i]->usn, dev->usn) == 0) { + return; /* already found */ + } + } + if(elt->count >= elt->allocated_count) { + struct UPNPDev * * tmp; + elt->allocated_count += ADD_DEVICE_COUNT_STEP; + tmp = realloc(elt->array, elt->allocated_count * sizeof(struct UPNPDev *)); + if(tmp == NULL) { + fprintf(stderr, "Failed to realloc(%p, %lu)\n", elt->array, (unsigned long)(elt->allocated_count * sizeof(struct UPNPDev *))); + return; + } + elt->array = tmp; + } + elt->array[elt->count++] = dev; + return; + } + } + elt = malloc(sizeof(struct upnp_dev_list)); + if(elt == NULL) { + fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)sizeof(struct upnp_dev_list)); + return; + } + elt->next = *list_head; + elt->descURL = strdup(dev->descURL); + if(elt->descURL == NULL) { + fprintf(stderr, "Failed to strdup(%s)\n", dev->descURL); + free(elt); + return; + } + elt->allocated_count = ADD_DEVICE_COUNT_STEP; + elt->array = malloc(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *)); + if(elt->array == NULL) { + fprintf(stderr, "Failed to malloc(%lu)\n", (unsigned long)(ADD_DEVICE_COUNT_STEP * sizeof(struct UPNPDev *))); + free(elt->descURL); + free(elt); + return; + } + elt->array[0] = dev; + elt->count = 1; + *list_head = elt; +} + +void free_device(struct upnp_dev_list * elt) +{ + free(elt->descURL); + free(elt->array); + free(elt); +} + +int main(int argc, char * * argv) +{ + const char * searched_device = NULL; + const char * * searched_devices = NULL; + const char * multicastif = 0; + const char * minissdpdpath = 0; + int ipv6 = 0; + unsigned char ttl = 2; + int error = 0; + struct UPNPDev * devlist = 0; + struct UPNPDev * dev; + struct upnp_dev_list * sorted_list = NULL; + struct upnp_dev_list * dev_array; + int i; + +#ifdef _WIN32 + WSADATA wsaData; + int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if(nResult != NO_ERROR) + { + fprintf(stderr, "WSAStartup() failed.\n"); + return -1; + } +#endif + + for(i = 1; i < argc; i++) { + if(strcmp(argv[i], "-6") == 0) + ipv6 = 1; + else if(strcmp(argv[i], "-d") == 0) { + if(++i >= argc) { + fprintf(stderr, "%s option needs one argument\n", "-d"); + return 1; + } + searched_device = argv[i]; + } else if(strcmp(argv[i], "-t") == 0) { + if(++i >= argc) { + fprintf(stderr, "%s option needs one argument\n", "-t"); + return 1; + } + ttl = (unsigned char)atoi(argv[i]); + } else if(strcmp(argv[i], "-l") == 0) { + if(++i >= argc) { + fprintf(stderr, "-l option needs at least one argument\n"); + return 1; + } + searched_devices = (const char * *)(argv + i); + break; + } else if(strcmp(argv[i], "-m") == 0) { + if(++i >= argc) { + fprintf(stderr, "-m option needs one argument\n"); + return 1; + } + multicastif = argv[i]; + } else { + printf("usage : %s [options] [-l ...]\n", argv[0]); + printf("options :\n"); + printf(" -6 : use IPv6\n"); + printf(" -m address/ifname : network interface to use for multicast\n"); + printf(" -d : search only for this type of device\n"); + printf(" -l ... : search only for theses types of device\n"); + printf(" -t ttl : set multicast TTL. Default value is 2.\n"); + printf(" -h : this help\n"); + return 1; + } + } + + if(searched_device) { + printf("searching UPnP device type %s\n", searched_device); + devlist = upnpDiscoverDevice(searched_device, + 2000, multicastif, minissdpdpath, + 0/*localport*/, ipv6, ttl, &error); + } else if(searched_devices) { + printf("searching UPnP device types :\n"); + for(i = 0; searched_devices[i]; i++) + printf("\t%s\n", searched_devices[i]); + devlist = upnpDiscoverDevices(searched_devices, + 2000, multicastif, minissdpdpath, + 0/*localport*/, ipv6, ttl, &error, 1); + } else { + printf("searching all UPnP devices\n"); + devlist = upnpDiscoverAll(2000, multicastif, minissdpdpath, + 0/*localport*/, ipv6, ttl, &error); + } + if(devlist) { + for(dev = devlist, i = 1; dev != NULL; dev = dev->pNext, i++) { + printf("%3d: %-48s\n", i, dev->st); + printf(" %s\n", dev->descURL); + printf(" %s\n", dev->usn); + add_device(&sorted_list, dev); + } + putchar('\n'); + for (dev_array = sorted_list; dev_array != NULL ; dev_array = dev_array->next) { + printf("%s :\n", dev_array->descURL); + for(i = 0; (unsigned)i < dev_array->count; i++) { + printf("%2d: %s\n", i+1, dev_array->array[i]->st); + printf(" %s\n", dev_array->array[i]->usn); + } + putchar('\n'); + } + freeUPNPDevlist(devlist); + while(sorted_list != NULL) { + dev_array = sorted_list; + sorted_list = sorted_list->next; + free_device(dev_array); + } + } else { + fprintf(stderr,"No UPnP devices found\n"); + return 1; + } + + return 0; +} + diff --git a/Externals/miniupnpc/src/minihttptestserver.c b/Externals/miniupnpc/src/minihttptestserver.c new file mode 100644 index 0000000000..57235841eb --- /dev/null +++ b/Externals/miniupnpc/src/minihttptestserver.c @@ -0,0 +1,690 @@ +/* $Id: minihttptestserver.c,v 1.25 2020/05/29 21:14:22 nanard Exp $ */ +/* Project : miniUPnP + * Author : Thomas Bernard + * Copyright (c) 2011-2018 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK 0x7f000001 +#endif + +#define CRAP_LENGTH (2048) + +static int server(unsigned short port, const char * expected_file_name, int ipv6); + +volatile sig_atomic_t quit = 0; +volatile sig_atomic_t child_to_wait_for = 0; + +/** + * signal handler for SIGCHLD (child status has changed) + */ +void handle_signal_chld(int sig) +{ + (void)sig; + /* printf("handle_signal_chld(%d)\n", sig); */ + ++child_to_wait_for; +} + +/** + * signal handler for SIGINT (CRTL C) + */ +void handle_signal_int(int sig) +{ + (void)sig; + /* printf("handle_signal_int(%d)\n", sig); */ + quit = 1; +} + +/** + * build a text/plain content of the specified length + */ +void build_content(char * p, size_t n) +{ + char line_buffer[80]; + int k; + int i = 0; + + while(n > 0) { + k = snprintf(line_buffer, sizeof(line_buffer), + "%04d_ABCDEFGHIJKL_This_line_is_64_bytes_long_ABCDEFGHIJKL_%04d\r\n", + i, i); + if(k != 64) { + fprintf(stderr, "snprintf() returned %d in build_content()\n", k); + } + ++i; + if(n >= 64) { + memcpy(p, line_buffer, 64); + p += 64; + n -= 64; + } else { + memcpy(p, line_buffer, n); + p += n; + n = 0; + } + } +} + +/** + * build crappy content + */ +void build_crap(char * p, size_t n) +{ + static const char crap[] = "_CRAP_\r\n"; + size_t i; + + while(n > 0) { + i = sizeof(crap) - 1; + if(i > n) + i = n; + memcpy(p, crap, i); + p += i; + n -= i; + } +} + +/** + * build chunked response. + * return a malloc'ed buffer + */ +char * build_chunked_response(size_t content_length, size_t * response_len) +{ + char * response_buffer; + char * content_buffer; + size_t buffer_length; + size_t i; + unsigned int n; + + /* allocate to have some margin */ + buffer_length = 256 + content_length + (content_length >> 4); + response_buffer = malloc(buffer_length); + if(response_buffer == NULL) + return NULL; + *response_len = snprintf(response_buffer, buffer_length, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n"); + + /* build the content */ + content_buffer = malloc(content_length); + if(content_buffer == NULL) { + free(response_buffer); + return NULL; + } + build_content(content_buffer, content_length); + + /* chunk it */ + i = 0; + while(i < content_length) { + n = (rand() % 199) + 1; + if(i + n > content_length) { + n = content_length - i; + } + /* TODO : check buffer size ! */ + *response_len += snprintf(response_buffer + *response_len, + buffer_length - *response_len, + "%x\r\n", n); + memcpy(response_buffer + *response_len, content_buffer + i, n); + *response_len += n; + i += n; + response_buffer[(*response_len)++] = '\r'; + response_buffer[(*response_len)++] = '\n'; + } + /* the last chunk : "0\r\n" a empty body and then + * the final "\r\n" */ + memcpy(response_buffer + *response_len, "0\r\n\r\n", 5); + *response_len += 5; + free(content_buffer); + + printf("resp_length=%lu buffer_length=%lu content_length=%lu\n", + *response_len, buffer_length, content_length); + return response_buffer; +} + +/* favicon.ico generator */ +#ifdef OLD_HEADER +#define FAVICON_LENGTH (6 + 16 + 12 + 8 + 32 * 4) +#else +#define FAVICON_LENGTH (6 + 16 + 40 + 8 + 32 * 4) +#endif +void build_favicon_content(unsigned char * p, size_t n) +{ + int i; + if(n < FAVICON_LENGTH) + return; + /* header : 6 bytes */ + *p++ = 0; + *p++ = 0; + *p++ = 1; /* type : ICO */ + *p++ = 0; + *p++ = 1; /* number of images in file */ + *p++ = 0; + /* image directory (1 entry) : 16 bytes */ + *p++ = 16; /* width */ + *p++ = 16; /* height */ + *p++ = 2; /* number of colors in the palette. 0 = no palette */ + *p++ = 0; /* reserved */ + *p++ = 1; /* color planes */ + *p++ = 0; /* " */ + *p++ = 1; /* bpp */ + *p++ = 0; /* " */ +#ifdef OLD_HEADER + *p++ = 12 + 8 + 32 * 4; /* bmp size */ +#else + *p++ = 40 + 8 + 32 * 4; /* bmp size */ +#endif + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 6 + 16; /* bmp offset */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + /* BMP */ +#ifdef OLD_HEADER + /* BITMAPCOREHEADER */ + *p++ = 12; /* size of this header */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 16; /* width */ + *p++ = 0; /* " */ + *p++ = 16 * 2; /* height x 2 ! */ + *p++ = 0; /* " */ + *p++ = 1; /* color planes */ + *p++ = 0; /* " */ + *p++ = 1; /* bpp */ + *p++ = 0; /* " */ +#else + /* BITMAPINFOHEADER */ + *p++ = 40; /* size of this header */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 16; /* width */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 16 * 2; /* height x 2 ! */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 0; /* " */ + *p++ = 1; /* color planes */ + *p++ = 0; /* " */ + *p++ = 1; /* bpp */ + *p++ = 0; /* " */ + /* compression method, image size, ppm x, ppm y */ + /* colors in the palette ? */ + /* important colors */ + for(i = 4 * 6; i > 0; --i) + *p++ = 0; +#endif + /* palette */ + *p++ = 0; /* b */ + *p++ = 0; /* g */ + *p++ = 0; /* r */ + *p++ = 0; /* reserved */ + *p++ = 255; /* b */ + *p++ = 255; /* g */ + *p++ = 255; /* r */ + *p++ = 0; /* reserved */ + /* pixel data */ + for(i = 16; i > 0; --i) { + if(i & 1) { + *p++ = 0125; + *p++ = 0125; + } else { + *p++ = 0252; + *p++ = 0252; + } + *p++ = 0; + *p++ = 0; + } + /* Opacity MASK */ + for(i = 16 * 4; i > 0; --i) { + *p++ = 0; + } +} + +enum modes { + MODE_INVALID, MODE_CHUNKED, MODE_ADDCRAP, MODE_NORMAL, MODE_FAVICON, MODE_MALFORMED +}; + +const struct { + const enum modes mode; + const char * text; +} modes_array[] = { + {MODE_CHUNKED, "chunked"}, + {MODE_ADDCRAP, "addcrap"}, + {MODE_NORMAL, "normal"}, + {MODE_FAVICON, "favicon.ico"}, + {MODE_MALFORMED, "malformed"}, + {MODE_INVALID, NULL} +}; + +/** + * write the response with random behaviour ! + */ +void send_response(int c, const char * buffer, size_t len) +{ + ssize_t n; + while(len > 0) { + n = (rand() % 99) + 1; + if((size_t)n > len) + n = len; + n = write(c, buffer, n); + if(n < 0) { + if(errno != EINTR) { + perror("write"); + return; + } + /* if errno == EINTR, try again */ + } else { + len -= n; + buffer += n; + usleep(10000); /* 10ms */ + } + } +} + +/** + * handle the HTTP connection + */ +void handle_http_connection(int c) +{ + char request_buffer[2048]; + size_t request_len = 0; + int headers_found = 0; + ssize_t n, m; + size_t i; + char request_method[16]; + char request_uri[256]; + char http_version[16]; + char * p; + char * response_buffer; + size_t response_len; + enum modes mode; + size_t content_length = 16*1024; + + /* read the request */ + while(request_len < sizeof(request_buffer) && !headers_found) { + n = read(c, + request_buffer + request_len, + sizeof(request_buffer) - request_len); + if(n < 0) { + if(errno == EINTR) + continue; + perror("read"); + return; + } else if(n==0) { + /* remote host closed the connection */ + break; + } else { + request_len += n; + for(i = 0; i < request_len - 3; i++) { + if(0 == memcmp(request_buffer + i, "\r\n\r\n", 4)) { + /* found the end of headers */ + headers_found = 1; + break; + } + } + } + } + if(!headers_found) { + /* error */ + printf("no HTTP header found in the request\n"); + return; + } + printf("headers :\n%.*s", (int)request_len, request_buffer); + /* the request have been received, now parse the request line */ + p = request_buffer; + for(i = 0; i < sizeof(request_method) - 1; i++) { + if(*p == ' ' || *p == '\r') + break; + request_method[i] = *p; + ++p; + } + request_method[i] = '\0'; + while(*p == ' ') + p++; + for(i = 0; i < (int)sizeof(request_uri) - 1; i++) { + if(*p == ' ' || *p == '\r') + break; + request_uri[i] = *p; + ++p; + } + request_uri[i] = '\0'; + while(*p == ' ') + p++; + for(i = 0; i < (int)sizeof(http_version) - 1; i++) { + if(*p == ' ' || *p == '\r') + break; + http_version[i] = *p; + ++p; + } + http_version[i] = '\0'; + printf("Method = %s, URI = %s, %s\n", + request_method, request_uri, http_version); + /* check if the request method is allowed */ + if(0 != strcmp(request_method, "GET")) { + const char response405[] = "HTTP/1.1 405 Method Not Allowed\r\n" + "Allow: GET\r\n\r\n"; + const char * pc; + /* 405 Method Not Allowed */ + /* The response MUST include an Allow header containing a list + * of valid methods for the requested resource. */ + n = sizeof(response405) - 1; + pc = response405; + while(n > 0) { + m = write(c, pc, n); + if(m<0) { + if(errno != EINTR) { + perror("write"); + return; + } + } else { + n -= m; + pc += m; + } + } + return; + } + + mode = MODE_INVALID; + /* use the request URI to know what to do */ + for(i = 0; modes_array[i].mode != MODE_INVALID; i++) { + if(strstr(request_uri, modes_array[i].text)) { + mode = modes_array[i].mode; /* found */ + break; + } + } + + switch(mode) { + case MODE_MALFORMED: + response_len = 2048; + response_buffer = malloc(response_len); + if(!response_buffer) + break; + n = snprintf(response_buffer, response_len, + "HTTP/1.1 \r\n" + "\r\n" + /*"0000\r\n"*/); + for (i = n; i < response_len; i++) { + response_buffer[i] = ' '; + } + response_len = n; + break; + case MODE_CHUNKED: + response_buffer = build_chunked_response(content_length, &response_len); + break; + case MODE_ADDCRAP: + response_len = content_length+256; + response_buffer = malloc(response_len); + if(!response_buffer) + break; + n = snprintf(response_buffer, response_len, + "HTTP/1.1 200 OK\r\n" + "Server: minihttptestserver\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: %lu\r\n" + "\r\n", content_length); + response_len = content_length+n+CRAP_LENGTH; + p = realloc(response_buffer, response_len); + if(p == NULL) { + /* error 500 */ + free(response_buffer); + response_buffer = NULL; + break; + } + response_buffer = p; + build_content(response_buffer + n, content_length); + build_crap(response_buffer + n + content_length, CRAP_LENGTH); + break; + case MODE_FAVICON: + content_length = FAVICON_LENGTH; + response_len = content_length + 256; + response_buffer = malloc(response_len); + if(!response_buffer) + break; + n = snprintf(response_buffer, response_len, + "HTTP/1.1 200 OK\r\n" + "Server: minihttptestserver\r\n" + "Content-Type: image/vnd.microsoft.icon\r\n" + "Content-Length: %lu\r\n" + "\r\n", content_length); + /* image/x-icon */ + build_favicon_content((unsigned char *)(response_buffer + n), content_length); + response_len = content_length + n; + break; + default: + response_len = content_length+256; + response_buffer = malloc(response_len); + if(!response_buffer) + break; + n = snprintf(response_buffer, response_len, + "HTTP/1.1 200 OK\r\n" + "Server: minihttptestserver\r\n" + "Content-Type: text/plain\r\n" + "\r\n"); + response_len = content_length+n; + p = realloc(response_buffer, response_len); + if(p == NULL) { + /* Error 500 */ + free(response_buffer); + response_buffer = NULL; + break; + } + response_buffer = p; + build_content(response_buffer + n, response_len - n); + } + + if(response_buffer) { + send_response(c, response_buffer, response_len); + free(response_buffer); + } else { + /* Error 500 */ + } +} + +/** + */ +int main(int argc, char * * argv) { + int ipv6 = 0; + int r, i; + unsigned short port = 0; + const char * expected_file_name = NULL; + + for(i = 1; i < argc; i++) { + if(argv[i][0] == '-') { + switch(argv[i][1]) { + case '6': + ipv6 = 1; + break; + case 'e': + /* write expected file ! */ + expected_file_name = argv[++i]; + break; + case 'p': + /* port */ + if(++i < argc) { + port = (unsigned short)atoi(argv[i]); + } + break; + default: + fprintf(stderr, "unknown command line switch '%s'\n", argv[i]); + } + } else { + fprintf(stderr, "unknown command line argument '%s'\n", argv[i]); + } + } + + srand(time(NULL)); + + r = server(port, expected_file_name, ipv6); + if(r != 0) { + printf("*** ERROR ***\n"); + } + return r; +} + +static int server(unsigned short port, const char * expected_file_name, int ipv6) +{ + int s, c; + int i; + struct sockaddr_storage server_addr; + socklen_t server_addrlen; + struct sockaddr_storage client_addr; + socklen_t client_addrlen; + pid_t pid; + int child = 0; + int status; + struct sigaction sa; + + memset(&sa, 0, sizeof(struct sigaction)); + + /*signal(SIGCHLD, handle_signal_chld);*/ + sa.sa_handler = handle_signal_chld; + if(sigaction(SIGCHLD, &sa, NULL) < 0) { + perror("sigaction"); + return 1; + } + /*signal(SIGINT, handle_signal_int);*/ + sa.sa_handler = handle_signal_int; + if(sigaction(SIGINT, &sa, NULL) < 0) { + perror("sigaction"); + return 1; + } + + s = socket(ipv6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0); + if(s < 0) { + perror("socket"); + return 1; + } + memset(&server_addr, 0, sizeof(struct sockaddr_storage)); + memset(&client_addr, 0, sizeof(struct sockaddr_storage)); + if(ipv6) { + struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr; + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(port); + addr->sin6_addr = in6addr_loopback; + } else { + struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr; + addr->sin_family = AF_INET; + addr->sin_port = htons(port); + addr->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + } + if(bind(s, (struct sockaddr *)&server_addr, + ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) < 0) { + perror("bind"); + return 1; + } + if(listen(s, 5) < 0) { + perror("listen"); + } + if(port == 0) { + server_addrlen = sizeof(struct sockaddr_storage); + if(getsockname(s, (struct sockaddr *)&server_addr, &server_addrlen) < 0) { + perror("getsockname"); + return 1; + } + if(ipv6) { + struct sockaddr_in6 * addr = (struct sockaddr_in6 *)&server_addr; + port = ntohs(addr->sin6_port); + } else { + struct sockaddr_in * addr = (struct sockaddr_in *)&server_addr; + port = ntohs(addr->sin_port); + } + printf("Listening on port %hu\n", port); + fflush(stdout); + } + + /* write expected file */ + if(expected_file_name) { + FILE * f; + f = fopen(expected_file_name, "wb"); + if(f) { + char * buffer; + buffer = malloc(16*1024); + if(buffer == NULL) { + fprintf(stderr, "memory allocation error\n"); + } else { + build_content(buffer, 16*1024); + i = fwrite(buffer, 1, 16*1024, f); + if(i != 16*1024) { + fprintf(stderr, "error writing to file %s : %dbytes written (out of %d)\n", expected_file_name, i, 16*1024); + } + free(buffer); + } + fclose(f); + } else { + fprintf(stderr, "error opening file %s for writing\n", expected_file_name); + } + } + + /* fork() loop */ + while(!child && !quit) { + while(child_to_wait_for > 0) { + pid = wait(&status); + if(pid < 0) { + perror("wait"); + } else { + printf("child(%d) terminated with status %d\n", (int)pid, status); + } + --child_to_wait_for; + } + client_addrlen = sizeof(struct sockaddr_storage); + c = accept(s, (struct sockaddr *)&client_addr, + &client_addrlen); + if(c < 0) { + if(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; + perror("accept"); + return 1; + } + printf("accept...\n"); + pid = fork(); + if(pid < 0) { + perror("fork"); + return 1; + } else if(pid == 0) { + /* child */ + child = 1; + close(s); + s = -1; + handle_http_connection(c); + } + close(c); + } + if(s >= 0) { + close(s); + s = -1; + } + if(!child) { + while(child_to_wait_for > 0) { + pid = wait(&status); + if(pid < 0) { + perror("wait"); + } else { + printf("child(%d) terminated with status %d\n", (int)pid, status); + } + --child_to_wait_for; + } + printf("Bye...\n"); + } + return 0; +} + diff --git a/Externals/miniupnpc/src/minisoap.c b/Externals/miniupnpc/src/minisoap.c index f120fe5151..5c6bf01684 100644 --- a/Externals/miniupnpc/src/minisoap.c +++ b/Externals/miniupnpc/src/minisoap.c @@ -1,7 +1,8 @@ -/* $Id: minisoap.c,v 1.23 2014/11/04 22:31:55 nanard Exp $ */ -/* Project : miniupnp +/* $Id: minisoap.c,v 1.32 2023/07/05 22:43:50 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005-2014 Thomas Bernard + * Copyright (c) 2005-2024 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * @@ -12,7 +13,7 @@ #ifdef _WIN32 #include #include -#define snprintf _snprintf +#include "win32_snprintf.h" #else #include #include @@ -24,16 +25,10 @@ /* only for malloc */ #include -#ifdef _WIN32 -#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); -#else -#define PRINT_SOCKET_ERROR(x) perror(x) -#endif - /* httpWrite sends the headers and the body to the socket * and returns the number of bytes sent */ static int -httpWrite(int fd, const char * body, int bodysize, +httpWrite(SOCKET fd, const char * body, int bodysize, const char * headers, int headerssize) { int n = 0; @@ -43,10 +38,10 @@ httpWrite(int fd, const char * body, int bodysize, /* Note : my old linksys router only took into account * soap request that are sent into only one packet */ char * p; - /* TODO: AVOID MALLOC */ + /* TODO: AVOID MALLOC, we could use writev() for that */ p = malloc(headerssize+bodysize); if(!p) - return 0; + return -1; memcpy(p, headers, headerssize); memcpy(p+headerssize, body, bodysize); /*n = write(fd, p, headerssize+bodysize);*/ @@ -55,7 +50,7 @@ httpWrite(int fd, const char * body, int bodysize, PRINT_SOCKET_ERROR("send"); } /* disable send on the socket */ - /* draytek routers dont seems to like that... */ + /* draytek routers don't seem to like that... */ #if 0 #ifdef _WIN32 if(shutdown(fd, SD_SEND)<0) { @@ -70,7 +65,7 @@ httpWrite(int fd, const char * body, int bodysize, } /* self explanatory */ -int soapPostSubmit(int fd, +int soapPostSubmit(SOCKET fd, const char * url, const char * host, unsigned short port, @@ -78,33 +73,37 @@ int soapPostSubmit(int fd, const char * body, const char * httpversion) { - int bodysize; char headerbuf[512]; int headerssize; char portstr[8]; - bodysize = (int)strlen(body); + int bodysize = (int)strlen(body); /* We are not using keep-alive HTTP connections. * HTTP/1.1 needs the header Connection: close to do that. * This is the default with HTTP/1.0 * Using HTTP/1.1 means we need to support chunked transfer-encoding : * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked * transfer encoding. */ - /* Connection: Close is normally there only in HTTP/1.1 but who knows */ + /* Connection: close is normally there only in HTTP/1.1 but who knows */ portstr[0] = '\0'; if(port != 80) snprintf(portstr, sizeof(portstr), ":%hu", port); headerssize = snprintf(headerbuf, sizeof(headerbuf), "POST %s HTTP/%s\r\n" "Host: %s%s\r\n" - "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" + "User-Agent: " OS_STRING " " UPNP_VERSION_STRING " MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" "Content-Length: %d\r\n" +#if (UPNP_VERSION_MAJOR == 1) && (UPNP_VERSION_MINOR == 0) "Content-Type: text/xml\r\n" +#else + "Content-Type: text/xml; charset=\"utf-8\"\r\n" +#endif "SOAPAction: \"%s\"\r\n" - "Connection: Close\r\n" + "Connection: close\r\n" "Cache-Control: no-cache\r\n" /* ??? */ - "Pragma: no-cache\r\n" "\r\n", url, httpversion, host, portstr, bodysize, action); + if ((unsigned int)headerssize >= sizeof(headerbuf)) + return -1; #ifdef DEBUG /*printf("SOAP request : headersize=%d bodysize=%d\n", headerssize, bodysize); diff --git a/Externals/miniupnpc/src/minisoap.h b/Externals/miniupnpc/src/minisoap.h index 60554f5c39..d6a45d03ba 100644 --- a/Externals/miniupnpc/src/minisoap.h +++ b/Externals/miniupnpc/src/minisoap.h @@ -1,14 +1,16 @@ /* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */ /* Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005 Thomas Bernard + * Copyright (c) 2005-2018 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ #ifndef MINISOAP_H_INCLUDED #define MINISOAP_H_INCLUDED +#include "miniupnpc_socketdef.h" + /*int httpWrite(int, const char *, int, const char *);*/ -int soapPostSubmit(int, const char *, const char *, unsigned short, +int soapPostSubmit(SOCKET, const char *, const char *, unsigned short, const char *, const char *, const char *); #endif diff --git a/Externals/miniupnpc/src/minissdpc.c b/Externals/miniupnpc/src/minissdpc.c index 235175c1a3..6c54c34261 100644 --- a/Externals/miniupnpc/src/minissdpc.c +++ b/Externals/miniupnpc/src/minissdpc.c @@ -1,23 +1,26 @@ -/* $Id: minissdpc.c,v 1.28 2015/09/18 13:05:39 nanard Exp $ */ -/* Project : miniupnp - * Web : http://miniupnp.free.fr/ +/* $Id: minissdpc.c,v 1.52 2025/01/12 15:47:17 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas BERNARD - * copyright (c) 2005-2015 Thomas Bernard + * copyright (c) 2005-2025 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */ -/*#include */ #include #include #include +#include #include +#if defined (__NetBSD__) +#include +#endif #if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) #ifdef _WIN32 #include #include #include #include -#include -#define snprintf _snprintf +#include "win32_snprintf.h" #if !defined(_MSC_VER) #include #else /* !defined(_MSC_VER) */ @@ -30,6 +33,15 @@ typedef unsigned short uint16_t; #define strncasecmp memicmp #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */ #endif /* #ifndef strncasecmp */ +#if defined(WINAPI_FAMILY) && defined(WINAPI_FAMILY_PARTITION) +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP +#define in6addr_any in6addr_any_init +static const IN6_ADDR in6addr_any_init = {0}; +#endif +#endif +#if !defined(_WIN32_WINNT_VISTA) +#define _WIN32_WINNT_VISTA 0x0600 +#endif #endif /* _WIN32 */ #if defined(__amigaos__) || defined(__amigaos4__) #include @@ -57,14 +69,17 @@ struct sockaddr_un { #define closesocket close #endif -#ifdef _WIN32 -#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); -#else -#define PRINT_SOCKET_ERROR(x) perror(x) +#include "miniupnpc_socketdef.h" + +#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__) && !defined(__HAIKU__) +#define HAS_IP_MREQN #endif -#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__) -#define HAS_IP_MREQN +#ifndef _WIN32 +#include +#if defined(__sun) || defined(__HAIKU__) +#include +#endif #endif #if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN) @@ -84,6 +99,7 @@ struct ip_mreqn #endif #include "minissdpc.h" +#include "miniupnpc.h" #include "receivedata.h" #if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) @@ -160,7 +176,7 @@ connectToMiniSSDPD(const char * socketpath) { int s; struct sockaddr_un addr; -#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT +#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun) struct timeval timeout; #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ @@ -171,23 +187,25 @@ connectToMiniSSDPD(const char * socketpath) perror("socket(unix)"); return MINISSDPC_SOCKET_ERROR; } -#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT +#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun) /* setting a 3 seconds timeout */ + /* not supported for AF_UNIX sockets under Solaris */ timeout.tv_sec = 3; timeout.tv_usec = 0; if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0) { - perror("setsockopt"); + perror("setsockopt SO_RCVTIMEO unix"); } timeout.tv_sec = 3; timeout.tv_usec = 0; if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0) { - perror("setsockopt"); + perror("setsockopt SO_SNDTIMEO unix"); } #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ if(!socketpath) socketpath = "/var/run/minissdpd.sock"; + memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path)); /* TODO : check if we need to handle the EINTR */ @@ -323,7 +341,7 @@ receiveDevicesFromMiniSSDPD(int s, int * error) #ifdef DEBUG printf(" usnsize=%u\n", usnsize); #endif /* DEBUG */ - tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize); + tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3); if(tmp == NULL) { if (error) *error = MINISSDPC_MEMORY_ERROR; @@ -372,6 +390,7 @@ free_tmp_and_return: * the last 4 arguments are filled during the parsing : * - location/locationsize : "location:" field of the SSDP reply packet * - st/stsize : "st:" field of the SSDP reply packet. + * - usn/usnsize : "usn:" filed of the SSDP reply packet * The strings are NOT null terminated */ static void parseMSEARCHReply(const char * reply, int size, @@ -409,17 +428,17 @@ parseMSEARCHReply(const char * reply, int size, putchar('\n');*/ /* skip the colon and white spaces */ do { b++; } while(reply[b]==' '); - if(0==strncasecmp(reply+a, "location", 8)) + if(0==strncasecmp(reply+a, "location:", 9)) { *location = reply+b; *locationsize = i-b; } - else if(0==strncasecmp(reply+a, "st", 2)) + else if(0==strncasecmp(reply+a, "st:", 3)) { *st = reply+b; *stsize = i-b; } - else if(0==strncasecmp(reply+a, "usn", 3)) + else if(0==strncasecmp(reply+a, "usn:", 4)) { *usn = reply+b; *usnsize = i-b; @@ -435,8 +454,53 @@ parseMSEARCHReply(const char * reply, int size, } } +#if defined(CLOCK_MONOTONIC_FAST) +#define UPNP_CLOCKID CLOCK_MONOTONIC_FAST +#elif defined(CLOCK_MONOTONIC) +#define UPNP_CLOCKID CLOCK_MONOTONIC +#endif + +static int upnp_gettimeofday(struct timeval * tv) +{ +#if defined(_WIN32) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA + ULONGLONG ts = GetTickCount64(); +#else + DWORD ts = GetTickCount(); +#endif + tv->tv_sec = (long)(ts / 1000); + tv->tv_usec = (ts % 1000) * 1000; + return 0; /* success */ +#elif defined(CLOCK_MONOTONIC_FAST) || defined(CLOCK_MONOTONIC) +#if defined(__APPLE__) +#if defined(__clang__) + if (__builtin_available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) { +#else /* !defined(__clang__) */ + if (clock_gettime != NULL) { +#endif /* defined(__clang__) */ +#endif /* defined(__APPLE__) */ + struct timespec ts; + int ret_code = clock_gettime(UPNP_CLOCKID, &ts); + if (ret_code == 0) + { + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / 1000; + } + return ret_code; +#if defined(__APPLE__) + } + else + { + /* fall-back for earlier Apple platforms */ + return gettimeofday(tv, NULL); + } +#endif /* defined(__APPLE__) */ +#else + return gettimeofday(tv, NULL); +#endif +} /* port upnp discover : SSDP protocol */ -#define PORT 1900 +#define SSDP_PORT 1900 #define XSTR(s) STR(s) #define STR(s) #s #define UPNP_MCAST_ADDR "239.255.255.250" @@ -456,25 +520,25 @@ parseMSEARCHReply(const char * reply, int size, struct UPNPDev * ssdpDiscoverDevices(const char * const deviceTypes[], int delay, const char * multicastif, - int sameport, + int localport, int ipv6, unsigned char ttl, int * error, int searchalltypes) { struct UPNPDev * tmp; - struct UPNPDev * devlist = 0; + struct UPNPDev * devlist = NULL; unsigned int scope_id = 0; int opt = 1; static const char MSearchMsgFmt[] = "M-SEARCH * HTTP/1.1\r\n" - "HOST: %s:" XSTR(PORT) "\r\n" + "HOST: %s:" XSTR(SSDP_PORT) "\r\n" "ST: %s\r\n" "MAN: \"ssdp:discover\"\r\n" "MX: %u\r\n" "\r\n"; int deviceIndex; char bufr[1536]; /* reception and emission buffer */ - int sudp; + SOCKET sudp; int n; struct sockaddr_storage sockudp_r; unsigned int mx; @@ -482,23 +546,26 @@ ssdpDiscoverDevices(const char * const deviceTypes[], struct sockaddr_storage sockudp_w; #else int rv; - struct addrinfo hints, *servinfo, *p; + struct addrinfo hints, *servinfo; #endif #ifdef _WIN32 - MIB_IPFORWARDROW ip_forward; unsigned long _ttl = (unsigned long)ttl; #endif - int linklocal = 1; + int linklocal = 0; /* try first with site-local multicast */ + int sentok; if(error) *error = MINISSDPC_UNKNOWN_ERROR; + if(localport==UPNP_LOCAL_PORT_SAME) + localport = SSDP_PORT; + #ifdef _WIN32 sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP); #else sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0); #endif - if(sudp < 0) + if(ISINVALID(sudp)) { if(error) *error = MINISSDPC_SOCKET_ERROR; @@ -510,14 +577,14 @@ ssdpDiscoverDevices(const char * const deviceTypes[], if(ipv6) { struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r; p->sin6_family = AF_INET6; - if(sameport) - p->sin6_port = htons(PORT); + if(localport > 0 && localport < 65536) + p->sin6_port = htons((unsigned short)localport); p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */ } else { struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r; p->sin_family = AF_INET; - if(sameport) - p->sin_port = htons(PORT); + if(localport > 0 && localport < 65536) + p->sin_port = htons((unsigned short)localport); p->sin_addr.s_addr = INADDR_ANY; } #ifdef _WIN32 @@ -525,61 +592,98 @@ ssdpDiscoverDevices(const char * const deviceTypes[], * SSDP multicast traffic */ /* Get IP associated with the index given in the ip_forward struct * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */ - if(!ipv6 - && (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) { - DWORD dwRetVal = 0; - PMIB_IPADDRTABLE pIPAddrTable; - DWORD dwSize = 0; -#ifdef DEBUG - IN_ADDR IPAddr; + if(!ipv6) { + DWORD ifbestidx; +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA + // While we don't need IPv6 support, the IPv4 only function is not available in UWP apps. + SOCKADDR_IN destAddr; + memset(&destAddr, 0, sizeof(destAddr)); + destAddr.sin_family = AF_INET; + destAddr.sin_addr.s_addr = inet_addr("223.255.255.255"); + destAddr.sin_port = 0; + if (GetBestInterfaceEx((struct sockaddr *)&destAddr, &ifbestidx) == NO_ERROR) { +#else + if (GetBestInterface(inet_addr("223.255.255.255"), &ifbestidx) == NO_ERROR) { #endif - int i; -#ifdef DEBUG - printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop); -#endif - pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE)); - if(pIPAddrTable) { - if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { - free(pIPAddrTable); - pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize); + DWORD dwRetVal = NO_ERROR; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + ULONG outBufLen = 15360; + int Iterations; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL; + + for (Iterations = 0; Iterations < 3; Iterations++) { + pAddresses = (IP_ADAPTER_ADDRESSES *) HeapAlloc(GetProcessHeap(), 0, outBufLen); + if (pAddresses == NULL) { + break; + } + + dwRetVal = GetAdaptersAddresses(AF_INET, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &outBufLen); + + if (dwRetVal != ERROR_BUFFER_OVERFLOW) { + break; + } + HeapFree(GetProcessHeap(), 0, pAddresses); + pAddresses = NULL; } - } - if(pIPAddrTable) { - dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); + if (dwRetVal == NO_ERROR) { + pCurrAddresses = pAddresses; + while (pCurrAddresses) { #ifdef DEBUG - printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries); -#endif - for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) { -#ifdef DEBUG - printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr; - printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask; - printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); - IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr; - printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr); - printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize); - printf("\tType and State[%d]:", i); + int i; + PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL; + PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL; + + printf("\tIfIndex (IPv4 interface): %u\n", pCurrAddresses->IfIndex); + printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName); + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pUnicast != NULL) { + for (i = 0; pUnicast != NULL; i++) { + printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pUnicast->Address.lpSockaddr)->sin_addr) ); + pUnicast = pUnicast->Next; + } + printf("\tNumber of Unicast Addresses: %d\n", i); + } + pAnycast = pCurrAddresses->FirstAnycastAddress; + if (pAnycast) { + for (i = 0; pAnycast != NULL; i++) { + printf("\tAnycast Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pAnycast->Address.lpSockaddr)->sin_addr) ); + pAnycast = pAnycast->Next; + } + printf("\tNumber of Anycast Addresses: %d\n", i); + } + pMulticast = pCurrAddresses->FirstMulticastAddress; + if (pMulticast) { + for (i = 0; pMulticast != NULL; i++) { + printf("\tMulticast Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pMulticast->Address.lpSockaddr)->sin_addr) ); + pMulticast = pMulticast->Next; + } + } printf("\n"); #endif - if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) { + pUnicast = pCurrAddresses->FirstUnicastAddress; + if (pCurrAddresses->IfIndex == ifbestidx && pUnicast != NULL) { + SOCKADDR_IN *ipv4 = (SOCKADDR_IN *)(pUnicast->Address.lpSockaddr); /* Set the address of this interface to be used */ struct in_addr mc_if; memset(&mc_if, 0, sizeof(mc_if)); - mc_if.s_addr = pIPAddrTable->table[i].dwAddr; + mc_if.s_addr = ipv4->sin_addr.s_addr; if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) { PRINT_SOCKET_ERROR("setsockopt"); } - ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr; + ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = ipv4->sin_addr.s_addr; #ifndef DEBUG break; #endif } + pCurrAddresses = pCurrAddresses->Next; } } - free(pIPAddrTable); - pIPAddrTable = NULL; + if (pAddresses != NULL) { + HeapFree(GetProcessHeap(), 0, pAddresses); + pAddresses = NULL; + } } } #endif /* _WIN32 */ @@ -593,20 +697,33 @@ ssdpDiscoverDevices(const char * const deviceTypes[], if(error) *error = MINISSDPC_SOCKET_ERROR; PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)"); - return NULL; + goto error; } + if(ipv6) { #ifdef _WIN32 - if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0) + DWORD mcastHops = ttl; + if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0) #else /* _WIN32 */ - if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) + int mcastHops = ttl; + if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0) #endif /* _WIN32 */ - { - /* not a fatal error */ - PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)"); + { + PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)"); + } + } else { +#ifdef _WIN32 + if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0) +#else /* _WIN32 */ + if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0) +#endif /* _WIN32 */ + { + /* not a fatal error */ + PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)"); + } } - if(multicastif) + if(multicastif && multicastif[0] != '\0') { if(ipv6) { #if !defined(_WIN32) @@ -614,9 +731,16 @@ ssdpDiscoverDevices(const char * const deviceTypes[], * MS Windows Vista and MS Windows Server 2008. * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */ unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */ + if(ifindex == 0) + { + if(error) + *error = MINISSDPC_INVALID_INPUT; + fprintf(stderr, "Invalid multicast interface name %s\n", multicastif); + goto error; + } if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0) { - PRINT_SOCKET_ERROR("setsockopt"); + PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF"); } #else #ifdef DEBUG @@ -625,29 +749,67 @@ ssdpDiscoverDevices(const char * const deviceTypes[], #endif } else { struct in_addr mc_if; - mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */ +#if defined(_WIN32) +#if _WIN32_WINNT >= _WIN32_WINNT_VISTA + InetPtonA(AF_INET, multicastif, &mc_if); +#else + mc_if.s_addr = inet_addr(multicastif); /* old Windows SDK do not support InetPtoA() */ +#endif +#else + /* was : mc_if.s_addr = inet_addr(multicastif); */ /* ex: 192.168.x.x */ + if (inet_pton(AF_INET, multicastif, &mc_if.s_addr) <= 0) { + mc_if.s_addr = INADDR_NONE; + } +#endif if(mc_if.s_addr != INADDR_NONE) { ((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr; if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) { - PRINT_SOCKET_ERROR("setsockopt"); + PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF"); } } else { -#ifdef HAS_IP_MREQN /* was not an ip address, try with an interface name */ +#ifndef _WIN32 +#ifdef HAS_IP_MREQN struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */ +#endif + struct ifreq ifr; + int ifrlen = sizeof(ifr); + strncpy(ifr.ifr_name, multicastif, IFNAMSIZ); + ifr.ifr_name[IFNAMSIZ-1] = '\0'; + if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0) + { + PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)"); + goto error; + } + mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr; +#ifdef HAS_IP_MREQN memset(&reqn, 0, sizeof(struct ip_mreqn)); + reqn.imr_address.s_addr = mc_if.s_addr; reqn.imr_ifindex = if_nametoindex(multicastif); + if(reqn.imr_ifindex == 0) + { + if(error) + *error = MINISSDPC_INVALID_INPUT; + fprintf(stderr, "Invalid multicast ip address / interface name %s\n", multicastif); + goto error; + } if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0) { - PRINT_SOCKET_ERROR("setsockopt"); + PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF"); } #else + if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) + { + PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF"); + } +#endif +#else /* _WIN32 */ #ifdef DEBUG printf("Setting of multicast interface not supported with interface name.\n"); #endif -#endif +#endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */ } } } @@ -674,6 +836,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[], } /* receiving SSDP response packet */ for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) { + sentok = 0; /* sending the SSDP M-SEARCH packet */ n = snprintf(bufr, sizeof(bufr), MSearchMsgFmt, @@ -681,6 +844,11 @@ ssdpDiscoverDevices(const char * const deviceTypes[], (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]") : UPNP_MCAST_ADDR, deviceTypes[deviceIndex], mx); + if ((unsigned int)n >= sizeof(bufr)) { + if(error) + *error = MINISSDPC_MEMORY_ERROR; + goto error; + } #ifdef DEBUG /*printf("Sending %s", bufr);*/ printf("Sending M-SEARCH request to %s with ST: %s\n", @@ -696,14 +864,14 @@ ssdpDiscoverDevices(const char * const deviceTypes[], if(ipv6) { struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w; p->sin6_family = AF_INET6; - p->sin6_port = htons(PORT); + p->sin6_port = htons(SSDP_PORT); inet_pton(AF_INET6, linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR, &(p->sin6_addr)); } else { struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w; p->sin_family = AF_INET; - p->sin_port = htons(PORT); + p->sin_port = htons(SSDP_PORT); p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR); } n = sendto(sudp, bufr, n, 0, &sockudp_w, @@ -712,7 +880,8 @@ ssdpDiscoverDevices(const char * const deviceTypes[], if(error) *error = MINISSDPC_SOCKET_ERROR; PRINT_SOCKET_ERROR("sendto"); - break; + } else { + sentok = 1; } #else /* #ifdef NO_GETADDRINFO */ memset(&hints, 0, sizeof(hints)); @@ -722,7 +891,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[], if ((rv = getaddrinfo(ipv6 ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR) : UPNP_MCAST_ADDR, - XSTR(PORT), &hints, &servinfo)) != 0) { + XSTR(SSDP_PORT), &hints, &servinfo)) != 0) { if(error) *error = MINISSDPC_SOCKET_ERROR; #ifdef _WIN32 @@ -731,105 +900,120 @@ ssdpDiscoverDevices(const char * const deviceTypes[], fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); #endif break; - } - for(p = servinfo; p; p = p->ai_next) { - n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen); - if (n < 0) { + } else { + struct addrinfo *p; + for(p = servinfo; p; p = p->ai_next) { + n = sendto(sudp, bufr, n, 0, p->ai_addr, MSC_CAST_INT p->ai_addrlen); + if (n < 0) { #ifdef DEBUG - char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; - if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf, - sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { - fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf); - } + char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; + if (getnameinfo(p->ai_addr, (socklen_t)p->ai_addrlen, hbuf, sizeof(hbuf), sbuf, + sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { + fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf); + } #endif - PRINT_SOCKET_ERROR("sendto"); - continue; + PRINT_SOCKET_ERROR("sendto"); + continue; + } else { + sentok = 1; + } } + freeaddrinfo(servinfo); } - freeaddrinfo(servinfo); - if(n < 0) { + if(!sentok) { if(error) *error = MINISSDPC_SOCKET_ERROR; - break; } #endif /* #ifdef NO_GETADDRINFO */ /* Waiting for SSDP REPLY packet to M-SEARCH * if searchalltypes is set, enter the loop only * when the last deviceType is reached */ - if(!searchalltypes || !deviceTypes[deviceIndex + 1]) do { - n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id); - if (n < 0) { - /* error */ - if(error) - *error = MINISSDPC_SOCKET_ERROR; - goto error; - } else if (n == 0) { - /* no data or Time Out */ -#ifdef DEBUG - printf("NODATA or TIMEOUT\n"); -#endif /* DEBUG */ - if (devlist && !searchalltypes) { - /* found some devices, stop now*/ + if((sentok && !searchalltypes) || !deviceTypes[deviceIndex + 1]) { + struct timeval start = {0, 0}, current = {0, 0}; + upnp_gettimeofday(&start); + do { + n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id); + if (n < 0) { + /* error */ if(error) - *error = MINISSDPC_SUCCESS; + *error = MINISSDPC_SOCKET_ERROR; goto error; - } - } else { - const char * descURL=NULL; - int urlsize=0; - const char * st=NULL; - int stsize=0; - const char * usn=NULL; - int usnsize=0; - parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize); - if(st&&descURL) { + } else if (n == 0) { + /* no data or Time Out */ #ifdef DEBUG - printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n", - stsize, st, usnsize, (usn?usn:""), urlsize, descURL); + printf("NODATA or TIMEOUT\n"); #endif /* DEBUG */ - for(tmp=devlist; tmp; tmp = tmp->pNext) { - if(memcmp(tmp->descURL, descURL, urlsize) == 0 && - tmp->descURL[urlsize] == '\0' && - memcmp(tmp->st, st, stsize) == 0 && - tmp->st[stsize] == '\0' && - (usnsize == 0 || memcmp(tmp->usn, usn, usnsize) == 0) && - tmp->usn[usnsize] == '\0') - break; - } - /* at the exit of the loop above, tmp is null if - * no duplicate device was found */ - if(tmp) - continue; - tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize); - if(!tmp) { - /* memory allocation error */ + if (devlist && !searchalltypes) { + /* found some devices, stop now*/ if(error) - *error = MINISSDPC_MEMORY_ERROR; + *error = MINISSDPC_SUCCESS; goto error; } - tmp->pNext = devlist; - tmp->descURL = tmp->buffer; - tmp->st = tmp->buffer + 1 + urlsize; - tmp->usn = tmp->st + 1 + stsize; - memcpy(tmp->buffer, descURL, urlsize); - tmp->buffer[urlsize] = '\0'; - memcpy(tmp->st, st, stsize); - tmp->buffer[urlsize+1+stsize] = '\0'; - if(usn != NULL) - memcpy(tmp->usn, usn, usnsize); - tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0'; - tmp->scope_id = scope_id; - devlist = tmp; + } else { + const char * descURL=NULL; + int urlsize=0; + const char * st=NULL; + int stsize=0; + const char * usn=NULL; + int usnsize=0; + parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize); + if(st&&descURL) { +#ifdef DEBUG + printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n", + stsize, st, usnsize, (usn?usn:""), urlsize, descURL); +#endif /* DEBUG */ + for(tmp=devlist; tmp; tmp = tmp->pNext) { + if(strncmp(tmp->descURL, descURL, urlsize) == 0 && + tmp->descURL[urlsize] == '\0' && + strncmp(tmp->st, st, stsize) == 0 && + tmp->st[stsize] == '\0' && + (usnsize == 0 || strncmp(tmp->usn, usn, usnsize) == 0) && + tmp->usn[usnsize] == '\0') + break; + } + /* at the exit of the loop above, tmp is null if + * no duplicate device was found */ + if(tmp) + continue; + tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3); + if(!tmp) { + /* memory allocation error */ + if(error) + *error = MINISSDPC_MEMORY_ERROR; + goto error; + } + tmp->pNext = devlist; + tmp->descURL = tmp->buffer; + tmp->st = tmp->buffer + 1 + urlsize; + tmp->usn = tmp->st + 1 + stsize; + memcpy(tmp->buffer, descURL, urlsize); + tmp->buffer[urlsize] = '\0'; + memcpy(tmp->st, st, stsize); + tmp->buffer[urlsize+1+stsize] = '\0'; + if(usn != NULL) + memcpy(tmp->usn, usn, usnsize); + tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0'; + tmp->scope_id = scope_id; + devlist = tmp; + } + if (upnp_gettimeofday(¤t) >= 0) { + /* exit the loop if delay is reached */ + long interval = (current.tv_sec - start.tv_sec) * 1000; + interval += (current.tv_usec - start.tv_usec) / 1000; + if (interval > (long)delay) + break; + } } - } - } while(n > 0); + } while(n > 0); + } if(ipv6) { /* switch linklocal flag */ if(linklocal) { linklocal = 0; - --deviceIndex; } else { + /* try again with linklocal multicast */ linklocal = 1; + --deviceIndex; } } } @@ -837,4 +1021,3 @@ error: closesocket(sudp); return devlist; } - diff --git a/Externals/miniupnpc/src/minissdpc.h b/Externals/miniupnpc/src/minissdpc.h index ab190fcb0c..c99f929b9e 100644 --- a/Externals/miniupnpc/src/minissdpc.h +++ b/Externals/miniupnpc/src/minissdpc.h @@ -32,20 +32,20 @@ MINIUPNP_LIBSPEC int connectToMiniSSDPD(const char * socketpath); MINIUPNP_LIBSPEC int -disconnectFromMiniSSDPD(int fd); +disconnectFromMiniSSDPD(int s); MINIUPNP_LIBSPEC int -requestDevicesFromMiniSSDPD(int fd, const char * devtype); +requestDevicesFromMiniSSDPD(int s, const char * devtype); MINIUPNP_LIBSPEC struct UPNPDev * -receiveDevicesFromMiniSSDPD(int fd, int * error); +receiveDevicesFromMiniSSDPD(int s, int * error); #endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */ MINIUPNP_LIBSPEC struct UPNPDev * ssdpDiscoverDevices(const char * const deviceTypes[], int delay, const char * multicastif, - int sameport, + int localport, int ipv6, unsigned char ttl, int * error, int searchalltypes); diff --git a/Externals/miniupnpc/src/miniupnpc.c b/Externals/miniupnpc/src/miniupnpc.c index 8cb1b8fcc5..ef75dbfe72 100644 --- a/Externals/miniupnpc/src/miniupnpc.c +++ b/Externals/miniupnpc/src/miniupnpc.c @@ -1,8 +1,9 @@ -/* $Id: miniupnpc.c,v 1.135 2015/07/23 20:40:08 nanard Exp $ */ -/* Project : miniupnp - * Web : http://miniupnp.free.fr/ +/* $Id: miniupnpc.c,v 1.165 2025/01/10 22:57:21 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas BERNARD - * copyright (c) 2005-2015 Thomas Bernard + * copyright (c) 2005-2025 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENSE file. */ #include @@ -14,7 +15,7 @@ #include #include #include -#define snprintf _snprintf +#include "win32_snprintf.h" #define strdup _strdup #ifndef strncasecmp #if defined(_MSC_VER) && (_MSC_VER >= 1400) @@ -55,13 +56,15 @@ #include "miniupnpc.h" #include "minissdpc.h" #include "miniwget.h" +#include "miniwget_private.h" #include "minisoap.h" #include "minixml.h" #include "upnpcommands.h" #include "connecthostport.h" +#include "addr_is_reserved.h" -/* compare the begining of a string with a constant string */ -#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1)) +/* compare the beginning of a string with a constant string */ +#define COMPARE(str, cstr) (0==strncmp(str, cstr, sizeof(cstr) - 1)) #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 64 @@ -89,98 +92,119 @@ MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGD #endif } -/* simpleUPnPcommand2 : +/* simpleUPnPcommand : * not so simple ! * return values : * pointer - OK * NULL - error */ -char * simpleUPnPcommand2(int s, const char * url, const char * service, - const char * action, struct UPNParg * args, - int * bufsize, const char * httpversion) +char * +simpleUPnPcommand(const char * url, const char * service, + const char * action, const struct UPNParg * args, + int * bufsize) { char hostname[MAXHOSTNAMELEN+1]; unsigned short port = 0; char * path; char soapact[128]; char soapbody[2048]; + int soapbodylen; char * buf; - int n; + int n; + int status_code; + SOCKET s; *bufsize = 0; snprintf(soapact, sizeof(soapact), "%s#%s", service, action); if(args==NULL) { - /*soapbodylen = */snprintf(soapbody, sizeof(soapbody), - "\r\n" - "<" SOAPPREFIX ":Envelope " + soapbodylen = snprintf(soapbody, sizeof(soapbody), + "\r\n" + "<" SOAPPREFIX ":Envelope " "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" " SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" "<" SOAPPREFIX ":Body>" "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">" "" "" - "\r\n", action, service, action); + "\r\n", action, service, action); + if ((unsigned int)soapbodylen >= sizeof(soapbody)) + return NULL; } else { char * p; const char * pe, * pv; - int soapbodylen; + const char * const pend = soapbody + sizeof(soapbody); soapbodylen = snprintf(soapbody, sizeof(soapbody), "\r\n" - "<" SOAPPREFIX ":Envelope " + "<" SOAPPREFIX ":Envelope " "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" " SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" "<" SOAPPREFIX ":Body>" "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">", action, service); + if ((unsigned int)soapbodylen >= sizeof(soapbody)) + return NULL; p = soapbody + soapbodylen; while(args->elt) { - /* check that we are never overflowing the string... */ - if(soapbody + sizeof(soapbody) <= p + 100) - { - /* we keep a margin of at least 100 bytes */ + if(p >= pend) /* check for space to write next byte */ return NULL; - } *(p++) = '<'; + pe = args->elt; - while(*pe) + while(p < pend && *pe) *(p++) = *(pe++); + + if(p >= pend) /* check for space to write next byte */ + return NULL; *(p++) = '>'; + if((pv = args->val)) { - while(*pv) + while(p < pend && *pv) *(p++) = *(pv++); } + + if((p+2) > pend) /* check for space to write next 2 bytes */ + return NULL; *(p++) = '<'; *(p++) = '/'; + pe = args->elt; - while(*pe) + while(p < pend && *pe) *(p++) = *(pe++); + + if(p >= pend) /* check for space to write next byte */ + return NULL; *(p++) = '>'; + args++; } + if((p+4) > pend) /* check for space to write next 4 bytes */ + return NULL; *(p++) = '<'; *(p++) = '/'; *(p++) = SERVICEPREFIX2; *(p++) = ':'; + pe = action; - while(*pe) + while(p < pend && *pe) *(p++) = *(pe++); + strncpy(p, ">\r\n", - soapbody + sizeof(soapbody) - p); + pend - p); + if(soapbody[sizeof(soapbody)-1]) /* strncpy pads buffer with 0s, so if it doesn't end in 0, could not fit full string */ + return NULL; } if(!parseURL(url, hostname, &port, &path, NULL)) return NULL; - if(s < 0) { - s = connecthostport(hostname, port, 0); - if(s < 0) { - /* failed to connect */ - return NULL; - } + s = connecthostport(hostname, port, 0); + if(ISINVALID(s)) { + /* failed to connect */ + return NULL; } - n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion); + n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, "1.1"); if(n<=0) { #ifdef DEBUG printf("Error sending SOAP request\n"); @@ -189,43 +213,21 @@ char * simpleUPnPcommand2(int s, const char * url, const char * service, return NULL; } - buf = getHTTPResponse(s, bufsize); + buf = getHTTPResponse(s, bufsize, &status_code); #ifdef DEBUG if(*bufsize > 0 && buf) { - printf("SOAP Response :\n%.*s\n", *bufsize, buf); + printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf); + } + else + { + printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize); } #endif closesocket(s); return buf; } -/* simpleUPnPcommand : - * not so simple ! - * return values : - * pointer - OK - * NULL - error */ -char * simpleUPnPcommand(int s, const char * url, const char * service, - const char * action, struct UPNParg * args, - int * bufsize) -{ - char * buf; - -#if 1 - buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1"); -#else - buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0"); - if (!buf || *bufsize == 0) - { -#if DEBUG - printf("Error or no result from SOAP request; retrying with HTTP/1.1\n"); -#endif - buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1"); - } -#endif - return buf; -} - /* upnpDiscoverDevices() : * return a chained list of all devices found or NULL if * no devices was found. @@ -237,7 +239,7 @@ char * simpleUPnPcommand(int s, const char * url, const char * service, MINIUPNP_LIBSPEC struct UPNPDev * upnpDiscoverDevices(const char * const deviceTypes[], int delay, const char * multicastif, - const char * minissdpdsock, int sameport, + const char * minissdpdsock, int localport, int ipv6, unsigned char ttl, int * error, int searchalltypes) @@ -254,30 +256,32 @@ upnpDiscoverDevices(const char * const deviceTypes[], /* first try to get infos from minissdpd ! */ if(!minissdpdsock) minissdpdsock = "/var/run/minissdpd.sock"; - for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) { - struct UPNPDev * minissdpd_devlist; - int only_rootdevice = 1; - minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex], - minissdpdsock, 0); - if(minissdpd_devlist) { -#ifdef DEBUG - printf("returned by MiniSSDPD: %s\t%s\n", - minissdpd_devlist->st, minissdpd_devlist->descURL); -#endif /* DEBUG */ - if(!strstr(minissdpd_devlist->st, "rootdevice")) - only_rootdevice = 0; - for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) { + if(minissdpdsock[0] != '\0') { + for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) { + struct UPNPDev * minissdpd_devlist; + int only_rootdevice = 1; + minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex], + minissdpdsock, 0); + if(minissdpd_devlist) { #ifdef DEBUG printf("returned by MiniSSDPD: %s\t%s\n", - tmp->pNext->st, tmp->pNext->descURL); + minissdpd_devlist->st, minissdpd_devlist->descURL); #endif /* DEBUG */ - if(!strstr(tmp->st, "rootdevice")) + if(!strstr(minissdpd_devlist->st, "rootdevice")) only_rootdevice = 0; + for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) { +#ifdef DEBUG + printf("returned by MiniSSDPD: %s\t%s\n", + tmp->pNext->st, tmp->pNext->descURL); +#endif /* DEBUG */ + if(!strstr(tmp->st, "rootdevice")) + only_rootdevice = 0; + } + tmp->pNext = devlist; + devlist = minissdpd_devlist; + if(!searchalltypes && !only_rootdevice) + break; } - tmp->pNext = devlist; - devlist = minissdpd_devlist; - if(!searchalltypes && !only_rootdevice) - break; } } for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) { @@ -288,12 +292,14 @@ upnpDiscoverDevices(const char * const deviceTypes[], return devlist; } } +#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ + (void)minissdpdsock; /* unused */ #endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ /* direct discovery if minissdpd responses are not sufficient */ { struct UPNPDev * discovered_devlist; - discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, sameport, + discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport, ipv6, ttl, error, searchalltypes); if(devlist == NULL) devlist = discovered_devlist; @@ -308,7 +314,7 @@ upnpDiscoverDevices(const char * const deviceTypes[], /* upnpDiscover() Discover IGD device */ MINIUPNP_LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif, - const char * minissdpdsock, int sameport, + const char * minissdpdsock, int localport, int ipv6, unsigned char ttl, int * error) { @@ -325,14 +331,14 @@ upnpDiscover(int delay, const char * multicastif, 0 }; return upnpDiscoverDevices(deviceList, - delay, multicastif, minissdpdsock, sameport, + delay, multicastif, minissdpdsock, localport, ipv6, ttl, error, 0); } /* upnpDiscoverAll() Discover all UPnP devices */ MINIUPNP_LIBSPEC struct UPNPDev * upnpDiscoverAll(int delay, const char * multicastif, - const char * minissdpdsock, int sameport, + const char * minissdpdsock, int localport, int ipv6, unsigned char ttl, int * error) { @@ -342,14 +348,14 @@ upnpDiscoverAll(int delay, const char * multicastif, 0 }; return upnpDiscoverDevices(deviceList, - delay, multicastif, minissdpdsock, sameport, + delay, multicastif, minissdpdsock, localport, ipv6, ttl, error, 0); } /* upnpDiscoverDevice() Discover a specific device */ MINIUPNP_LIBSPEC struct UPNPDev * upnpDiscoverDevice(const char * device, int delay, const char * multicastif, - const char * minissdpdsock, int sameport, + const char * minissdpdsock, int localport, int ipv6, unsigned char ttl, int * error) { @@ -358,7 +364,7 @@ upnpDiscoverDevice(const char * device, int delay, const char * multicastif, 0 }; return upnpDiscoverDevices(deviceList, - delay, multicastif, minissdpdsock, sameport, + delay, multicastif, minissdpdsock, localport, ipv6, ttl, error, 0); } @@ -366,7 +372,7 @@ static char * build_absolute_url(const char * baseurl, const char * descURL, const char * url, unsigned int scope_id) { - int l, n; + size_t l, n; char * s; const char * base; char * p; @@ -409,7 +415,7 @@ build_absolute_url(const char * baseurl, const char * descURL, memcpy(s, base, n); if(scope_id != 0) { s[n] = '\0'; - if(0 == memcmp(s, "http://[fe80:", 13)) { + if(n > 13 && 0 == memcmp(s, "http://[fe80:", 13)) { /* this is a linklocal IPv6 address */ p = strchr(s, ']'); if(p) { @@ -498,23 +504,29 @@ UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data) /* UPNP_GetValidIGD() : * return values : * -1 = Internal error - * 0 = NO IGD found - * 1 = A valid connected IGD has been found - * 2 = A valid IGD has been found but it reported as - * not connected - * 3 = an UPnP device has been found but was not recognized as an IGD + * 0 = NO IGD found (UPNP_NO_IGD) + * 1 = A valid connected IGD has been found (UPNP_CONNECTED_IGD) + * 2 = A valid connected IGD has been found but its + * IP address is reserved (non routable) (UPNP_PRIVATEIP_IGD) + * 3 = A valid IGD has been found but it reported as + * not connected (UPNP_DISCONNECTED_IGD) + * 4 = an UPnP device has been found but was not recognized as an IGD + * (UPNP_UNKNOWN_DEVICE) * * In any positive non zero return case, the urls and data structures - * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to + * passed as parameters are set. Don't forget to call FreeUPNPUrls(urls) to * free allocated memory. */ MINIUPNP_LIBSPEC int UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPUrls * urls, - struct IGDdatas * data, - char * lanaddr, int lanaddrlen) + struct IGDdatas * data, + char * lanaddr, int lanaddrlen, + char * wanaddr, int wanaddrlen) { struct xml_desc { + char lanaddr[40]; + char wanaddr[40]; char * xml; int size; int is_igd; @@ -522,9 +534,10 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPDev * dev; int ndev = 0; int i; - int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */ - int n_igd = 0; - char extIpAddr[16]; + int state = -1; /* state 1 : IGD connected. State 2 : connected with reserved IP. + * State 3 : IGD. State 4 : anything */ + int status_code = -1; + if(!devlist) { #ifdef DEBUG @@ -535,20 +548,18 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, /* counting total number of devices in the list */ for(dev = devlist; dev; dev = dev->pNext) ndev++; - if(ndev > 0) - { - desc = calloc(ndev, sizeof(struct xml_desc)); - if(!desc) - return -1; /* memory allocation error */ - } + /* ndev is always > 0 */ + desc = calloc(ndev, sizeof(struct xml_desc)); + if(!desc) + return -1; /* memory allocation error */ /* Step 1 : downloading descriptions and testing type */ for(dev = devlist, i = 0; dev; dev = dev->pNext, i++) { /* we should choose an internet gateway device. * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */ desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size), - lanaddr, lanaddrlen, - dev->scope_id); + desc[i].lanaddr, sizeof(desc[i].lanaddr), + dev->scope_id, &status_code); #ifdef DEBUG if(!desc[i].xml) { @@ -564,12 +575,11 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) { desc[i].is_igd = 1; - n_igd++; } } } /* iterate the list to find a device depending on state */ - for(state = 1; state <= 3; state++) + for(state = 1; state <= 4; state++) { for(dev = devlist, i = 0; dev; dev = dev->pNext, i++) { @@ -578,22 +588,28 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, memset(data, 0, sizeof(struct IGDdatas)); memset(urls, 0, sizeof(struct UPNPUrls)); parserootdesc(desc[i].xml, desc[i].size, data); - if(desc[i].is_igd || state >= 3 ) + if(desc[i].is_igd || state >= 4 ) { + int is_connected; + GetUPNPUrls(urls, data, dev->descURL, dev->scope_id); - /* in state 2 and 3 we dont test if device is connected ! */ - if(state >= 2) + /* in state 3 and 4 we don't test if device is connected ! */ + if(state >= 3) goto free_and_return; + is_connected = UPNPIGD_IsConnected(urls, data); #ifdef DEBUG printf("UPNPIGD_IsConnected(%s) = %d\n", - urls->controlURL, - UPNPIGD_IsConnected(urls, data)); + urls->controlURL, is_connected); #endif /* checks that status is connected AND there is a external IP address assigned */ - if(UPNPIGD_IsConnected(urls, data) - && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) - goto free_and_return; + if(is_connected) { + if(state >= 2) + goto free_and_return; + if(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, desc[i].wanaddr) == 0 + && !addr_is_reserved(desc[i].wanaddr)) + goto free_and_return; + } FreeUPNPUrls(urls); if(data->second.servicetype[0] != '\0') { #ifdef DEBUG @@ -605,14 +621,18 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service)); memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service)); GetUPNPUrls(urls, data, dev->descURL, dev->scope_id); + is_connected = UPNPIGD_IsConnected(urls, data); #ifdef DEBUG printf("UPNPIGD_IsConnected(%s) = %d\n", - urls->controlURL, - UPNPIGD_IsConnected(urls, data)); + urls->controlURL, is_connected); #endif - if(UPNPIGD_IsConnected(urls, data) - && (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) - goto free_and_return; + if(is_connected) { + if(state >= 2) + goto free_and_return; + if(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, desc[i].wanaddr) == 0 + && !addr_is_reserved(desc[i].wanaddr)) + goto free_and_return; + } FreeUPNPUrls(urls); } } @@ -622,14 +642,15 @@ UPNP_GetValidIGD(struct UPNPDev * devlist, } state = 0; free_and_return: - if(desc) { - for(i = 0; i < ndev; i++) { - if(desc[i].xml) { - free(desc[i].xml); - } - } - free(desc); + if (state >= 1 && state <= 4 && i < ndev) { + if (lanaddr != NULL) + strncpy(lanaddr, desc[i].lanaddr, lanaddrlen); + if (wanaddr != NULL) + strncpy(wanaddr, desc[i].wanaddr, wanaddrlen); } + for(i = 0; i < ndev; i++) + free(desc[i].xml); + free(desc); return state; } @@ -646,18 +667,17 @@ UPNP_GetIGDFromUrl(const char * rootdescurl, { char * descXML; int descXMLsize = 0; + descXML = miniwget_getaddr(rootdescurl, &descXMLsize, - lanaddr, lanaddrlen, 0); + lanaddr, lanaddrlen, 0, NULL); if(descXML) { memset(data, 0, sizeof(struct IGDdatas)); memset(urls, 0, sizeof(struct UPNPUrls)); parserootdesc(descXML, descXMLsize, data); free(descXML); - descXML = NULL; GetUPNPUrls(urls, data, rootdescurl, 0); return 1; } else { return 0; } } - diff --git a/Externals/miniupnpc/src/miniupnpc.h b/Externals/miniupnpc/src/miniupnpc.h index 7cf7191f81..b6ee168f3e 100644 --- a/Externals/miniupnpc/src/miniupnpc.h +++ b/Externals/miniupnpc/src/miniupnpc.h @@ -1,139 +1,298 @@ -/* $Id: miniupnpc.h,v 1.44 2015/07/23 20:40:10 nanard Exp $ */ -/* Project: miniupnp - * http://miniupnp.free.fr/ +/* $Id: miniupnpc.h,v 1.76 2025/03/05 10:35:57 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project: miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author: Thomas Bernard - * Copyright (c) 2005-2015 Thomas Bernard + * Copyright (c) 2005-2025 Thomas Bernard * This software is subjects to the conditions detailed * in the LICENCE file provided within this distribution */ #ifndef MINIUPNPC_H_INCLUDED #define MINIUPNPC_H_INCLUDED +/*! \file miniupnpc.h + * \brief Main C API for MiniUPnPc + * + * Contains functions to discover devices and check for device validity + * or connectivity. + * + * \mainpage MiniUPnPc API documentation + * MiniUPnPc (MiniUPnP client) is a library implementing a UPnP + * Internet Gateway Device (IGD) control point. + * + * It should be used by applications that needs to listen to incoming + * traffic from the internet which are running on a LAN where a + * UPnP IGD is running on the router (or gateway). + * + * See more documentation on the website http://miniupnp.free.fr + * or https://miniupnp.tuxfamily.org/ or GitHub : + * https://github.com/miniupnp/miniupnp/tree/master/miniupnpc + */ #include "miniupnpc_declspec.h" #include "igd_desc_parse.h" #include "upnpdev.h" /* error codes : */ +/*! \brief value for success */ #define UPNPDISCOVER_SUCCESS (0) +/*! \brief value for unknown error */ #define UPNPDISCOVER_UNKNOWN_ERROR (-1) +/*! \brief value for a socket error */ #define UPNPDISCOVER_SOCKET_ERROR (-101) +/*! \brief value for a memory allocation error */ #define UPNPDISCOVER_MEMORY_ERROR (-102) -/* versions : */ -#define MINIUPNPC_VERSION "1.9" -#define MINIUPNPC_API_VERSION 14 +/*! \brief software version */ +#define MINIUPNPC_VERSION "2.3.2" +/*! \brief C API version */ +#define MINIUPNPC_API_VERSION 20 + +/*! \brief any (ie system chosen) port */ +#define UPNP_LOCAL_PORT_ANY 0 +/*! \brief Use as an alias for 1900 for backwards compatibility */ +#define UPNP_LOCAL_PORT_SAME 1 #ifdef __cplusplus extern "C" { #endif /* Structures definitions : */ -struct UPNParg { const char * elt; const char * val; }; +/*! + * \brief UPnP method argument + */ +struct UPNParg { + const char * elt; /*!< \brief UPnP argument name */ + const char * val; /*!< \brief UPnP argument value */ +}; + +/*! + * \brief execute a UPnP method (SOAP action) + * + * \param[in] url Control URL for the service + * \param[in] service service to use + * \param[in] action action to call + * \param[in] args action arguments + * \param[out] bufsize the size of the returned buffer + * \return NULL in case of error or the raw XML response + */ char * -simpleUPnPcommand(int, const char *, const char *, - const char *, struct UPNParg *, - int *); +simpleUPnPcommand(const char * url, const char * service, + const char * action, const struct UPNParg * args, + int * bufsize); -/* upnpDiscover() - * discover UPnP devices on the network. +/*! + * \brief Discover UPnP IGD on the network. + * * The discovered devices are returned as a chained list. * It is up to the caller to free the list with freeUPNPDevlist(). - * delay (in millisecond) is the maximum time for waiting any device - * response. * If available, device list will be obtained from MiniSSDPd. - * Default path for minissdpd socket will be used if minissdpdsock argument - * is NULL. - * If multicastif is not NULL, it will be used instead of the default - * multicast interface for sending SSDP discover packets. - * If sameport is not null, SSDP packets will be sent from the source port - * 1900 (same as destination port) otherwise system assign a source port. - * "searchalltypes" parameter is useful when searching several types, - * if 0, the discovery will stop with the first type returning results. - * TTL should default to 2. */ + * + * \param[in] delay (in millisecond) maximum time for waiting any device + * response + * \param[in] multicastif If not NULL, used instead of the default + * multicast interface for sending SSDP discover packets + * \param[in] minissdpdsock Path to minissdpd socket, default is used if + * NULL + * \param[in] localport Source port to send SSDP packets. + * #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port) + * #UPNP_LOCAL_PORT_ANY to let system assign a source port + * \param[in] ipv6 0 for IPv4, 1 of IPv6 + * \param[in] ttl should default to 2 as advised by UDA 1.1 + * \param[out] error error code when NULL is returned + * \return NULL or a linked list + */ MINIUPNP_LIBSPEC struct UPNPDev * upnpDiscover(int delay, const char * multicastif, - const char * minissdpdsock, int sameport, + const char * minissdpdsock, int localport, int ipv6, unsigned char ttl, int * error); +/*! + * \brief Discover all UPnP devices on the network + * + * search for "ssdp:all" + * \param[in] delay (in millisecond) maximum time for waiting any device + * response + * \param[in] multicastif If not NULL, used instead of the default + * multicast interface for sending SSDP discover packets + * \param[in] minissdpdsock Path to minissdpd socket, default is used if + * NULL + * \param[in] localport Source port to send SSDP packets. + * #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port) + * #UPNP_LOCAL_PORT_ANY to let system assign a source port + * \param[in] ipv6 0 for IPv4, 1 of IPv6 + * \param[in] ttl should default to 2 as advised by UDA 1.1 + * \param[out] error error code when NULL is returned + * \return NULL or a linked list + */ MINIUPNP_LIBSPEC struct UPNPDev * upnpDiscoverAll(int delay, const char * multicastif, - const char * minissdpdsock, int sameport, + const char * minissdpdsock, int localport, int ipv6, unsigned char ttl, int * error); +/*! + * \brief Discover one type of UPnP devices + * + * \param[in] device device type to search + * \param[in] delay (in millisecond) maximum time for waiting any device + * response + * \param[in] multicastif If not NULL, used instead of the default + * multicast interface for sending SSDP discover packets + * \param[in] minissdpdsock Path to minissdpd socket, default is used if + * NULL + * \param[in] localport Source port to send SSDP packets. + * #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port) + * #UPNP_LOCAL_PORT_ANY to let system assign a source port + * \param[in] ipv6 0 for IPv4, 1 of IPv6 + * \param[in] ttl should default to 2 as advised by UDA 1.1 + * \param[out] error error code when NULL is returned + * \return NULL or a linked list + */ MINIUPNP_LIBSPEC struct UPNPDev * upnpDiscoverDevice(const char * device, int delay, const char * multicastif, - const char * minissdpdsock, int sameport, + const char * minissdpdsock, int localport, int ipv6, unsigned char ttl, int * error); +/*! + * \brief Discover one or several type of UPnP devices + * + * \param[in] deviceTypes array of device types to search (ending with NULL) + * \param[in] delay (in millisecond) maximum time for waiting any device + * response + * \param[in] multicastif If not NULL, used instead of the default + * multicast interface for sending SSDP discover packets + * \param[in] minissdpdsock Path to minissdpd socket, default is used if + * NULL + * \param[in] localport Source port to send SSDP packets. + * #UPNP_LOCAL_PORT_SAME for 1900 (same as destination port) + * #UPNP_LOCAL_PORT_ANY to let system assign a source port + * \param[in] ipv6 0 for IPv4, 1 of IPv6 + * \param[in] ttl should default to 2 as advised by UDA 1.1 + * \param[out] error error code when NULL is returned + * \param[in] searchalltypes 0 to stop with the first type returning results + * \return NULL or a linked list + */ MINIUPNP_LIBSPEC struct UPNPDev * upnpDiscoverDevices(const char * const deviceTypes[], int delay, const char * multicastif, - const char * minissdpdsock, int sameport, + const char * minissdpdsock, int localport, int ipv6, unsigned char ttl, int * error, int searchalltypes); -/* parserootdesc() : - * parse root XML description of a UPnP device and fill the IGDdatas - * structure. */ -MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *); +/*! + * \brief parse root XML description of a UPnP device + * + * fill the IGDdatas structure. + * \param[in] buffer character buffer containing the XML description + * \param[in] bufsize size in bytes of the buffer + * \param[out] data IGDdatas structure to fill + */ +MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data); -/* structure used to get fast access to urls - * controlURL: controlURL of the WANIPConnection - * ipcondescURL: url of the description of the WANIPConnection - * controlURL_CIF: controlURL of the WANCommonInterfaceConfig - * controlURL_6FC: controlURL of the WANIPv6FirewallControl +/*! + * \brief structure used to get fast access to urls */ struct UPNPUrls { + /*! \brief controlURL of the WANIPConnection */ char * controlURL; + /*! \brief url of the description of the WANIPConnection */ char * ipcondescURL; + /*! \brief controlURL of the WANCommonInterfaceConfig */ char * controlURL_CIF; + /*! \brief controlURL of the WANIPv6FirewallControl */ char * controlURL_6FC; + /*! \brief url of the root description */ char * rootdescURL; }; -/* UPNP_GetValidIGD() : - * return values : - * 0 = NO IGD found - * 1 = A valid connected IGD has been found - * 2 = A valid IGD has been found but it reported as - * not connected - * 3 = an UPnP device has been found but was not recognized as an IGD +/*! \brief NO IGD found */ +#define UPNP_NO_IGD (0) +/*! \brief valid and connected IGD */ +#define UPNP_CONNECTED_IGD (1) +/*! \brief valid and connected IGD but with a reserved address + * (non routable) */ +#define UPNP_PRIVATEIP_IGD (2) +/*! \brief valid but not connected IGD */ +#define UPNP_DISCONNECTED_IGD (3) +/*! \brief UPnP device not recognized as an IGD */ +#define UPNP_UNKNOWN_DEVICE (4) + +/*! + * \brief look for a valid and possibly connected IGD in the list * * In any non zero return case, the urls and data structures * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to * free allocated memory. + * \param[in] devlist A device list obtained with upnpDiscover() / + * upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices() + * \param[out] urls Urls for the IGD found + * \param[out] data datas for the IGD found + * \param[out] lanaddr buffer to copy the local address of the host to reach the IGD + * \param[in] lanaddrlen size of the lanaddr buffer + * \param[out] wanaddr buffer to copy the public address of the IGD + * \param[in] wanaddrlen size of the wanaddr buffer + * \return #UPNP_NO_IGD / #UPNP_CONNECTED_IGD / #UPNP_PRIVATEIP_IGD / + * #UPNP_DISCONNECTED_IGD / #UPNP_UNKNOWN_DEVICE */ MINIUPNP_LIBSPEC int UPNP_GetValidIGD(struct UPNPDev * devlist, struct UPNPUrls * urls, - struct IGDdatas * data, - char * lanaddr, int lanaddrlen); + struct IGDdatas * data, + char * lanaddr, int lanaddrlen, + char * wanaddr, int wanaddrlen); -/* UPNP_GetIGDFromUrl() +/*! + * \brief Get IGD URLs and data for URL + * * Used when skipping the discovery process. - * When succeding, urls, data, and lanaddr arguments are set. - * return value : - * 0 - Not ok - * 1 - OK */ + * \param[in] rootdescurl Root description URL of the device + * \param[out] urls Urls for the IGD found + * \param[out] data datas for the IGD found + * \param[out] lanaddr buffer to copy the local address of the host to reach the IGD + * \param[in] lanaddrlen size of the lanaddr buffer + * \return 0 Not ok / 1 OK + */ MINIUPNP_LIBSPEC int UPNP_GetIGDFromUrl(const char * rootdescurl, struct UPNPUrls * urls, struct IGDdatas * data, char * lanaddr, int lanaddrlen); +/*! + * \brief Prepare the URLs for usage + * + * build absolute URLs from the root description + * \param[out] urls URL structure to initialize + * \param[in] data datas for the IGD + * \param[in] descURL root description URL for the IGD + * \param[in] scope_id if not 0, add the scope to the linklocal IPv6 + * addresses in URLs + */ MINIUPNP_LIBSPEC void -GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, - const char *, unsigned int); +GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data, + const char * descURL, unsigned int scope_id); +/*! + * \brief free the members of a UPNPUrls struct + * + * All URLs buffers are freed and zeroed + * \param[out] urls + */ MINIUPNP_LIBSPEC void -FreeUPNPUrls(struct UPNPUrls *); +FreeUPNPUrls(struct UPNPUrls * urls); -/* return 0 or 1 */ -MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *); +/*! + * \brief check the current connection status of an IGD + * + * it uses UPNP_GetStatusInfo() + * \param[in] urls IGD URLs + * \param[in] data IGD data + * \return 1 Connected / 0 Disconnected + */ +MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data); #ifdef __cplusplus diff --git a/Externals/miniupnpc/src/miniupnpc_declspec.h b/Externals/miniupnpc/src/miniupnpc_declspec.h index 06053af345..e0e9d2b3f7 100644 --- a/Externals/miniupnpc/src/miniupnpc_declspec.h +++ b/Externals/miniupnpc/src/miniupnpc_declspec.h @@ -1,12 +1,22 @@ #ifndef MINIUPNPC_DECLSPEC_H_INCLUDED #define MINIUPNPC_DECLSPEC_H_INCLUDED -#if defined(__GNUC__) && __GNUC__ >= 4 - /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */ - #define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default"))) +/*! \file miniupnpc_declspec.h + * \brief define #MINIUPNP_LIBSPEC for dll exports and imports */ +#if defined(_WIN32) && !defined(MINIUPNP_STATICLIB) + /* for windows dll */ + #ifdef MINIUPNP_EXPORTS + #define MINIUPNP_LIBSPEC __declspec(dllexport) + #else + #define MINIUPNP_LIBSPEC __declspec(dllimport) + #endif #else - #define MINIUPNP_LIBSPEC + #if defined(__GNUC__) && __GNUC__ >= 4 + /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */ + #define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default"))) + #else + #define MINIUPNP_LIBSPEC + #endif #endif #endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */ - diff --git a/Externals/miniupnpc/src/miniupnpc_socketdef.h b/Externals/miniupnpc/src/miniupnpc_socketdef.h new file mode 100644 index 0000000000..5986e58c76 --- /dev/null +++ b/Externals/miniupnpc/src/miniupnpc_socketdef.h @@ -0,0 +1,44 @@ +/* $Id: miniupnpc_socketdef.h,v 1.1 2018/03/13 23:44:10 nanard Exp $ */ +/* Miniupnp project : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author : Thomas Bernard + * Copyright (c) 2018 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided within this distribution */ +#ifndef MINIUPNPC_SOCKETDEF_H_INCLUDED +#define MINIUPNPC_SOCKETDEF_H_INCLUDED + +#ifdef _WIN32 + +#define ISINVALID(s) (INVALID_SOCKET==(s)) + +#else + +#ifndef SOCKET +#define SOCKET int +#endif +#ifndef SSIZE_T +#define SSIZE_T ssize_t +#endif +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (-1) +#endif +#ifndef ISINVALID +#define ISINVALID(s) ((s)<0) +#endif + +#endif + +#ifdef _MSC_VER +#define MSC_CAST_INT (int) +#else +#define MSC_CAST_INT +#endif + +/* definition of PRINT_SOCKET_ERROR */ +#ifdef _WIN32 +#define PRINT_SOCKET_ERROR(x) fprintf(stderr, "Socket error: %s, %d\n", x, WSAGetLastError()); +#else +#define PRINT_SOCKET_ERROR(x) perror(x) +#endif + +#endif /* MINIUPNPC_SOCKETDEF_H_INCLUDED */ diff --git a/Externals/miniupnpc/src/miniupnpcmodule.c b/Externals/miniupnpc/src/miniupnpcmodule.c index 7740c338ed..69b26795ef 100644 --- a/Externals/miniupnpc/src/miniupnpcmodule.c +++ b/Externals/miniupnpc/src/miniupnpcmodule.c @@ -1,17 +1,22 @@ -/* $Id: miniupnpcmodule.c,v 1.24 2014/06/10 09:48:11 nanard Exp $*/ -/* Project : miniupnp +/* $Id: miniupnpcmodule.c,v 1.40 2024/05/09 15:10:29 nanard Exp $*/ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas BERNARD - * website : http://miniupnp.tuxfamily.org/ - * copyright (c) 2007-2014 Thomas Bernard + * website : https://miniupnp.tuxfamily.org/ + * copyright (c) 2007-2024 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #include #define MINIUPNP_STATICLIB -#include "structmember.h" +#include #include "miniupnpc.h" #include "upnpcommands.h" #include "upnperrors.h" +#ifdef _WIN32 +#include +#endif + /* for compatibility with Python < 2.4 */ #ifndef Py_RETURN_NONE #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None @@ -42,7 +47,9 @@ typedef struct { struct UPNPUrls urls; struct IGDdatas data; unsigned int discoverdelay; /* value passed to upnpDiscover() */ + unsigned int localport; /* value passed to upnpDiscover() */ char lanaddr[40]; /* our ip address on the LAN */ + char wanaddr[40]; /* the ExternalIPAddress returned by the IGD */ char * multicastif; char * minissdpdsocket; } UPnPObject; @@ -51,10 +58,22 @@ static PyMemberDef UPnP_members[] = { {"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr), READONLY, "ip address on the LAN" }, + {"wanaddr", T_STRING_INPLACE, offsetof(UPnPObject, wanaddr), + READONLY, "public ip address on the WAN" + }, {"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay), 0/*READWRITE*/, "value in ms used to wait for SSDP responses" }, - /* T_STRING is allways readonly :( */ + {"localport", T_UINT, offsetof(UPnPObject, localport), + 0/*READWRITE*/, + "If localport is set to UPNP_LOCAL_PORT_SAME(1) " + "SSDP packets will be sent from the source port " + "1900 (same as destination port), if set to " + "UPNP_LOCAL_PORT_ANY(0) system assign a source " + "port, any other value will be attempted as the " + "source port" + }, + /* T_STRING is always readonly :( */ {"multicastif", T_STRING, offsetof(UPnPObject, multicastif), 0, "IP of the network interface to be used for multicast operations" }, @@ -70,15 +89,22 @@ static int UPnP_init(UPnPObject *self, PyObject *args, PyObject *kwds) char* multicastif = NULL; char* minissdpdsocket = NULL; static char *kwlist[] = { - "multicastif", "minissdpdsocket", "discoverdelay", NULL + "multicastif", "minissdpdsocket", "discoverdelay", + "localport", NULL }; - if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzI", kwlist, + if(!PyArg_ParseTupleAndKeywords(args, kwds, "|zzII", kwlist, &multicastif, &minissdpdsocket, - &self->discoverdelay)) - return -1; + &self->discoverdelay, + &self->localport)) + return -1; + if(self->localport>1 && + (self->localport>65534||self->localport<1024)) { + PyErr_SetString(PyExc_Exception, "Invalid localport value"); + return -1; + } if(multicastif) self->multicastif = strdup(multicastif); if(minissdpdsocket) @@ -100,9 +126,9 @@ UPnPObject_dealloc(UPnPObject *self) static PyObject * UPnP_discover(UPnPObject *self) { - struct UPNPDev * dev; - int i; + int error = 0; PyObject *res = NULL; + if(self->devlist) { freeUPNPDevlist(self->devlist); @@ -112,25 +138,42 @@ UPnP_discover(UPnPObject *self) self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/, self->multicastif, self->minissdpdsocket, - 0/*sameport flag*/, + (int)self->localport, 0/*ip v6*/, 2/* TTL */, - 0/*error */); + &error); Py_END_ALLOW_THREADS /* Py_RETURN_NONE ??? */ - for(dev = self->devlist, i = 0; dev; dev = dev->pNext) - i++; - res = Py_BuildValue("i", i); - return res; + if (self->devlist != NULL) { + struct UPNPDev * dev; + int i = 0; + + for(dev = self->devlist; dev; dev = dev->pNext) + i++; + res = Py_BuildValue("i", i); + return res; + } else { + PyErr_SetString(PyExc_Exception, strupnperror(error)); + return NULL; + } } static PyObject * -UPnP_selectigd(UPnPObject *self) +UPnP_selectigd(UPnPObject *self, PyObject *args) { + const char * rootDescUrl = NULL; int r; + if(!PyArg_ParseTuple(args, "|z", &rootDescUrl)) + return NULL; Py_BEGIN_ALLOW_THREADS - r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data, - self->lanaddr, sizeof(self->lanaddr)); + if (rootDescUrl == NULL) { + r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data, + self->lanaddr, sizeof(self->lanaddr), + self->wanaddr, sizeof(self->wanaddr)); + } else { + r = UPNP_GetIGDFromUrl(rootDescUrl, &self->urls, &self->data, + self->lanaddr, sizeof(self->lanaddr)); + } Py_END_ALLOW_THREADS if(r) { @@ -152,7 +195,11 @@ Py_BEGIN_ALLOW_THREADS i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF, self->data.CIF.servicetype); Py_END_ALLOW_THREADS +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", i); +#else + return Py_BuildValue("i", (int)i); +#endif } static PyObject * @@ -163,7 +210,11 @@ Py_BEGIN_ALLOW_THREADS i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF, self->data.CIF.servicetype); Py_END_ALLOW_THREADS +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", i); +#else + return Py_BuildValue("i", (int)i); +#endif } static PyObject * @@ -174,7 +225,11 @@ Py_BEGIN_ALLOW_THREADS i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF, self->data.CIF.servicetype); Py_END_ALLOW_THREADS +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", i); +#else + return Py_BuildValue("i", (int)i); +#endif } static PyObject * @@ -185,7 +240,11 @@ Py_BEGIN_ALLOW_THREADS i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF, self->data.CIF.servicetype); Py_END_ALLOW_THREADS +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", i); +#else + return Py_BuildValue("i", (int)i); +#endif } static PyObject * @@ -202,7 +261,11 @@ Py_BEGIN_ALLOW_THREADS status, &uptime, lastconnerror); Py_END_ALLOW_THREADS if(r==UPNPCOMMAND_SUCCESS) { +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror); +#else + return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror); +#endif } else { /* TODO: have our own exception type ! */ PyErr_SetString(PyExc_Exception, strupnperror(r)); @@ -251,7 +314,7 @@ Py_END_ALLOW_THREADS } /* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc, - * remoteHost) + * remoteHost, leaseDuration) * protocol is 'UDP' or 'TCP' */ static PyObject * UPnP_addportmapping(UPnPObject *self, PyObject *args) @@ -264,17 +327,24 @@ UPnP_addportmapping(UPnPObject *self, PyObject *args) const char * host; const char * desc; const char * remoteHost; - const char * leaseDuration = "0"; + unsigned int intLeaseDuration = 0; + char strLeaseDuration[12]; int r; - if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto, - &host, &iPort, &desc, &remoteHost)) +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) + if (!PyArg_ParseTuple(args, "HssHzz|I", &ePort, &proto, + &host, &iPort, &desc, &remoteHost, &intLeaseDuration)) +#else + if (!PyArg_ParseTuple(args, "HssHzz|i", &ePort, &proto, + &host, &iPort, &desc, &remoteHost, (int *)&intLeaseDuration)) +#endif return NULL; Py_BEGIN_ALLOW_THREADS sprintf(extPort, "%hu", ePort); sprintf(inPort, "%hu", iPort); + sprintf(strLeaseDuration, "%u", intLeaseDuration); r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype, extPort, inPort, host, desc, proto, - remoteHost, leaseDuration); + remoteHost, strLeaseDuration); Py_END_ALLOW_THREADS if(r==UPNPCOMMAND_SUCCESS) { @@ -308,7 +378,7 @@ UPnP_addanyportmapping(UPnPObject *self, PyObject *args) const char * remoteHost; const char * leaseDuration = "0"; int r; - if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto, &host, &iPort, &desc, &remoteHost)) + if (!PyArg_ParseTuple(args, "HssHzz", &ePort, &proto, &host, &iPort, &desc, &remoteHost)) return NULL; Py_BEGIN_ALLOW_THREADS sprintf(extPort, "%hu", ePort); @@ -364,14 +434,14 @@ UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args) unsigned short ePortEnd; const char * proto; unsigned char manage; - char manageStr[1]; + char manageStr[6]; int r; if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage)) return NULL; Py_BEGIN_ALLOW_THREADS sprintf(extPortStart, "%hu", ePortStart); sprintf(extPortEnd, "%hu", ePortEnd); - sprintf(manageStr, "%hhu", manage); + sprintf(manageStr, "%hu", (unsigned short)manage); r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype, extPortStart, extPortEnd, proto, manageStr); Py_END_ALLOW_THREADS @@ -395,7 +465,11 @@ Py_BEGIN_ALLOW_THREADS &n); Py_END_ALLOW_THREADS if(r==UPNPCOMMAND_SUCCESS) { +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("I", n); +#else + return Py_BuildValue("i", (int)n); +#endif } else { /* TODO: have our own exception type ! */ PyErr_SetString(PyExc_Exception, strupnperror(r)); @@ -480,9 +554,15 @@ Py_END_ALLOW_THREADS ePort = (unsigned short)atoi(extPort); iPort = (unsigned short)atoi(intPort); dur = (unsigned int)strtoul(duration, 0, 0); +#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3) return Py_BuildValue("(H,s,(s,H),s,s,s,I)", ePort, protocol, intClient, iPort, desc, enabled, rHost, dur); +#else + return Py_BuildValue("(i,s,(s,i),s,s,s,i)", + (int)ePort, protocol, intClient, (int)iPort, + desc, enabled, rHost, (int)dur); +#endif } else { @@ -495,7 +575,7 @@ static PyMethodDef UPnP_methods[] = { {"discover", (PyCFunction)UPnP_discover, METH_NOARGS, "discover UPnP IGD devices on the network" }, - {"selectigd", (PyCFunction)UPnP_selectigd, METH_NOARGS, + {"selectigd", (PyCFunction)UPnP_selectigd, METH_VARARGS, "select a valid UPnP IGD among discovered devices" }, {"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS, @@ -585,8 +665,9 @@ static PyTypeObject UPnPType = { #ifndef _WIN32 PyType_GenericNew,/*UPnP_new,*/ /* tp_new */ #else - 0, + 0, /* tp_new */ #endif + 0, /* tp_free */ }; /* module methods */ @@ -622,6 +703,20 @@ initminiupnpc(void) PyObject* m; #ifdef _WIN32 + /* initialize Winsock. */ + WSADATA wsaData; + int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if (nResult != 0) + { + /* error code could be WSASYSNOTREADY WSASYSNOTREADY + * WSASYSNOTREADY WSASYSNOTREADY WSASYSNOTREADY */ +#if PY_MAJOR_VERSION >= 3 + return 0; +#else + return; +#endif + } + UPnPType.tp_new = PyType_GenericNew; #endif if (PyType_Ready(&UPnPType) < 0) diff --git a/Externals/miniupnpc/src/miniupnpctypes.h b/Externals/miniupnpc/src/miniupnpctypes.h index 307ce39699..c832e01720 100644 --- a/Externals/miniupnpc/src/miniupnpctypes.h +++ b/Externals/miniupnpc/src/miniupnpctypes.h @@ -1,19 +1,27 @@ -/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */ -/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org - * Author : Thomas Bernard - * Copyright (c) 2011 Thomas Bernard +/* $Id: miniupnpctypes.h,v 1.4 2025/02/08 23:15:16 nanard Exp $ */ +/* Project: miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org + * Author: Thomas Bernard + * Copyright (c) 2021-2025 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided within this distribution */ #ifndef MINIUPNPCTYPES_H_INCLUDED #define MINIUPNPCTYPES_H_INCLUDED -#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) +/*! \file miniupnpctypes.h + * \brief type definitions + * + * Use unsigned long long when available : + * strtoull is C99 + * + * \def UNSIGNED_INTEGER + * \brief `unsigned long long` or `unsigned int` + * \todo int can be 16 bits, so it should be `unsigned long` + */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L #define UNSIGNED_INTEGER unsigned long long -#define STRTOUI strtoull #else #define UNSIGNED_INTEGER unsigned int -#define STRTOUI strtoul #endif #endif - diff --git a/Externals/miniupnpc/src/miniwget.c b/Externals/miniupnpc/src/miniwget.c index 98019f6e28..a7a32dfdba 100644 --- a/Externals/miniupnpc/src/miniwget.c +++ b/Externals/miniupnpc/src/miniwget.c @@ -1,8 +1,8 @@ -/* $Id: miniwget.c,v 1.70 2015/07/15 12:41:13 nanard Exp $ */ +/* $Id: miniwget.c,v 1.85 2023/06/15 21:47:50 nanard Exp $ */ /* Project : miniupnp - * Website : http://miniupnp.free.fr/ + * Website : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas Bernard - * Copyright (c) 2005-2015 Thomas Bernard + * Copyright (c) 2005-2024 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -15,7 +15,7 @@ #include #include #define MAXHOSTNAMELEN 64 -#define snprintf _snprintf +#include "win32_snprintf.h" #define socklen_t int #ifndef strncasecmp #if defined(_MSC_VER) && (_MSC_VER >= 1400) @@ -65,7 +65,7 @@ * to the length parameter. */ void * -getHTTPResponse(int s, int * size) +getHTTPResponse(SOCKET s, int * size, int * status_code) { char buf[2048]; int n; @@ -83,7 +83,12 @@ getHTTPResponse(int s, int * size) unsigned int content_buf_used = 0; char chunksize_buf[32]; unsigned int chunksize_buf_index; +#ifdef DEBUG + char * reason_phrase = NULL; + int reason_phrase_len = 0; +#endif + if(status_code) *status_code = -1; header_buf = malloc(header_buf_len); if(header_buf == NULL) { @@ -106,7 +111,7 @@ getHTTPResponse(int s, int * size) chunksize_buf[0] = '\0'; chunksize_buf_index = 0; - while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0) + while((n = receivedata(s, buf, sizeof(buf), 5000, NULL)) > 0) { if(endofheaders == 0) { @@ -155,7 +160,7 @@ getHTTPResponse(int s, int * size) continue; /* parse header lines */ for(i = 0; i < endofheaders - 1; i++) { - if(colon <= linestart && header_buf[i]==':') + if(linestart > 0 && colon <= linestart && header_buf[i]==':') { colon = i; while(i < (endofheaders-1) @@ -166,7 +171,34 @@ getHTTPResponse(int s, int * size) /* detecting end of line */ else if(header_buf[i]=='\r' || header_buf[i]=='\n') { - if(colon > linestart && valuestart > colon) + if(linestart == 0 && status_code) + { + /* Status line + * HTTP-Version SP Status-Code SP Reason-Phrase CRLF */ + int sp; + for(sp = 0; sp < i - 1; sp++) + if(header_buf[sp] == ' ') + { + if(*status_code < 0) + { + if (header_buf[sp+1] >= '1' && header_buf[sp+1] <= '9') + *status_code = atoi(header_buf + sp + 1); + } + else + { +#ifdef DEBUG + reason_phrase = header_buf + sp + 1; + reason_phrase_len = i - sp - 1; +#endif + break; + } + } +#ifdef DEBUG + printf("HTTP status code = %d, Reason phrase = %.*s\n", + *status_code, reason_phrase_len, reason_phrase); +#endif + } + else if(colon > linestart && valuestart > colon) { #ifdef DEBUG printf("header='%.*s', value='%.*s'\n", @@ -201,100 +233,69 @@ getHTTPResponse(int s, int * size) memcpy(buf, header_buf + endofheaders, n); /* if(headers) */ } - if(endofheaders) + /* if we get there, endofheaders != 0. + * In the other case, there was a continue above */ + /* content */ + if(chunked) { - /* content */ - if(chunked) + int i = 0; + while(i < n) { - int i = 0; - while(i < n) + if(chunksize == 0) { + /* reading chunk size */ + if(chunksize_buf_index == 0) { + /* skipping any leading CR LF */ + if(buf[i] == '\r') i++; + if(i= '0' + && chunksize_buf[j] <= '9') + chunksize = (chunksize << 4) + (chunksize_buf[j] - '0'); + else + chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10); + } + chunksize_buf[0] = '\0'; + chunksize_buf_index = 0; + i++; + } else { + /* not finished to get chunksize */ + continue; + } +#ifdef DEBUG + printf("chunksize = %u (%x)\n", chunksize, chunksize); +#endif if(chunksize == 0) { - /* reading chunk size */ - if(chunksize_buf_index == 0) { - /* skipping any leading CR LF */ - if(i= '0' - && chunksize_buf[j] <= '9') - chunksize = (chunksize << 4) + (chunksize_buf[j] - '0'); - else - chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10); - } - chunksize_buf[0] = '\0'; - chunksize_buf_index = 0; - i++; - } else { - /* not finished to get chunksize */ - continue; - } #ifdef DEBUG - printf("chunksize = %u (%x)\n", chunksize, chunksize); + printf("end of HTTP content - %d %d\n", i, n); + /*printf("'%.*s'\n", n-i, buf+i);*/ #endif - if(chunksize == 0) - { -#ifdef DEBUG - printf("end of HTTP content - %d %d\n", i, n); - /*printf("'%.*s'\n", n-i, buf+i);*/ -#endif - goto end_of_stream; - } + goto end_of_stream; } - bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i); - if((content_buf_used + bytestocopy) > content_buf_len) - { - char * tmp; - if(content_length >= (int)(content_buf_used + bytestocopy)) { - content_buf_len = content_length; - } else { - content_buf_len = content_buf_used + bytestocopy; - } - tmp = realloc(content_buf, content_buf_len); - if(tmp == NULL) { - /* memory allocation error */ - free(content_buf); - free(header_buf); - *size = -1; - return NULL; - } - content_buf = tmp; - } - memcpy(content_buf + content_buf_used, buf + i, bytestocopy); - content_buf_used += bytestocopy; - i += bytestocopy; - chunksize -= bytestocopy; } - } - else - { - /* not chunked */ - if(content_length > 0 - && (int)(content_buf_used + n) > content_length) { - /* skipping additional bytes */ - n = content_length - content_buf_used; - } - if(content_buf_used + n > content_buf_len) + /* it is guaranteed that (n >= i) */ + bytestocopy = (chunksize < (unsigned int)(n - i))?chunksize:(unsigned int)(n - i); + if((content_buf_used + bytestocopy) > content_buf_len) { char * tmp; - if(content_length >= (int)(content_buf_used + n)) { + if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) { content_buf_len = content_length; } else { - content_buf_len = content_buf_used + n; + content_buf_len = content_buf_used + bytestocopy; } tmp = realloc(content_buf, content_buf_len); if(tmp == NULL) { @@ -306,12 +307,44 @@ getHTTPResponse(int s, int * size) } content_buf = tmp; } - memcpy(content_buf + content_buf_used, buf, n); - content_buf_used += n; + memcpy(content_buf + content_buf_used, buf + i, bytestocopy); + content_buf_used += bytestocopy; + i += bytestocopy; + chunksize -= bytestocopy; } } + else + { + /* not chunked */ + if(content_length > 0 + && (content_buf_used + n) > (unsigned int)content_length) { + /* skipping additional bytes */ + n = content_length - content_buf_used; + } + if(content_buf_used + n > content_buf_len) + { + char * tmp; + if(content_length >= 0 + && (unsigned int)content_length >= (content_buf_used + n)) { + content_buf_len = content_length; + } else { + content_buf_len = content_buf_used + n; + } + tmp = realloc(content_buf, content_buf_len); + if(tmp == NULL) { + /* memory allocation error */ + free(content_buf); + free(header_buf); + *size = -1; + return NULL; + } + content_buf = tmp; + } + memcpy(content_buf + content_buf_used, buf, n); + content_buf_used += n; + } /* use the Content-Length header value if available */ - if(content_length > 0 && (int)content_buf_used >= content_length) + if(content_length > 0 && content_buf_used >= (unsigned int)content_length) { #ifdef DEBUG printf("End of HTTP content\n"); @@ -320,7 +353,7 @@ getHTTPResponse(int s, int * size) } } end_of_stream: - free(header_buf); header_buf = NULL; + free(header_buf); *size = content_buf_used; if(content_buf_used == 0) { @@ -337,10 +370,11 @@ static void * miniwget3(const char * host, unsigned short port, const char * path, int * size, char * addr_str, int addr_str_len, - const char * httpversion, unsigned int scope_id) + const char * httpversion, unsigned int scope_id, + int * status_code) { char buf[2048]; - int s; + SOCKET s; int n; int len; int sent; @@ -348,7 +382,7 @@ miniwget3(const char * host, *size = 0; s = connecthostport(host, port, scope_id); - if(s < 0) + if(ISINVALID(s)) return NULL; /* get address for caller ! */ @@ -409,11 +443,15 @@ miniwget3(const char * host, len = snprintf(buf, sizeof(buf), "GET %s HTTP/%s\r\n" "Host: %s:%d\r\n" - "Connection: Close\r\n" - "User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" - + "Connection: close\r\n" + "User-Agent: " OS_STRING " " UPNP_VERSION_STRING " MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" "\r\n", path, httpversion, host, port); + if ((unsigned int)len >= sizeof(buf)) + { + closesocket(s); + return NULL; + } sent = 0; /* sending the HTTP request */ while(sent < len) @@ -430,43 +468,11 @@ miniwget3(const char * host, sent += n; } } - content = getHTTPResponse(s, size); + content = getHTTPResponse(s, size, status_code); closesocket(s); return content; } -/* miniwget2() : - * Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */ -static void * -miniwget2(const char * host, - unsigned short port, const char * path, - int * size, char * addr_str, int addr_str_len, - unsigned int scope_id) -{ - char * respbuffer; - -#if 1 - respbuffer = miniwget3(host, port, path, size, - addr_str, addr_str_len, "1.1", scope_id); -#else - respbuffer = miniwget3(host, port, path, size, - addr_str, addr_str_len, "1.0", scope_id); - if (*size == 0) - { -#ifdef DEBUG - printf("Retrying with HTTP/1.1\n"); -#endif - free(respbuffer); - respbuffer = miniwget3(host, port, path, size, - addr_str, addr_str_len, "1.1", scope_id); - } -#endif - return respbuffer; -} - - - - /* parseURL() * arguments : * url : source string not modified @@ -520,7 +526,7 @@ parseURL(const char * url, #else /* under windows, scope is numerical */ char tmp[8]; - int l; + size_t l; scope++; /* "%25" is just '%' in URL encoding */ if(scope[0] == '2' && scope[1] == '5') @@ -583,7 +589,8 @@ parseURL(const char * url, } void * -miniwget(const char * url, int * size, unsigned int scope_id) +miniwget(const char * url, int * size, + unsigned int scope_id, int * status_code) { unsigned short port; char * path; @@ -596,12 +603,13 @@ miniwget(const char * url, int * size, unsigned int scope_id) printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", hostname, port, path, scope_id); #endif - return miniwget2(hostname, port, path, size, 0, 0, scope_id); + return miniwget3(hostname, port, path, size, 0, 0, "1.1", scope_id, status_code); } void * miniwget_getaddr(const char * url, int * size, - char * addr, int addrlen, unsigned int scope_id) + char * addr, int addrlen, unsigned int scope_id, + int * status_code) { unsigned short port; char * path; @@ -616,6 +624,5 @@ miniwget_getaddr(const char * url, int * size, printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n", hostname, port, path, scope_id); #endif - return miniwget2(hostname, port, path, size, addr, addrlen, scope_id); + return miniwget3(hostname, port, path, size, addr, addrlen, "1.1", scope_id, status_code); } - diff --git a/Externals/miniupnpc/src/miniwget.h b/Externals/miniupnpc/src/miniwget.h index e231fc27a3..ed5364c2f1 100644 --- a/Externals/miniupnpc/src/miniwget.h +++ b/Externals/miniupnpc/src/miniwget.h @@ -1,30 +1,54 @@ -/* $Id: miniwget.h,v 1.7 2012/06/23 22:35:59 nanard Exp $ */ +/* $Id: miniwget.h,v 1.14 2025/02/08 23:15:17 nanard Exp $ */ /* Project : miniupnp * Author : Thomas Bernard - * Copyright (c) 2005-2015 Thomas Bernard + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Copyright (c) 2005-2025 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. * */ #ifndef MINIWGET_H_INCLUDED #define MINIWGET_H_INCLUDED +/*! \file miniwget.h + * \brief Lightweight HTTP client API + */ #include "miniupnpc_declspec.h" #ifdef __cplusplus extern "C" { #endif -MINIUPNP_LIBSPEC void * getHTTPResponse(int s, int * size); +/*! \brief perform HTTP GET on an URL + * + * \param[in] url HTTP URL to GET + * \param[out] size length of the returned buffer. -1 in case of memory + * allocation error + * \param[in] scope_id interface id for IPv6 to use if not specified in the URL + * \param[out] status_code HTTP response status code (200, 404, etc.) + * \return the body of the HTTP response + */ +MINIUPNP_LIBSPEC void * miniwget(const char * url, int * size, + unsigned int scope_id, int * status_code); -MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int); - -MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int); - -int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *); +/*! \brief perform HTTP GET on an URL + * + * Also get the local address used to reach the HTTP server + * + * \param[in] url HTTP URL to GET + * \param[out] size length of the returned buffer. -1 in case of memory + * allocation error + * \param[out] addr local address used to connect to the server + * \param[in] addrlen size of the addr buffer + * \param[in] scope_id interface id for IPv6 to use if not specified in the URL + * \param[out] status_code HTTP response status code (200, 404, etc.) + * \return the body of the HTTP response + */ +MINIUPNP_LIBSPEC void * miniwget_getaddr(const char * url, int * size, + char * addr, int addrlen, + unsigned int scope_id, int * status_code); #ifdef __cplusplus } #endif #endif - diff --git a/Externals/miniupnpc/src/miniwget_private.h b/Externals/miniupnpc/src/miniwget_private.h new file mode 100644 index 0000000000..0585de9e4d --- /dev/null +++ b/Externals/miniupnpc/src/miniwget_private.h @@ -0,0 +1,54 @@ +/* $Id: miniwget_private.h,v 1.1 2018/04/06 10:17:58 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2018-2025 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. + * */ +#ifndef MINIWGET_INTERNAL_H_INCLUDED +#define MINIWGET_INTERNAL_H_INCLUDED + +/*! \file miniwget_private.h + * \brief Lightweight HTTP client private API + */ +#include "miniupnpc_socketdef.h" + +/*! \brief Read a HTTP response from a socket + * + * Processed HTTP headers : + * - `Content-Length` + * - `Transfer-encoding` + * return a pointer to the content buffer, which length is saved + * to the length parameter. + * \param[in] s socket + * \param[out] size returned content buffer size + * \param[out] status_code HTTP Status code + * \return malloc'ed content buffer + */ +void * getHTTPResponse(SOCKET s, int * size, int * status_code); + +/*! \brief parse a HTTP URL + * + * URL formats supported : + * - `http://192.168.1.1/path/xxx` + * - `http://192.168.1.1:8080/path/xxx` + * - `http://[2a00:1234:5678:90ab::123]/path/xxx` + * - `http://[2a00:1234:5678:90ab::123]:8080/path/xxx` + * - `http://[fe80::1234:5678:90ab%%eth0]/path/xxx` + * - `http://[fe80::1234:5678:90ab%%eth0]:8080/path/xxx` + * + * `%` may be encoded as `%25` + * + * \param[in] url URL to parse + * \param[out] hostname hostname part of the URL (size of MAXHOSTNAMELEN+1) + * \param[out] port set to the port specified in the URL or 80 + * \param[out] path set to the begining of the path part of the URL + * \param[out] scope_id set to the interface id if specified in the + * link-local IPv6 address + * \return 0 for failure, 1 for success + */ +int parseURL(const char * url, + char * hostname, unsigned short * port, char * * path, + unsigned int * scope_id); + +#endif diff --git a/Externals/miniupnpc/src/minixml.c b/Externals/miniupnpc/src/minixml.c index 1f2227343c..935ec443e7 100644 --- a/Externals/miniupnpc/src/minixml.c +++ b/Externals/miniupnpc/src/minixml.c @@ -1,10 +1,11 @@ -/* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 nanard Exp $ */ -/* minixml.c : the minimum size a xml parser can be ! */ +/* $Id: minixml.c,v 1.12 2017/12/12 11:17:40 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * minixml.c : the minimum size a xml parser can be ! */ /* Project : miniupnp * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author : Thomas Bernard -Copyright (c) 2005-2014, Thomas BERNARD +Copyright (c) 2005-2017, Thomas BERNARD All rights reserved. Redistribution and use in source and binary forms, with or without @@ -161,7 +162,8 @@ static void parseelt(struct xmlparser * p) if (p->xml >= p->xmlend) return; } - if(memcmp(p->xml, " */ + if((p->xmlend >= (p->xml + (9 + 3))) && (memcmp(p->xml, "xml += 9; diff --git a/Externals/miniupnpc/src/minixml.h b/Externals/miniupnpc/src/minixml.h index 19e6f513bf..2e60397388 100644 --- a/Externals/miniupnpc/src/minixml.h +++ b/Externals/miniupnpc/src/minixml.h @@ -10,7 +10,7 @@ * */ #ifndef MINIXML_H_INCLUDED #define MINIXML_H_INCLUDED -#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n')) +#define IS_WHITE_SPACE(c) ((c)==' ' || (c)=='\t' || (c)=='\r' || (c)=='\n') /* if a callback function pointer is set to NULL, * the function is not called */ diff --git a/Externals/miniupnpc/src/minixmlvalid.c b/Externals/miniupnpc/src/minixmlvalid.c new file mode 100644 index 0000000000..dad1488122 --- /dev/null +++ b/Externals/miniupnpc/src/minixmlvalid.c @@ -0,0 +1,163 @@ +/* $Id: minixmlvalid.c,v 1.7 2015/07/15 12:41:15 nanard Exp $ */ +/* MiniUPnP Project + * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/ + * minixmlvalid.c : + * validation program for the minixml parser + * + * (c) 2006-2011 Thomas Bernard */ + +#include +#include +#include +#include "minixml.h" + +/* xml event structure */ +struct event { + enum { ELTSTART, ELTEND, ATT, CHARDATA } type; + const char * data; + int len; +}; + +struct eventlist { + int n; + struct event * events; +}; + +/* compare 2 xml event lists + * return 0 if the two lists are equals */ +int evtlistcmp(struct eventlist * a, struct eventlist * b) +{ + int i; + struct event * ae, * be; + if(a->n != b->n) + { + printf("event number not matching : %d != %d\n", a->n, b->n); + /*return 1;*/ + } + for(i=0; in; i++) + { + ae = a->events + i; + be = b->events + i; + if( (ae->type != be->type) + ||(ae->len != be->len) + ||memcmp(ae->data, be->data, ae->len)) + { + printf("Found a difference : %d '%.*s' != %d '%.*s'\n", + ae->type, ae->len, ae->data, + be->type, be->len, be->data); + return 1; + } + } + return 0; +} + +/* Test data */ +static const char xmldata[] = +"\n" +" " +"character data" +" \n \t" +"" +"\nstuff !\n ]]> \n\n" +" \tchardata1 chardata2 " +""; + +static const struct event evtref[] = +{ + {ELTSTART, "xmlroot", 7}, + {ELTSTART, "elt1", 4}, + /* attributes */ + {CHARDATA, "character data", 14}, + {ELTEND, "elt1", 4}, + {ELTSTART, "elt1b", 5}, + {ELTSTART, "elt1", 4}, + {CHARDATA, " stuff !\n ", 16}, + {ELTEND, "elt1", 4}, + {ELTSTART, "elt2a", 5}, + {ELTSTART, "elt2b", 5}, + {CHARDATA, "chardata1", 9}, + {ELTEND, "elt2b", 5}, + {ELTSTART, "elt2b", 5}, + {CHARDATA, " chardata2 ", 11}, + {ELTEND, "elt2b", 5}, + {ELTEND, "elt2a", 5}, + {ELTEND, "xmlroot", 7} +}; + +void startelt(void * data, const char * p, int l) +{ + struct eventlist * evtlist = data; + struct event * evt; + evt = evtlist->events + evtlist->n; + /*printf("startelt : %.*s\n", l, p);*/ + evt->type = ELTSTART; + evt->data = p; + evt->len = l; + evtlist->n++; +} + +void endelt(void * data, const char * p, int l) +{ + struct eventlist * evtlist = data; + struct event * evt; + evt = evtlist->events + evtlist->n; + /*printf("endelt : %.*s\n", l, p);*/ + evt->type = ELTEND; + evt->data = p; + evt->len = l; + evtlist->n++; +} + +void chardata(void * data, const char * p, int l) +{ + struct eventlist * evtlist = data; + struct event * evt; + evt = evtlist->events + evtlist->n; + /*printf("chardata : '%.*s'\n", l, p);*/ + evt->type = CHARDATA; + evt->data = p; + evt->len = l; + evtlist->n++; +} + +int testxmlparser(const char * xml, int size) +{ + int r; + struct eventlist evtlist; + struct eventlist evtlistref; + struct xmlparser parser; + evtlist.n = 0; + evtlist.events = malloc(sizeof(struct event)*100); + if(evtlist.events == NULL) + { + fprintf(stderr, "Memory allocation error.\n"); + return -1; + } + memset(&parser, 0, sizeof(parser)); + parser.xmlstart = xml; + parser.xmlsize = size; + parser.data = &evtlist; + parser.starteltfunc = startelt; + parser.endeltfunc = endelt; + parser.datafunc = chardata; + parsexml(&parser); + printf("%d events\n", evtlist.n); + /* compare */ + evtlistref.n = sizeof(evtref)/sizeof(struct event); + evtlistref.events = (struct event *)evtref; + r = evtlistcmp(&evtlistref, &evtlist); + free(evtlist.events); + return r; +} + +int main(int argc, char * * argv) +{ + int r; + (void)argc; (void)argv; + + r = testxmlparser(xmldata, sizeof(xmldata)-1); + if(r) + printf("minixml validation test failed\n"); + return r; +} + diff --git a/Externals/miniupnpc/src/portlistingparse.c b/Externals/miniupnpc/src/portlistingparse.c index 0e09278035..162cf8b7ec 100644 --- a/Externals/miniupnpc/src/portlistingparse.c +++ b/Externals/miniupnpc/src/portlistingparse.c @@ -1,7 +1,7 @@ /* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2011-2015 Thomas Bernard + * (c) 2011-2020 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #include @@ -12,6 +12,11 @@ #include "portlistingparse.h" #include "minixml.h" +#if defined(__HAIKU__) +/* rename our private function because Haiku already defines a atoui() function */ +#define atoui atoui2 +#endif + /* list of the elements */ static const struct { const portMappingElt code; @@ -55,7 +60,7 @@ startelt(void * d, const char * name, int l) pdata->curelt = PortMappingEltNone; for(i = 0; elements[i].str; i++) { - if(memcmp(name, elements[i].str, l) == 0) + if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0) { pdata->curelt = elements[i].code; break; diff --git a/Externals/miniupnpc/src/portlistingparse.h b/Externals/miniupnpc/src/portlistingparse.h index e3957a3f4c..6b4e415aa7 100644 --- a/Externals/miniupnpc/src/portlistingparse.h +++ b/Externals/miniupnpc/src/portlistingparse.h @@ -1,12 +1,29 @@ -/* $Id: portlistingparse.h,v 1.10 2014/11/01 10:37:32 nanard Exp $ */ +/* $Id: portlistingparse.h,v 1.12 2025/02/08 23:15:17 nanard Exp $ */ /* MiniUPnP project * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2011-2015 Thomas Bernard + * (c) 2011-2025 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef PORTLISTINGPARSE_H_INCLUDED #define PORTLISTINGPARSE_H_INCLUDED +/*! \file portlistingparse.h + * \brief Parsing of the list of port mappings + * + * As returned by GetListOfPortMappings. + * Sample of PortMappingEntry : + * + * + * 202.233.2.1 + * 2345 + * TCP + * 2345 + * 192.168.1.137 + * 1 + * dooom + * 345 + * + */ #include "miniupnpc_declspec.h" /* for the definition of UNSIGNED_INTEGER */ #include "miniupnpctypes.h" @@ -15,17 +32,8 @@ extern "C" { #endif -/* sample of PortMappingEntry : - - 202.233.2.1 - 2345 - TCP - 2345 - 192.168.1.137 - 1 - dooom - 345 - +/*! + * \brief enum of all XML elements */ typedef enum { PortMappingEltNone, PortMappingEntry, NewRemoteHost, @@ -34,27 +42,45 @@ typedef enum { PortMappingEltNone, NewEnabled, NewDescription, NewLeaseTime } portMappingElt; +/*! + * \brief linked list of port mappings + */ struct PortMapping { - struct PortMapping * l_next; /* list next element */ - UNSIGNED_INTEGER leaseTime; - unsigned short externalPort; - unsigned short internalPort; - char remoteHost[64]; - char internalClient[64]; - char description[64]; - char protocol[4]; - unsigned char enabled; + struct PortMapping * l_next; /*!< \brief next list element */ + UNSIGNED_INTEGER leaseTime; /*!< \brief in seconds */ + unsigned short externalPort; /*!< \brief external port */ + unsigned short internalPort; /*!< \brief internal port */ + char remoteHost[64]; /*!< \brief empty for wildcard */ + char internalClient[64]; /*!< \brief internal IP address */ + char description[64]; /*!< \brief description */ + char protocol[4]; /*!< \brief `TCP` or `UDP` */ + unsigned char enabled; /*!< \brief 0 (false) or 1 (true) */ }; +/*! + * \brief structure for ParsePortListing() + */ struct PortMappingParserData { - struct PortMapping * l_head; /* list head */ - portMappingElt curelt; + struct PortMapping * l_head; /*!< \brief list head */ + portMappingElt curelt; /*!< \brief currently parsed element */ }; +/*! + * \brief parse the NewPortListing part of GetListOfPortMappings response + * + * \param[in] buffer XML data + * \param[in] bufsize length of XML data + * \param[out] pdata Parsed data + */ MINIUPNP_LIBSPEC void ParsePortListing(const char * buffer, int bufsize, struct PortMappingParserData * pdata); +/*! + * \brief free parsed data structure + * + * \param[in] pdata Parsed data to free + */ MINIUPNP_LIBSPEC void FreePortListing(struct PortMappingParserData * pdata); diff --git a/Externals/miniupnpc/src/receivedata.c b/Externals/miniupnpc/src/receivedata.c index fb05e09db6..7f187f6e56 100644 --- a/Externals/miniupnpc/src/receivedata.c +++ b/Externals/miniupnpc/src/receivedata.c @@ -1,8 +1,8 @@ -/* $Id: receivedata.c,v 1.6 2014/11/13 13:51:52 nanard Exp $ */ +/* $Id: receivedata.c,v 1.10 2021/03/02 23:33:07 nanard Exp $ */ /* Project : miniupnp * Website : http://miniupnp.free.fr/ * Author : Thomas Bernard - * Copyright (c) 2011-2014 Thomas Bernard + * Copyright (c) 2011-2021 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided in this distribution. */ @@ -27,20 +27,14 @@ #define MINIUPNPC_IGNORE_EINTR #endif /* _WIN32 */ -#ifdef _WIN32 -#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); -#else -#define PRINT_SOCKET_ERROR(x) perror(x) -#endif - #include "receivedata.h" int -receivedata(int socket, +receivedata(SOCKET socket, char * data, int length, int timeout, unsigned int * scope_id) { -#if MINIUPNPC_GET_SRC_ADDR +#ifdef MINIUPNPC_GET_SRC_ADDR struct sockaddr_storage src_addr; socklen_t src_addr_len = sizeof(src_addr); #endif /* MINIUPNPC_GET_SRC_ADDR */ @@ -80,7 +74,7 @@ receivedata(int socket, return 0; } #endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ -#if MINIUPNPC_GET_SRC_ADDR +#ifdef MINIUPNPC_GET_SRC_ADDR memset(&src_addr, 0, sizeof(src_addr)); n = recvfrom(socket, data, length, 0, (struct sockaddr *)&src_addr, &src_addr_len); @@ -90,7 +84,7 @@ receivedata(int socket, if(n<0) { PRINT_SOCKET_ERROR("recv"); } -#if MINIUPNPC_GET_SRC_ADDR +#ifdef MINIUPNPC_GET_SRC_ADDR if (src_addr.ss_family == AF_INET6) { const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr; #ifdef DEBUG @@ -98,7 +92,13 @@ receivedata(int socket, #endif /* DEBUG */ if(scope_id) *scope_id = src_addr6->sin6_scope_id; + } else { + if(scope_id) + *scope_id = 0; } +#else /* MINIUPNPC_GET_SRC_ADDR */ + if(scope_id) + *scope_id = 0; #endif /* MINIUPNPC_GET_SRC_ADDR */ return n; } diff --git a/Externals/miniupnpc/src/receivedata.h b/Externals/miniupnpc/src/receivedata.h index cb85c33177..c9fdc561f8 100644 --- a/Externals/miniupnpc/src/receivedata.h +++ b/Externals/miniupnpc/src/receivedata.h @@ -2,16 +2,18 @@ /* Project: miniupnp * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * Author: Thomas Bernard - * Copyright (c) 2011-2012 Thomas Bernard + * Copyright (c) 2011-2018 Thomas Bernard * This software is subjects to the conditions detailed * in the LICENCE file provided within this distribution */ #ifndef RECEIVEDATA_H_INCLUDED #define RECEIVEDATA_H_INCLUDED +#include "miniupnpc_socketdef.h" + /* Reads data from the specified socket. * Returns the number of bytes read if successful, zero if no bytes were * read or if we timed out. Returns negative if there was an error. */ -int receivedata(int socket, +int receivedata(SOCKET socket, char * data, int length, int timeout, unsigned int * scope_id); diff --git a/Externals/miniupnpc/src/upnpc.c b/Externals/miniupnpc/src/upnpc.c new file mode 100644 index 0000000000..af287c9be6 --- /dev/null +++ b/Externals/miniupnpc/src/upnpc.c @@ -0,0 +1,944 @@ +/* $Id: upnpc.c,v 1.146 2025/01/10 23:02:54 nanard Exp $ */ +/* Project : miniupnp + * Author : Thomas Bernard + * Copyright (c) 2005-2025 Thomas Bernard + * This software is subject to the conditions detailed in the + * LICENCE file provided in this distribution. */ + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include "win32_snprintf.h" +#else +/* for IPPROTO_TCP / IPPROTO_UDP */ +#include +#endif +#include +#include "miniwget.h" +#include "miniupnpc.h" +#include "upnpcommands.h" +#include "portlistingparse.h" +#include "upnperrors.h" +#include "miniupnpcstrings.h" + +/* protofix() checks if protocol is "UDP" or "TCP" + * returns NULL if not */ +const char * protofix(const char * proto) +{ + static const char proto_tcp[4] = { 'T', 'C', 'P', 0}; + static const char proto_udp[4] = { 'U', 'D', 'P', 0}; + int i, b; + for(i=0, b=1; i<4; i++) + b = b && ( (proto[i] == proto_tcp[i]) + || (proto[i] == (proto_tcp[i] | 32)) ); + if(b) + return proto_tcp; + for(i=0, b=1; i<4; i++) + b = b && ( (proto[i] == proto_udp[i]) + || (proto[i] == (proto_udp[i] | 32)) ); + if(b) + return proto_udp; + return 0; +} + +/* is_int() checks if parameter is an integer or not + * 1 for integer + * 0 for not an integer */ +int is_int(char const* s) +{ + if(s == NULL) + return 0; + while(*s) { + /* #define isdigit(c) ((c) >= '0' && (c) <= '9') */ + if(!isdigit(*s)) + return 0; + s++; + } + return 1; +} + +static void DisplayInfos(struct UPNPUrls * urls, + struct IGDdatas * data) +{ + char externalIPAddress[40]; + char connectionType[64]; + char status[64]; + char lastconnerr[64]; + unsigned int uptime = 0; + unsigned int brUp, brDown; + time_t timenow, timestarted; + int r; + if(UPNP_GetConnectionTypeInfo(urls->controlURL, + data->first.servicetype, + connectionType) != UPNPCOMMAND_SUCCESS) + printf("GetConnectionTypeInfo failed.\n"); + else + printf("Connection Type : %s\n", connectionType); + if(UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype, + status, &uptime, lastconnerr) != UPNPCOMMAND_SUCCESS) + printf("GetStatusInfo failed.\n"); + else + printf("Status : %s, uptime=%us, LastConnectionError : %s\n", + status, uptime, lastconnerr); + if(uptime > 0) { + timenow = time(NULL); + timestarted = timenow - uptime; + printf(" Time started : %s", ctime(×tarted)); + } + if(UPNP_GetLinkLayerMaxBitRates(urls->controlURL_CIF, data->CIF.servicetype, + &brDown, &brUp) != UPNPCOMMAND_SUCCESS) { + printf("GetLinkLayerMaxBitRates failed.\n"); + } else { + printf("MaxBitRateDown : %u bps", brDown); + if(brDown >= 1000000) { + printf(" (%u.%u Mbps)", brDown / 1000000, (brDown / 100000) % 10); + } else if(brDown >= 1000) { + printf(" (%u Kbps)", brDown / 1000); + } + printf(" MaxBitRateUp %u bps", brUp); + if(brUp >= 1000000) { + printf(" (%u.%u Mbps)", brUp / 1000000, (brUp / 100000) % 10); + } else if(brUp >= 1000) { + printf(" (%u Kbps)", brUp / 1000); + } + printf("\n"); + } + r = UPNP_GetExternalIPAddress(urls->controlURL, + data->first.servicetype, + externalIPAddress); + if(r != UPNPCOMMAND_SUCCESS) { + printf("GetExternalIPAddress failed. (errorcode=%d)\n", r); + } else if(!externalIPAddress[0]) { + printf("GetExternalIPAddress failed. (empty string)\n"); + } else { + printf("ExternalIPAddress = %s\n", externalIPAddress); + } +} + +static void GetConnectionStatus(struct UPNPUrls * urls, + struct IGDdatas * data) +{ + unsigned int bytessent, bytesreceived, packetsreceived, packetssent; + DisplayInfos(urls, data); + bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype); + bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype); + packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype); + packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype); + printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived); + printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived); +} + +static void ListRedirections(struct UPNPUrls * urls, + struct IGDdatas * data) +{ + int r; + unsigned short i = 0; + char index[6]; + char intClient[40]; + char intPort[6]; + char extPort[6]; + char protocol[4]; + char desc[80]; + char enabled[6]; + char rHost[64]; + char duration[16]; + /*unsigned int num=0; + UPNP_GetPortMappingNumberOfEntries(urls->controlURL, data->servicetype, &num); + printf("PortMappingNumberOfEntries : %u\n", num);*/ + printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n"); + do { + snprintf(index, 6, "%hu", i); + rHost[0] = '\0'; enabled[0] = '\0'; + duration[0] = '\0'; desc[0] = '\0'; + extPort[0] = '\0'; intPort[0] = '\0'; intClient[0] = '\0'; + r = UPNP_GetGenericPortMappingEntry(urls->controlURL, + data->first.servicetype, + index, + extPort, intClient, intPort, + protocol, desc, enabled, + rHost, duration); + if(r==0) + /* + printf("%02hu - %s %s->%s:%s\tenabled=%s leaseDuration=%s\n" + " desc='%s' rHost='%s'\n", + i, protocol, extPort, intClient, intPort, + enabled, duration, + desc, rHost); + */ + printf("%2hu %s %5s->%s:%-5s '%s' '%s' %s\n", + i, protocol, extPort, intClient, intPort, + desc, rHost, duration); + else if(r==713) /* ignore SpecifiedArrayIndexInvalid => we are at the end of the list */ + break; + else + printf("GetGenericPortMappingEntry() returned %d (%s)\n", + r, strupnperror(r)); + } while(r == 0 && i++ < 65535); +} + +static void NewListRedirections(struct UPNPUrls * urls, + struct IGDdatas * data) +{ + int r; + int i = 0; + struct PortMappingParserData pdata; + struct PortMapping * pm; + + memset(&pdata, 0, sizeof(struct PortMappingParserData)); + r = UPNP_GetListOfPortMappings(urls->controlURL, + data->first.servicetype, + "1", + "65535", + "TCP", + "1000", + &pdata); + if(r == UPNPCOMMAND_SUCCESS) + { + printf(" i protocol exPort->inAddr:inPort description remoteHost leaseTime\n"); + for(pm = pdata.l_head; pm != NULL; pm = pm->l_next) + { + printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n", + i, pm->protocol, pm->externalPort, pm->internalClient, + pm->internalPort, + pm->description, pm->remoteHost, + (unsigned)pm->leaseTime); + i++; + } + FreePortListing(&pdata); + } + else + { + printf("GetListOfPortMappings() returned %d (%s)\n", + r, strupnperror(r)); + } + r = UPNP_GetListOfPortMappings(urls->controlURL, + data->first.servicetype, + "1", + "65535", + "UDP", + "1000", + &pdata); + if(r == UPNPCOMMAND_SUCCESS) + { + for(pm = pdata.l_head; pm != NULL; pm = pm->l_next) + { + printf("%2d %s %5hu->%s:%-5hu '%s' '%s' %u\n", + i, pm->protocol, pm->externalPort, pm->internalClient, + pm->internalPort, + pm->description, pm->remoteHost, + (unsigned)pm->leaseTime); + i++; + } + FreePortListing(&pdata); + } + else + { + printf("GetListOfPortMappings() returned %d (%s)\n", + r, strupnperror(r)); + } +} + +/* Test function + * 1 - get connection type + * 2 - get extenal ip address + * 3 - Add port mapping + * 4 - get this port mapping from the IGD */ +static int SetRedirectAndTest(struct UPNPUrls * urls, + struct IGDdatas * data, + const char * iaddr, + const char * iport, + const char * eport, + const char * proto, + const char * leaseDuration, + const char * remoteHost, + const char * description, + int addAny) +{ + char externalIPAddress[40]; + char intClient[40]; + char intPort[6]; + char reservedPort[6]; + char duration[16]; + int r; + + if(!iaddr || !iport || !eport || !proto) + { + fprintf(stderr, "Wrong arguments\n"); + return -1; + } + proto = protofix(proto); + if(!proto) + { + fprintf(stderr, "invalid protocol\n"); + return -1; + } + + r = UPNP_GetExternalIPAddress(urls->controlURL, + data->first.servicetype, + externalIPAddress); + if(r!=UPNPCOMMAND_SUCCESS) + printf("GetExternalIPAddress failed.\n"); + else + printf("ExternalIPAddress = %s\n", externalIPAddress); + + if (addAny) { + r = UPNP_AddAnyPortMapping(urls->controlURL, data->first.servicetype, + eport, iport, iaddr, description, + proto, remoteHost, leaseDuration, reservedPort); + if(r==UPNPCOMMAND_SUCCESS) + eport = reservedPort; + else + printf("AddAnyPortMapping(%s, %s, %s) failed with code %d (%s)\n", + eport, iport, iaddr, r, strupnperror(r)); + } else { + r = UPNP_AddPortMapping(urls->controlURL, data->first.servicetype, + eport, iport, iaddr, description, + proto, remoteHost, leaseDuration); + if(r!=UPNPCOMMAND_SUCCESS) { + printf("AddPortMapping(%s, %s, %s) failed with code %d (%s)\n", + eport, iport, iaddr, r, strupnperror(r)); + return -2; + } + } + + r = UPNP_GetSpecificPortMappingEntry(urls->controlURL, + data->first.servicetype, + eport, proto, remoteHost, + intClient, intPort, NULL/*desc*/, + NULL/*enabled*/, duration); + if(r!=UPNPCOMMAND_SUCCESS) { + printf("GetSpecificPortMappingEntry() failed with code %d (%s)\n", + r, strupnperror(r)); + return -2; + } else { + printf("InternalIP:Port = %s:%s\n", intClient, intPort); + printf("external %s:%s %s is redirected to internal %s:%s (duration=%s)\n", + externalIPAddress, eport, proto, intClient, intPort, duration); + } + return 0; +} + +static int +RemoveRedirect(struct UPNPUrls * urls, + struct IGDdatas * data, + const char * eport, + const char * proto, + const char * remoteHost) +{ + int r; + if(!proto || !eport) + { + fprintf(stderr, "invalid arguments\n"); + return -1; + } + proto = protofix(proto); + if(!proto) + { + fprintf(stderr, "protocol invalid\n"); + return -1; + } + r = UPNP_DeletePortMapping(urls->controlURL, data->first.servicetype, eport, proto, remoteHost); + if(r!=UPNPCOMMAND_SUCCESS) { + printf("UPNP_DeletePortMapping() failed with code : %d\n", r); + return -2; + }else { + printf("UPNP_DeletePortMapping() returned : %d\n", r); + } + return 0; +} + +static int +RemoveRedirectRange(struct UPNPUrls * urls, + struct IGDdatas * data, + const char * ePortStart, char const * ePortEnd, + const char * proto, const char * manage) +{ + int r; + + if (!manage) + manage = "0"; + + if(!proto || !ePortStart || !ePortEnd) + { + fprintf(stderr, "invalid arguments\n"); + return -1; + } + proto = protofix(proto); + if(!proto) + { + fprintf(stderr, "protocol invalid\n"); + return -1; + } + r = UPNP_DeletePortMappingRange(urls->controlURL, data->first.servicetype, ePortStart, ePortEnd, proto, manage); + if(r!=UPNPCOMMAND_SUCCESS) { + printf("UPNP_DeletePortMappingRange() failed with code : %d\n", r); + return -2; + }else { + printf("UPNP_DeletePortMappingRange() returned : %d\n", r); + } + return 0; +} + +/* IGD:2, functions for service WANIPv6FirewallControl:1 */ +static void GetFirewallStatus(struct UPNPUrls * urls, struct IGDdatas * data) +{ + unsigned int bytessent, bytesreceived, packetsreceived, packetssent; + int firewallEnabled = 0, inboundPinholeAllowed = 0; + + UPNP_GetFirewallStatus(urls->controlURL_6FC, data->IPv6FC.servicetype, &firewallEnabled, &inboundPinholeAllowed); + printf("FirewallEnabled: %d & Inbound Pinhole Allowed: %d\n", firewallEnabled, inboundPinholeAllowed); + printf("GetFirewallStatus:\n Firewall Enabled: %s\n Inbound Pinhole Allowed: %s\n", (firewallEnabled)? "Yes":"No", (inboundPinholeAllowed)? "Yes":"No"); + + bytessent = UPNP_GetTotalBytesSent(urls->controlURL_CIF, data->CIF.servicetype); + bytesreceived = UPNP_GetTotalBytesReceived(urls->controlURL_CIF, data->CIF.servicetype); + packetssent = UPNP_GetTotalPacketsSent(urls->controlURL_CIF, data->CIF.servicetype); + packetsreceived = UPNP_GetTotalPacketsReceived(urls->controlURL_CIF, data->CIF.servicetype); + printf("Bytes: Sent: %8u\tRecv: %8u\n", bytessent, bytesreceived); + printf("Packets: Sent: %8u\tRecv: %8u\n", packetssent, packetsreceived); +} + +/* Test function + * 1 - Add pinhole + * 2 - Check if pinhole is working from the IGD side */ +static int SetPinholeAndTest(struct UPNPUrls * urls, struct IGDdatas * data, + const char * remoteaddr, const char * eport, + const char * intaddr, const char * iport, + const char * proto, const char * lease_time) +{ + char uniqueID[8]; + /*int isWorking = 0;*/ + int r; + char proto_tmp[8]; + + if(!intaddr || !remoteaddr || !iport || !eport || !proto || !lease_time) + { + fprintf(stderr, "Wrong arguments\n"); + return -1; + } + if(atoi(proto) == 0) + { + const char * protocol; + protocol = protofix(proto); + if(protocol && (strcmp("TCP", protocol) == 0)) + { + snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_TCP); + proto = proto_tmp; + } + else if(protocol && (strcmp("UDP", protocol) == 0)) + { + snprintf(proto_tmp, sizeof(proto_tmp), "%d", IPPROTO_UDP); + proto = proto_tmp; + } + else + { + fprintf(stderr, "invalid protocol\n"); + return -1; + } + } + r = UPNP_AddPinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, lease_time, uniqueID); + if(r!=UPNPCOMMAND_SUCCESS) + { + printf("AddPinhole([%s]:%s -> [%s]:%s) failed with code %d (%s)\n", + remoteaddr, eport, intaddr, iport, r, strupnperror(r)); + return -2; + } + else + { + printf("AddPinhole: ([%s]:%s -> [%s]:%s) / Pinhole ID = %s\n", + remoteaddr, eport, intaddr, iport, uniqueID); + /*r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->servicetype_6FC, uniqueID, &isWorking); + if(r!=UPNPCOMMAND_SUCCESS) + printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r)); + printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No");*/ + } + return 0; +} + +/* Test function + * 1 - Check if pinhole is working from the IGD side + * 2 - Update pinhole */ +static void GetPinholeAndUpdate(struct UPNPUrls * urls, struct IGDdatas * data, + const char * uniqueID, const char * lease_time) +{ + int isWorking = 0; + int r; + + if(!uniqueID || !lease_time) + { + fprintf(stderr, "Wrong arguments\n"); + return; + } + /* CheckPinholeWorking is an Optional Action, error 602 should be + * returned if it is not implemented */ + r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking); + if(r==UPNPCOMMAND_SUCCESS) + printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No"); + else + printf("CheckPinholeWorking(%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r)); + /* 702 FirewallDisabled Firewall is disabled and this action is disabled + * 703 InboundPinholeNotAllowed Creation of inbound pinholes by UPnP CPs + * are not allowed and this action is disabled + * 704 NoSuchEntry There is no pinhole with the specified UniqueID. + * 709 NoTrafficReceived No traffic corresponding to this pinhole has + * been received by the gateway. */ + if(isWorking || (r!=702 && r!=703 && r!=704)) + { + r = UPNP_UpdatePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, lease_time); + printf("UpdatePinhole: Pinhole ID = %s with Lease Time: %s\n", uniqueID, lease_time); + if(r!=UPNPCOMMAND_SUCCESS) + printf("UpdatePinhole: ID (%s) failed with code %d (%s)\n", uniqueID, r, strupnperror(r)); + } +} + +/* Test function + * Get pinhole timeout + */ +static void GetPinholeOutboundTimeout(struct UPNPUrls * urls, struct IGDdatas * data, + const char * remoteaddr, const char * eport, + const char * intaddr, const char * iport, + const char * proto) +{ + int timeout = 0; + int r; + + if(!intaddr || !remoteaddr || !iport || !eport || !proto) + { + fprintf(stderr, "Wrong arguments\n"); + return; + } + + r = UPNP_GetOutboundPinholeTimeout(urls->controlURL_6FC, data->IPv6FC.servicetype, remoteaddr, eport, intaddr, iport, proto, &timeout); + if(r!=UPNPCOMMAND_SUCCESS) + printf("GetOutboundPinholeTimeout([%s]:%s -> [%s]:%s) failed with code %d (%s)\n", + intaddr, iport, remoteaddr, eport, r, strupnperror(r)); + else + printf("GetOutboundPinholeTimeout: ([%s]:%s -> [%s]:%s) / Timeout = %d\n", intaddr, iport, remoteaddr, eport, timeout); +} + +static void +GetPinholePackets(struct UPNPUrls * urls, + struct IGDdatas * data, const char * uniqueID) +{ + int r, pinholePackets = 0; + if(!uniqueID) + { + fprintf(stderr, "invalid arguments\n"); + return; + } + r = UPNP_GetPinholePackets(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &pinholePackets); + if(r!=UPNPCOMMAND_SUCCESS) + printf("GetPinholePackets() failed with code %d (%s)\n", r, strupnperror(r)); + else + printf("GetPinholePackets: Pinhole ID = %s / PinholePackets = %d\n", uniqueID, pinholePackets); +} + +static void +CheckPinhole(struct UPNPUrls * urls, + struct IGDdatas * data, const char * uniqueID) +{ + int r, isWorking = 0; + if(!uniqueID) + { + fprintf(stderr, "invalid arguments\n"); + return; + } + r = UPNP_CheckPinholeWorking(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID, &isWorking); + if(r!=UPNPCOMMAND_SUCCESS) + printf("CheckPinholeWorking() failed with code %d (%s)\n", r, strupnperror(r)); + else + printf("CheckPinholeWorking: Pinhole ID = %s / IsWorking = %s\n", uniqueID, (isWorking)? "Yes":"No"); +} + +static int +RemovePinhole(struct UPNPUrls * urls, + struct IGDdatas * data, const char * uniqueID) +{ + int r; + if(!uniqueID) + { + fprintf(stderr, "invalid arguments\n"); + return -1; + } + r = UPNP_DeletePinhole(urls->controlURL_6FC, data->IPv6FC.servicetype, uniqueID); + printf("UPNP_DeletePinhole() returned : %d\n", r); + return r; +} + +static void usage(FILE * out, const char * argv0) { + fprintf(out, "Usage:\n"); + fprintf(out, " %s [options] -a ip port external_port protocol [duration] [remote host]\n Add port mapping\n", argv0); + fprintf(out, " %s [options] -r port1 [external_port1] protocol1 [port2 [external_port2] protocol2] [...]\n Add multiple port mappings to the current host\n", argv0); + fprintf(out, " %s [options] -d external_port protocol [remote host]\n Delete port redirection\n", argv0); + fprintf(out, " %s [options] -f external_port1 protocol1 [external_port2 protocol2] [...]\n Delete multiple port redirections\n", argv0); + fprintf(out, " %s [options] -s\n Get Connection status\n", argv0); + fprintf(out, " %s [options] -l\n List redirections\n", argv0); + fprintf(out, " %s [options] -L\n List redirections (using GetListOfPortMappings (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -n ip port external_port protocol [duration] [remote host]\n Add (any) port mapping allowing IGD to use alternative external_port (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -N external_port_start external_port_end protocol [manage]\n Delete range of port mappings (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -A remote_ip remote_port internal_ip internal_port protocol lease_time\n Add Pinhole (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -U uniqueID new_lease_time\n Update Pinhole (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -C uniqueID\n Check if Pinhole is Working (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -K uniqueID\n Get Number of packets going through the rule (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -D uniqueID\n Delete Pinhole (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -S\n Get Firewall status (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -G remote_ip remote_port internal_ip internal_port protocol\n Get Outbound Pinhole Timeout (for IGD:2 only)\n", argv0); + fprintf(out, " %s [options] -P\n Get Presentation URL\n", argv0); + fprintf(out, "\nNotes:\n"); + fprintf(out, " protocol is UDP or TCP.\n"); + fprintf(out, " Use \"\" for any remote_host and 0 for any remote_port.\n"); + fprintf(out, " @ can be used in option -a, -n, -A and -G to represent local LAN address.\n"); + fprintf(out, "\nOptions:\n"); + fprintf(out, " -e description : set description for port mapping.\n"); + fprintf(out, " -6 : use IPv6 instead of IPv4.\n"); + fprintf(out, " -u URL : bypass discovery process by providing the XML root description URL.\n"); + fprintf(out, " -m address/interface : provide IPv4 address or interface name (IPv4 or IPv6) to use for sending SSDP multicast packets.\n"); + fprintf(out, " -z localport : SSDP packets local (source) port (1024-65535).\n"); + fprintf(out, " -p path : use this path for MiniSSDPd socket.\n"); + fprintf(out, " -t ttl : set multicast TTL. Default value is 2.\n"); + fprintf(out, " -i : ignore errors and try to use also disconnected IGD or non-IGD device.\n"); +} + +/* sample upnp client program */ +int main(int argc, char ** argv) +{ + char command = 0; + char ** commandargv = 0; + int commandargc = 0; + struct UPNPDev * devlist = 0; + char lanaddr[64] = "unset"; /* my ip address on the LAN */ + char wanaddr[64] = "unset"; /* up address of the IGD on the WAN */ + int i; + const char * rootdescurl = 0; + const char * multicastif = 0; + const char * minissdpdpath = 0; + int localport = UPNP_LOCAL_PORT_ANY; + int retcode = 0; + int error = 0; + int ipv6 = 0; + int ignore = 0; + unsigned char ttl = 2; /* defaulting to 2 */ + const char * description = 0; + +#ifdef _WIN32 + WSADATA wsaData; + int nResult = WSAStartup(MAKEWORD(2,2), &wsaData); + if(nResult != NO_ERROR) + { + fprintf(stderr, "WSAStartup() failed.\n"); + return -1; + } +#endif + printf("upnpc: miniupnpc library test client, version %s.\n", MINIUPNPC_VERSION_STRING); + printf(" (c) 2005-2025 Thomas Bernard.\n"); + printf("More information at https://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/\n\n"); + + /* command line processing */ + for(i=1; i65535 || + (localport >1 && localport < 1024)) + { + fprintf(stderr, "Invalid localport '%s'\n", argv[i]); + localport = UPNP_LOCAL_PORT_ANY; + break; + } + } + else if(argv[i][1] == 'p') + minissdpdpath = argv[++i]; + else if(argv[i][1] == '6') + ipv6 = 1; + else if(argv[i][1] == 'e') + description = argv[++i]; + else if(argv[i][1] == 't') + ttl = (unsigned char)atoi(argv[++i]); + else if(argv[i][1] == 'i') + ignore = 1; + else + { + command = argv[i][1]; + i++; + commandargv = argv + i; + commandargc = argc - i; + break; + } + } + else + { + fprintf(stderr, "option '%s' invalid\n", argv[i]); + } + } + + if(!command + || (command == 'a' && commandargc<4) + || (command == 'r' && commandargc<2) + || (command == 'A' && commandargc<6) + || (command == 'd' && commandargc<2) + || (command == 'D' && commandargc<1) + || (command == 'n' && commandargc<4) + || (command == 'N' && commandargc<3) + || (command == 'U' && commandargc<2) + || (command == 'K' && commandargc<1) + || (command == 'C' && commandargc<1) + || (command == 'G' && commandargc<5)) + { + fprintf(stderr, "Command line argument error.\n\n"); + usage(stderr, argv[0]); + return 1; + } + + if(ipv6 == 0 && (command == 'A' || command == 'D' || command == 'U' || command == 'K' || command == 'C' || command == 'G')) { + printf("Use IPv6 (option -6) GUA address to ensure UPnP IGDv2 pinholes are allowed\n\n"); + } + + if( rootdescurl + || (devlist = upnpDiscover(2000, multicastif, minissdpdpath, + localport, ipv6, ttl, &error))) + { + struct UPNPDev * device; + struct UPNPUrls urls; + struct IGDdatas data; + if(devlist) + { + printf("List of UPNP devices found on the network :\n"); + for(device = devlist; device; device = device->pNext) + { + printf(" desc: %s\n st: %s\n\n", + device->descURL, device->st); + } + } + else if(!rootdescurl) + { + printf("upnpDiscover() error code=%d\n", error); + } + i = 1; + if( (rootdescurl && UPNP_GetIGDFromUrl(rootdescurl, &urls, &data, lanaddr, sizeof(lanaddr))) + || (i = UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), wanaddr, sizeof(wanaddr)))) + { + switch(i) { + case UPNP_CONNECTED_IGD: + printf("Found valid IGD : %s\n", urls.controlURL); + break; + case UPNP_PRIVATEIP_IGD: + printf("Found an IGD with a reserved IP address (%s) : %s\n", wanaddr, urls.controlURL); + break; + case UPNP_DISCONNECTED_IGD: + printf("Found a (not connected?) IGD : %s\n", urls.controlURL); + if (ignore) printf("Trying to continue anyway\n"); + break; + case UPNP_UNKNOWN_DEVICE: + printf("UPnP device found. Is it an IGD ? : %s\n", urls.controlURL); + if (ignore) printf("Trying to continue anyway\n"); + break; + default: + printf("Found device (igd ?) : %s\n", urls.controlURL); + if (ignore) printf("Trying to continue anyway\n"); + } + if(i==UPNP_CONNECTED_IGD || i==UPNP_PRIVATEIP_IGD || ignore) { + + printf("Local LAN ip address : %s\n", lanaddr); + #if 0 + printf("getting \"%s\"\n", urls.ipcondescURL); + descXML = miniwget(urls.ipcondescURL, &descXMLsize); + if(descXML) + { + /*fwrite(descXML, 1, descXMLsize, stdout);*/ + free(descXML); descXML = NULL; + } + #endif + + /* replace '@' with the local LAN ip address */ + if ((command == 'a' || command == 'n') && 0 == strcmp(commandargv[0], "@")) + commandargv[0] = lanaddr; + else if ((command == 'A' || command == 'G') && 0 == strcmp(commandargv[2], "@")) + commandargv[2] = lanaddr; + + switch(command) + { + case 'l': + DisplayInfos(&urls, &data); + ListRedirections(&urls, &data); + break; + case 'L': + NewListRedirections(&urls, &data); + break; + case 'a': + if (SetRedirectAndTest(&urls, &data, + commandargv[0], commandargv[1], + commandargv[2], commandargv[3], + (commandargc > 4) && is_int(commandargv[4]) ? commandargv[4] : "0", + (commandargc > 4) && !is_int(commandargv[4]) ? commandargv[4] : (commandargc > 5) ? commandargv[5] : NULL, + description, 0) < 0) + retcode = 2; + break; + case 'd': + if (RemoveRedirect(&urls, &data, commandargv[0], commandargv[1], + commandargc > 2 ? commandargv[2] : NULL) < 0) + retcode = 2; + break; + case 'n': /* aNy */ + if (SetRedirectAndTest(&urls, &data, + commandargv[0], commandargv[1], + commandargv[2], commandargv[3], + (commandargc > 4) && is_int(commandargv[4]) ? commandargv[4] : "0", + (commandargc > 4) && !is_int(commandargv[4]) ? commandargv[4] : (commandargc > 5) ? commandargv[5] : NULL, + description, 1) < 0) + retcode = 2; + break; + case 'N': + if (commandargc < 3) + fprintf(stderr, "too few arguments\n"); + + if (RemoveRedirectRange(&urls, &data, commandargv[0], commandargv[1], commandargv[2], + commandargc > 3 ? commandargv[3] : NULL) < 0) + retcode = 2; + break; + case 's': + GetConnectionStatus(&urls, &data); + break; + case 'r': + i = 0; + while(i */ + if (SetRedirectAndTest(&urls, &data, + lanaddr, commandargv[i], + commandargv[i+1], commandargv[i+2], "0", NULL, + description, 0) < 0) + retcode = 2; + i+=3; /* 3 parameters parsed */ + } else { + /* 2nd parameter not an integer : */ + if (SetRedirectAndTest(&urls, &data, + lanaddr, commandargv[i], + commandargv[i], commandargv[i+1], "0", NULL, + description, 0) < 0) + retcode = 2; + i+=2; /* 2 parameters parsed */ + } + } + break; + case 'f': + i = 0; + while(i */ + if (RemoveRedirect(&urls, &data, + commandargv[i], commandargv[i+1], NULL) < 0) + retcode = 3; + i+=2; /* 2 parameters parsed */ + } + } + break; + case 'A': + if (SetPinholeAndTest(&urls, &data, + commandargv[0], commandargv[1], + commandargv[2], commandargv[3], + commandargv[4], commandargv[5]) < 0) + retcode = 2; + break; + case 'U': + GetPinholeAndUpdate(&urls, &data, + commandargv[0], commandargv[1]); + break; + case 'C': + for(i=0; i= 199901L +#define STRTOUI strtoull +#else +#define STRTOUI strtoul +#endif static UNSIGNED_INTEGER my_atoui(const char * s) @@ -29,13 +42,13 @@ UPNP_GetTotalBytesSent(const char * controlURL, int bufsize; unsigned int r = 0; char * p; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, + if(!(buffer = simpleUPnPcommand(controlURL, servicetype, "GetTotalBytesSent", 0, &bufsize))) { - return UPNPCOMMAND_HTTP_ERROR; + return (UNSIGNED_INTEGER)UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewTotalBytesSent"); r = my_atoui(p); ClearNameValueList(&pdata); @@ -53,13 +66,13 @@ UPNP_GetTotalBytesReceived(const char * controlURL, int bufsize; unsigned int r = 0; char * p; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, + if(!(buffer = simpleUPnPcommand(controlURL, servicetype, "GetTotalBytesReceived", 0, &bufsize))) { - return UPNPCOMMAND_HTTP_ERROR; + return (UNSIGNED_INTEGER)UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewTotalBytesReceived"); r = my_atoui(p); ClearNameValueList(&pdata); @@ -77,13 +90,13 @@ UPNP_GetTotalPacketsSent(const char * controlURL, int bufsize; unsigned int r = 0; char * p; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, + if(!(buffer = simpleUPnPcommand(controlURL, servicetype, "GetTotalPacketsSent", 0, &bufsize))) { - return UPNPCOMMAND_HTTP_ERROR; + return (UNSIGNED_INTEGER)UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewTotalPacketsSent"); r = my_atoui(p); ClearNameValueList(&pdata); @@ -101,13 +114,13 @@ UPNP_GetTotalPacketsReceived(const char * controlURL, int bufsize; unsigned int r = 0; char * p; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, + if(!(buffer = simpleUPnPcommand(controlURL, servicetype, "GetTotalPacketsReceived", 0, &bufsize))) { - return UPNPCOMMAND_HTTP_ERROR; + return (UNSIGNED_INTEGER)UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewTotalPacketsReceived"); r = my_atoui(p); ClearNameValueList(&pdata); @@ -134,13 +147,13 @@ UPNP_GetStatusInfo(const char * controlURL, if(!status && !uptime) return UPNPCOMMAND_INVALID_ARGS; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, + if(!(buffer = simpleUPnPcommand(controlURL, servicetype, "GetStatusInfo", 0, &bufsize))) { return UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); /*DisplayNameValueList(buffer, bufsize);*/ - free(buffer); buffer = NULL; + free(buffer); up = GetValueFromNameValueList(&pdata, "NewUptime"); p = GetValueFromNameValueList(&pdata, "NewConnectionStatus"); err = GetValueFromNameValueList(&pdata, "NewLastConnectionError"); @@ -156,9 +169,7 @@ UPNP_GetStatusInfo(const char * controlURL, } if(uptime) { - if(up) - sscanf(up,"%u",uptime); - else + if(!up || sscanf(up,"%u",uptime) != 1) *uptime = 0; } @@ -172,8 +183,8 @@ UPNP_GetStatusInfo(const char * controlURL, p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); return ret; @@ -195,12 +206,12 @@ UPNP_GetConnectionTypeInfo(const char * controlURL, if(!connectionType) return UPNPCOMMAND_INVALID_ARGS; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, + if(!(buffer = simpleUPnPcommand(controlURL, servicetype, "GetConnectionTypeInfo", 0, &bufsize))) { return UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewConnectionType"); /*p = GetValueFromNameValueList(&pdata, "NewPossibleConnectionTypes");*/ /* PossibleConnectionTypes will have several values.... */ @@ -212,8 +223,8 @@ UPNP_GetConnectionTypeInfo(const char * controlURL, connectionType[0] = '\0'; p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); return ret; @@ -242,14 +253,14 @@ UPNP_GetLinkLayerMaxBitRates(const char * controlURL, return UPNPCOMMAND_INVALID_ARGS; /* shouldn't we use GetCommonLinkProperties ? */ - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, + if(!(buffer = simpleUPnPcommand(controlURL, servicetype, "GetCommonLinkProperties", 0, &bufsize))) { /*"GetLinkLayerMaxBitRates", 0, &bufsize);*/ return UPNPCOMMAND_HTTP_ERROR; } /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); /*down = GetValueFromNameValueList(&pdata, "NewDownstreamMaxBitRate");*/ /*up = GetValueFromNameValueList(&pdata, "NewUpstreamMaxBitRate");*/ down = GetValueFromNameValueList(&pdata, "NewLayer1DownstreamMaxBitRate"); @@ -260,22 +271,18 @@ UPNP_GetLinkLayerMaxBitRates(const char * controlURL, ret = UPNPCOMMAND_SUCCESS; if(bitrateDown) { - if(down) - sscanf(down,"%u",bitrateDown); - else + if(!down || sscanf(down,"%u",bitrateDown) != 1) *bitrateDown = 0; } if(bitrateUp) { - if(up) - sscanf(up,"%u",bitrateUp); - else + if(!up || sscanf(up,"%u",bitrateUp) != 1) *bitrateUp = 0; } p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); return ret; @@ -307,13 +314,13 @@ UPNP_GetExternalIPAddress(const char * controlURL, if(!extIpAdd || !controlURL || !servicetype) return UPNPCOMMAND_INVALID_ARGS; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, + if(!(buffer = simpleUPnPcommand(controlURL, servicetype, "GetExternalIPAddress", 0, &bufsize))) { return UPNPCOMMAND_HTTP_ERROR; } /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); /*printf("external ip = %s\n", GetValueFromNameValueList(&pdata, "NewExternalIPAddress") );*/ p = GetValueFromNameValueList(&pdata, "NewExternalIPAddress"); if(p) { @@ -325,8 +332,8 @@ UPNP_GetExternalIPAddress(const char * controlURL, p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); @@ -343,7 +350,17 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype, const char * remoteHost, const char * leaseDuration) { - struct UPNParg * AddPortMappingArgs; + struct UPNParg AddPortMappingArgs[] = { + {"NewRemoteHost", remoteHost}, + {"NewExternalPort", extPort}, + {"NewProtocol", proto}, + {"NewInternalPort", inPort}, + {"NewInternalClient", inClient}, + {"NewEnabled", "1"}, + {"NewPortMappingDescription", desc?desc:"libminiupnpc"}, + {"NewLeaseDuration", leaseDuration?leaseDuration:"0"}, + {NULL, NULL} + }; char * buffer; int bufsize; struct NameValueParserData pdata; @@ -353,46 +370,26 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype, if(!inPort || !inClient || !proto || !extPort) return UPNPCOMMAND_INVALID_ARGS; - AddPortMappingArgs = calloc(9, sizeof(struct UPNParg)); - if(AddPortMappingArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - AddPortMappingArgs[0].elt = "NewRemoteHost"; - AddPortMappingArgs[0].val = remoteHost; - AddPortMappingArgs[1].elt = "NewExternalPort"; - AddPortMappingArgs[1].val = extPort; - AddPortMappingArgs[2].elt = "NewProtocol"; - AddPortMappingArgs[2].val = proto; - AddPortMappingArgs[3].elt = "NewInternalPort"; - AddPortMappingArgs[3].val = inPort; - AddPortMappingArgs[4].elt = "NewInternalClient"; - AddPortMappingArgs[4].val = inClient; - AddPortMappingArgs[5].elt = "NewEnabled"; - AddPortMappingArgs[5].val = "1"; - AddPortMappingArgs[6].elt = "NewPortMappingDescription"; - AddPortMappingArgs[6].val = desc?desc:"libminiupnpc"; - AddPortMappingArgs[7].elt = "NewLeaseDuration"; - AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0"; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "AddPortMapping", AddPortMappingArgs, - &bufsize))) { - free(AddPortMappingArgs); + buffer = simpleUPnPcommand(controlURL, servicetype, + "AddPortMapping", AddPortMappingArgs, + &bufsize); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } /*DisplayNameValueList(buffer, bufsize);*/ /*buffer[bufsize] = '\0';*/ /*puts(buffer);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { /*printf("AddPortMapping errorCode = '%s'\n", resVal); */ - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(resVal, "%d", &ret); + if(sscanf(resVal, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } else { ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(AddPortMappingArgs); return ret; } @@ -407,7 +404,17 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, const char * leaseDuration, char * reservedPort) { - struct UPNParg * AddPortMappingArgs; + struct UPNParg AddAnyPortMappingArgs[] = { + {"NewRemoteHost", remoteHost}, + {"NewExternalPort", extPort}, + {"NewProtocol", proto}, + {"NewInternalPort", inPort}, + {"NewInternalClient", inClient}, + {"NewEnabled", "1"}, + {"NewPortMappingDescription", desc?desc:"libminiupnpc"}, + {"NewLeaseDuration", leaseDuration?leaseDuration:"0"}, + {NULL, NULL} + }; char * buffer; int bufsize; struct NameValueParserData pdata; @@ -416,38 +423,18 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, if(!inPort || !inClient || !proto || !extPort) return UPNPCOMMAND_INVALID_ARGS; - - AddPortMappingArgs = calloc(9, sizeof(struct UPNParg)); - if(AddPortMappingArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - AddPortMappingArgs[0].elt = "NewRemoteHost"; - AddPortMappingArgs[0].val = remoteHost; - AddPortMappingArgs[1].elt = "NewExternalPort"; - AddPortMappingArgs[1].val = extPort; - AddPortMappingArgs[2].elt = "NewProtocol"; - AddPortMappingArgs[2].val = proto; - AddPortMappingArgs[3].elt = "NewInternalPort"; - AddPortMappingArgs[3].val = inPort; - AddPortMappingArgs[4].elt = "NewInternalClient"; - AddPortMappingArgs[4].val = inClient; - AddPortMappingArgs[5].elt = "NewEnabled"; - AddPortMappingArgs[5].val = "1"; - AddPortMappingArgs[6].elt = "NewPortMappingDescription"; - AddPortMappingArgs[6].val = desc?desc:"libminiupnpc"; - AddPortMappingArgs[7].elt = "NewLeaseDuration"; - AddPortMappingArgs[7].val = leaseDuration?leaseDuration:"0"; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "AddAnyPortMapping", AddPortMappingArgs, - &bufsize))) { - free(AddPortMappingArgs); + buffer = simpleUPnPcommand(controlURL, servicetype, + "AddAnyPortMapping", AddAnyPortMappingArgs, + &bufsize); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(resVal, "%d", &ret); + if(sscanf(resVal, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } else { char *p; @@ -461,7 +448,6 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, } } ClearNameValueList(&pdata); - free(AddPortMappingArgs); return ret; } @@ -471,7 +457,12 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, const char * remoteHost) { /*struct NameValueParserData pdata;*/ - struct UPNParg * DeletePortMappingArgs; + struct UPNParg DeletePortMappingArgs[] = { + {"NewRemoteHost", remoteHost}, + {"NewExternalPort", extPort}, + {"NewProtocol", proto}, + {NULL, NULL} + }; char * buffer; int bufsize; struct NameValueParserData pdata; @@ -481,43 +472,39 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, if(!extPort || !proto) return UPNPCOMMAND_INVALID_ARGS; - DeletePortMappingArgs = calloc(4, sizeof(struct UPNParg)); - if(DeletePortMappingArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - DeletePortMappingArgs[0].elt = "NewRemoteHost"; - DeletePortMappingArgs[0].val = remoteHost; - DeletePortMappingArgs[1].elt = "NewExternalPort"; - DeletePortMappingArgs[1].val = extPort; - DeletePortMappingArgs[2].elt = "NewProtocol"; - DeletePortMappingArgs[2].val = proto; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "DeletePortMapping", - DeletePortMappingArgs, &bufsize))) { - free(DeletePortMappingArgs); + buffer = simpleUPnPcommand(controlURL, servicetype, + "DeletePortMapping", + DeletePortMappingArgs, &bufsize); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(resVal, "%d", &ret); + if(sscanf(resVal, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } else { ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(DeletePortMappingArgs); return ret; } MINIUPNP_LIBSPEC int UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype, - const char * extPortStart, const char * extPortEnd, - const char * proto, - const char * manage) + const char * extPortStart, const char * extPortEnd, + const char * proto, + const char * manage) { - struct UPNParg * DeletePortMappingArgs; + struct UPNParg DeletePortMappingRangeArgs[] = { + {"NewStartPort", extPortStart}, + {"NewEndPort", extPortEnd}, + {"NewProtocol", proto}, + {"NewManage", manage}, + {NULL, NULL} + }; char * buffer; int bufsize; struct NameValueParserData pdata; @@ -527,35 +514,23 @@ UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype, if(!extPortStart || !extPortEnd || !proto || !manage) return UPNPCOMMAND_INVALID_ARGS; - DeletePortMappingArgs = calloc(5, sizeof(struct UPNParg)); - if(DeletePortMappingArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - DeletePortMappingArgs[0].elt = "NewStartPort"; - DeletePortMappingArgs[0].val = extPortStart; - DeletePortMappingArgs[1].elt = "NewEndPort"; - DeletePortMappingArgs[1].val = extPortEnd; - DeletePortMappingArgs[2].elt = "NewProtocol"; - DeletePortMappingArgs[2].val = proto; - DeletePortMappingArgs[3].elt = "NewManage"; - DeletePortMappingArgs[3].val = manage; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "DeletePortMappingRange", - DeletePortMappingArgs, &bufsize))) { - free(DeletePortMappingArgs); + buffer = simpleUPnPcommand(controlURL, servicetype, + "DeletePortMappingRange", + DeletePortMappingRangeArgs, &bufsize); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(resVal, "%d", &ret); + if(sscanf(resVal, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } else { ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(DeletePortMappingArgs); return ret; } @@ -573,28 +548,26 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL, char * duration) { struct NameValueParserData pdata; - struct UPNParg * GetPortMappingArgs; + struct UPNParg GetPortMappingArgs[] = { + {"NewPortMappingIndex", index}, + {NULL, NULL} + }; char * buffer; int bufsize; char * p; - int r = UPNPCOMMAND_UNKNOWN_ERROR; + int ret = UPNPCOMMAND_UNKNOWN_ERROR; if(!index) return UPNPCOMMAND_INVALID_ARGS; intClient[0] = '\0'; intPort[0] = '\0'; - GetPortMappingArgs = calloc(2, sizeof(struct UPNParg)); - if(GetPortMappingArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - GetPortMappingArgs[0].elt = "NewPortMappingIndex"; - GetPortMappingArgs[0].val = index; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "GetGenericPortMappingEntry", - GetPortMappingArgs, &bufsize))) { - free(GetPortMappingArgs); + buffer = simpleUPnPcommand(controlURL, servicetype, + "GetGenericPortMappingEntry", + GetPortMappingArgs, &bufsize); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewRemoteHost"); if(p && rHost) @@ -607,7 +580,7 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL, { strncpy(extPort, p, 6); extPort[5] = '\0'; - r = UPNPCOMMAND_SUCCESS; + ret = UPNPCOMMAND_SUCCESS; } p = GetValueFromNameValueList(&pdata, "NewProtocol"); if(p && protocol) @@ -616,14 +589,14 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL, protocol[3] = '\0'; } p = GetValueFromNameValueList(&pdata, "NewInternalClient"); - if(p && intClient) + if(p) { strncpy(intClient, p, 16); intClient[15] = '\0'; - r = 0; + ret = 0; } p = GetValueFromNameValueList(&pdata, "NewInternalPort"); - if(p && intPort) + if(p) { strncpy(intPort, p, 6); intPort[5] = '\0'; @@ -648,12 +621,11 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL, } p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - r = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &r); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); - free(GetPortMappingArgs); - return r; + return ret; } MINIUPNP_LIBSPEC int @@ -661,12 +633,12 @@ UPNP_GetPortMappingNumberOfEntries(const char * controlURL, const char * servicetype, unsigned int * numEntries) { - struct NameValueParserData pdata; - char * buffer; - int bufsize; - char* p; + struct NameValueParserData pdata; + char * buffer; + int bufsize; + char* p; int ret = UPNPCOMMAND_UNKNOWN_ERROR; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, + if(!(buffer = simpleUPnPcommand(controlURL, servicetype, "GetPortMappingNumberOfEntries", 0, &bufsize))) { return UPNPCOMMAND_HTTP_ERROR; @@ -674,23 +646,23 @@ UPNP_GetPortMappingNumberOfEntries(const char * controlURL, #ifdef DEBUG DisplayNameValueList(buffer, bufsize); #endif - ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + ParseNameValue(buffer, bufsize, &pdata); + free(buffer); - p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries"); - if(numEntries && p) { + p = GetValueFromNameValueList(&pdata, "NewPortMappingNumberOfEntries"); + if(numEntries && p) { *numEntries = 0; - sscanf(p, "%u", numEntries); + sscanf(p, "%u", numEntries); ret = UPNPCOMMAND_SUCCESS; - } + } p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } - ClearNameValueList(&pdata); + ClearNameValueList(&pdata); return ret; } @@ -710,7 +682,12 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL, char * leaseDuration) { struct NameValueParserData pdata; - struct UPNParg * GetPortMappingArgs; + struct UPNParg GetPortMappingArgs[] = { + {"NewRemoteHost", remoteHost}, + {"NewExternalPort", extPort}, + {"NewProtocol", proto}, + {NULL, NULL} + }; char * buffer; int bufsize; char * p; @@ -719,24 +696,15 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL, if(!intPort || !intClient || !extPort || !proto) return UPNPCOMMAND_INVALID_ARGS; - GetPortMappingArgs = calloc(4, sizeof(struct UPNParg)); - if(GetPortMappingArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - GetPortMappingArgs[0].elt = "NewRemoteHost"; - GetPortMappingArgs[0].val = remoteHost; - GetPortMappingArgs[1].elt = "NewExternalPort"; - GetPortMappingArgs[1].val = extPort; - GetPortMappingArgs[2].elt = "NewProtocol"; - GetPortMappingArgs[2].val = proto; - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "GetSpecificPortMappingEntry", - GetPortMappingArgs, &bufsize))) { - free(GetPortMappingArgs); + buffer = simpleUPnPcommand(controlURL, servicetype, + "GetSpecificPortMappingEntry", + GetPortMappingArgs, &bufsize); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "NewInternalClient"); if(p) { @@ -774,12 +742,11 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL, p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); - free(GetPortMappingArgs); return ret; } @@ -801,7 +768,14 @@ UPNP_GetListOfPortMappings(const char * controlURL, struct PortMappingParserData * data) { struct NameValueParserData pdata; - struct UPNParg * GetListOfPortMappingsArgs; + struct UPNParg GetListOfPortMappingsArgs[] = { + {"NewStartPort", startPort}, + {"NewEndPort", endPort}, + {"NewProtocol", protocol}, + {"NewManage", "1"}, + {"NewNumberOfPorts", numberOfPorts?numberOfPorts:"1000"}, + {NULL, NULL} + }; const char * p; char * buffer; int bufsize; @@ -810,31 +784,16 @@ UPNP_GetListOfPortMappings(const char * controlURL, if(!startPort || !endPort || !protocol) return UPNPCOMMAND_INVALID_ARGS; - GetListOfPortMappingsArgs = calloc(6, sizeof(struct UPNParg)); - if(GetListOfPortMappingsArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - GetListOfPortMappingsArgs[0].elt = "NewStartPort"; - GetListOfPortMappingsArgs[0].val = startPort; - GetListOfPortMappingsArgs[1].elt = "NewEndPort"; - GetListOfPortMappingsArgs[1].val = endPort; - GetListOfPortMappingsArgs[2].elt = "NewProtocol"; - GetListOfPortMappingsArgs[2].val = protocol; - GetListOfPortMappingsArgs[3].elt = "NewManage"; - GetListOfPortMappingsArgs[3].val = "1"; - GetListOfPortMappingsArgs[4].elt = "NewNumberOfPorts"; - GetListOfPortMappingsArgs[4].val = numberOfPorts?numberOfPorts:"1000"; - - if(!(buffer = simpleUPnPcommand(-1, controlURL, servicetype, - "GetListOfPortMappings", - GetListOfPortMappingsArgs, &bufsize))) { - free(GetListOfPortMappingsArgs); + buffer = simpleUPnPcommand(controlURL, servicetype, + "GetListOfPortMappings", + GetListOfPortMappingsArgs, &bufsize); + if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } - free(GetListOfPortMappingsArgs); /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); /*p = GetValueFromNameValueList(&pdata, "NewPortListing");*/ /*if(p) { @@ -864,8 +823,8 @@ UPNP_GetListOfPortMappings(const char * controlURL, p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); @@ -890,13 +849,13 @@ UPNP_GetFirewallStatus(const char * controlURL, if(!firewallEnabled || !inboundPinholeAllowed) return UPNPCOMMAND_INVALID_ARGS; - buffer = simpleUPnPcommand(-1, controlURL, servicetype, + buffer = simpleUPnPcommand(controlURL, servicetype, "GetFirewallStatus", 0, &bufsize); if(!buffer) { return UPNPCOMMAND_HTTP_ERROR; } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); fe = GetValueFromNameValueList(&pdata, "FirewallEnabled"); ipa = GetValueFromNameValueList(&pdata, "InboundPinholeAllowed"); if(ipa && fe) @@ -912,8 +871,8 @@ UPNP_GetFirewallStatus(const char * controlURL, p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); return ret; @@ -928,51 +887,43 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype const char * proto, int * opTimeout) { - struct UPNParg * GetOutboundPinholeTimeoutArgs; + struct UPNParg GetOutboundPinholeTimeoutArgs[] = { + {"RemoteHost", remoteHost}, + {"RemotePort", remotePort}, + {"Protocol", proto}, + {"InternalPort", intPort}, + {"InternalClient", intClient}, + {NULL, NULL} + }; char * buffer; int bufsize; struct NameValueParserData pdata; const char * resVal; - char * p; int ret; if(!intPort || !intClient || !proto || !remotePort || !remoteHost) return UPNPCOMMAND_INVALID_ARGS; - GetOutboundPinholeTimeoutArgs = calloc(6, sizeof(struct UPNParg)); - if(GetOutboundPinholeTimeoutArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - GetOutboundPinholeTimeoutArgs[0].elt = "RemoteHost"; - GetOutboundPinholeTimeoutArgs[0].val = remoteHost; - GetOutboundPinholeTimeoutArgs[1].elt = "RemotePort"; - GetOutboundPinholeTimeoutArgs[1].val = remotePort; - GetOutboundPinholeTimeoutArgs[2].elt = "Protocol"; - GetOutboundPinholeTimeoutArgs[2].val = proto; - GetOutboundPinholeTimeoutArgs[3].elt = "InternalPort"; - GetOutboundPinholeTimeoutArgs[3].val = intPort; - GetOutboundPinholeTimeoutArgs[4].elt = "InternalClient"; - GetOutboundPinholeTimeoutArgs[4].val = intClient; - buffer = simpleUPnPcommand(-1, controlURL, servicetype, + buffer = simpleUPnPcommand(controlURL, servicetype, "GetOutboundPinholeTimeout", GetOutboundPinholeTimeoutArgs, &bufsize); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(resVal, "%d", &ret); + if(sscanf(resVal, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } else { - ret = UPNPCOMMAND_SUCCESS; - p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout"); + const char * p = GetValueFromNameValueList(&pdata, "OutboundPinholeTimeout"); if(p) *opTimeout = my_atoui(p); + ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(GetOutboundPinholeTimeoutArgs); return ret; } @@ -986,7 +937,15 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype, const char * leaseTime, char * uniqueID) { - struct UPNParg * AddPinholeArgs; + struct UPNParg AddPinholeArgs[] = { + {"RemoteHost", ""}, + {"RemotePort", remotePort}, + {"Protocol", proto}, + {"InternalPort", intPort}, + {"InternalClient", ""}, + {"LeaseTime", leaseTime}, + {NULL, NULL} + }; char * buffer; int bufsize; struct NameValueParserData pdata; @@ -997,44 +956,18 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype, if(!intPort || !intClient || !proto || !remoteHost || !remotePort || !leaseTime) return UPNPCOMMAND_INVALID_ARGS; - AddPinholeArgs = calloc(7, sizeof(struct UPNParg)); - if(AddPinholeArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - /* RemoteHost can be wilcarded */ - if(strncmp(remoteHost, "empty", 5)==0) - { - AddPinholeArgs[0].elt = "RemoteHost"; - AddPinholeArgs[0].val = ""; - } - else - { - AddPinholeArgs[0].elt = "RemoteHost"; + /* RemoteHost and InternalClient can be wilcarded + * accept both the empty string and "empty" as wildcard */ + if(strncmp(remoteHost, "empty", 5) != 0) AddPinholeArgs[0].val = remoteHost; - } - AddPinholeArgs[1].elt = "RemotePort"; - AddPinholeArgs[1].val = remotePort; - AddPinholeArgs[2].elt = "Protocol"; - AddPinholeArgs[2].val = proto; - AddPinholeArgs[3].elt = "InternalPort"; - AddPinholeArgs[3].val = intPort; - if(strncmp(intClient, "empty", 5)==0) - { - AddPinholeArgs[4].elt = "InternalClient"; - AddPinholeArgs[4].val = ""; - } - else - { - AddPinholeArgs[4].elt = "InternalClient"; + if(strncmp(intClient, "empty", 5) != 0) AddPinholeArgs[4].val = intClient; - } - AddPinholeArgs[5].elt = "LeaseTime"; - AddPinholeArgs[5].val = leaseTime; - buffer = simpleUPnPcommand(-1, controlURL, servicetype, + buffer = simpleUPnPcommand(controlURL, servicetype, "AddPinhole", AddPinholeArgs, &bufsize); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "UniqueID"); if(p) { @@ -1045,15 +978,14 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype, if(resVal) { /*printf("AddPortMapping errorCode = '%s'\n", resVal);*/ - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(resVal, "%d", &ret); + if(sscanf(resVal, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } else { ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(AddPinholeArgs); return ret; } @@ -1062,7 +994,11 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, const char * uniqueID, const char * leaseTime) { - struct UPNParg * UpdatePinholeArgs; + struct UPNParg UpdatePinholeArgs[] = { + {"UniqueID", uniqueID}, + {"NewLeaseTime", leaseTime}, + {NULL, NULL} + }; char * buffer; int bufsize; struct NameValueParserData pdata; @@ -1072,32 +1008,24 @@ UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, if(!uniqueID || !leaseTime) return UPNPCOMMAND_INVALID_ARGS; - UpdatePinholeArgs = calloc(3, sizeof(struct UPNParg)); - if(UpdatePinholeArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - UpdatePinholeArgs[0].elt = "UniqueID"; - UpdatePinholeArgs[0].val = uniqueID; - UpdatePinholeArgs[1].elt = "NewLeaseTime"; - UpdatePinholeArgs[1].val = leaseTime; - buffer = simpleUPnPcommand(-1, controlURL, servicetype, + buffer = simpleUPnPcommand(controlURL, servicetype, "UpdatePinhole", UpdatePinholeArgs, &bufsize); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { /*printf("AddPortMapping errorCode = '%s'\n", resVal); */ - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(resVal, "%d", &ret); + if(sscanf(resVal, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } else { ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(UpdatePinholeArgs); return ret; } @@ -1105,7 +1033,10 @@ MINIUPNP_LIBSPEC int UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID) { /*struct NameValueParserData pdata;*/ - struct UPNParg * DeletePinholeArgs; + struct UPNParg DeletePinholeArgs[] = { + {"UniqueID", uniqueID}, + {NULL, NULL} + }; char * buffer; int bufsize; struct NameValueParserData pdata; @@ -1115,30 +1046,24 @@ UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char if(!uniqueID) return UPNPCOMMAND_INVALID_ARGS; - DeletePinholeArgs = calloc(2, sizeof(struct UPNParg)); - if(DeletePinholeArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - DeletePinholeArgs[0].elt = "UniqueID"; - DeletePinholeArgs[0].val = uniqueID; - buffer = simpleUPnPcommand(-1, controlURL, servicetype, + buffer = simpleUPnPcommand(controlURL, servicetype, "DeletePinhole", DeletePinholeArgs, &bufsize); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; /*DisplayNameValueList(buffer, bufsize);*/ ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); resVal = GetValueFromNameValueList(&pdata, "errorCode"); if(resVal) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(resVal, "%d", &ret); + if(sscanf(resVal, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } else { ret = UPNPCOMMAND_SUCCESS; } ClearNameValueList(&pdata); - free(DeletePinholeArgs); return ret; } @@ -1147,7 +1072,10 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, const char * uniqueID, int * isWorking) { struct NameValueParserData pdata; - struct UPNParg * CheckPinholeWorkingArgs; + struct UPNParg CheckPinholeWorkingArgs[] = { + {"UniqueID", uniqueID}, + {NULL, NULL} + }; char * buffer; int bufsize; char * p; @@ -1156,17 +1084,14 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, if(!uniqueID) return UPNPCOMMAND_INVALID_ARGS; - CheckPinholeWorkingArgs = calloc(4, sizeof(struct UPNParg)); - if(CheckPinholeWorkingArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - CheckPinholeWorkingArgs[0].elt = "UniqueID"; - CheckPinholeWorkingArgs[0].val = uniqueID; - buffer = simpleUPnPcommand(-1, controlURL, servicetype, + buffer = simpleUPnPcommand(controlURL, servicetype, "CheckPinholeWorking", CheckPinholeWorkingArgs, &bufsize); if(!buffer) + { return UPNPCOMMAND_HTTP_ERROR; + } ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "IsWorking"); if(p) @@ -1180,12 +1105,11 @@ UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); - free(CheckPinholeWorkingArgs); return ret; } @@ -1194,7 +1118,10 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, const char * uniqueID, int * packets) { struct NameValueParserData pdata; - struct UPNParg * GetPinholePacketsArgs; + struct UPNParg GetPinholePacketsArgs[] = { + {"UniqueID", uniqueID}, + {NULL, NULL} + }; char * buffer; int bufsize; char * p; @@ -1203,17 +1130,12 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, if(!uniqueID) return UPNPCOMMAND_INVALID_ARGS; - GetPinholePacketsArgs = calloc(4, sizeof(struct UPNParg)); - if(GetPinholePacketsArgs == NULL) - return UPNPCOMMAND_MEM_ALLOC_ERROR; - GetPinholePacketsArgs[0].elt = "UniqueID"; - GetPinholePacketsArgs[0].val = uniqueID; - buffer = simpleUPnPcommand(-1, controlURL, servicetype, + buffer = simpleUPnPcommand(controlURL, servicetype, "GetPinholePackets", GetPinholePacketsArgs, &bufsize); if(!buffer) return UPNPCOMMAND_HTTP_ERROR; ParseNameValue(buffer, bufsize, &pdata); - free(buffer); buffer = NULL; + free(buffer); p = GetValueFromNameValueList(&pdata, "PinholePackets"); if(p) @@ -1225,13 +1147,10 @@ UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, p = GetValueFromNameValueList(&pdata, "errorCode"); if(p) { - ret = UPNPCOMMAND_UNKNOWN_ERROR; - sscanf(p, "%d", &ret); + if(sscanf(p, "%d", &ret) != 1) + ret = UPNPCOMMAND_UNKNOWN_ERROR; } ClearNameValueList(&pdata); - free(GetPinholePacketsArgs); return ret; } - - diff --git a/Externals/miniupnpc/src/upnpcommands.h b/Externals/miniupnpc/src/upnpcommands.h index 55b7b060dc..2ede06ef17 100644 --- a/Externals/miniupnpc/src/upnpcommands.h +++ b/Externals/miniupnpc/src/upnpcommands.h @@ -1,50 +1,113 @@ -/* $Id: upnpcommands.h,v 1.30 2015/07/15 12:21:28 nanard Exp $ */ -/* Miniupnp project : http://miniupnp.free.fr/ - * Author : Thomas Bernard - * Copyright (c) 2005-2015 Thomas Bernard +/* $Id: upnpcommands.h,v 1.34 2025/02/08 23:15:17 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project: miniupnp + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * Author: Thomas Bernard + * Copyright (c) 2005-2025 Thomas Bernard * This software is subject to the conditions detailed in the * LICENCE file provided within this distribution */ #ifndef UPNPCOMMANDS_H_INCLUDED #define UPNPCOMMANDS_H_INCLUDED -#include "upnpreplyparse.h" -#include "portlistingparse.h" +/*! \file upnpcommands.h + * \brief Internet Gateway Device methods + * + * See the documentation for both IGD v1 and IGD v2 : + * - https://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf + * - https://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v2-Device.pdf + * + * The methods are from WANIPConnection:1 or 2, WANCommonInterfaceConfig:1, + * and WANIPv6FirewallControl:1 + * + */ + #include "miniupnpc_declspec.h" #include "miniupnpctypes.h" /* MiniUPnPc return codes : */ +/*! \brief value for success */ #define UPNPCOMMAND_SUCCESS (0) +/*! \brief value for unknown error */ #define UPNPCOMMAND_UNKNOWN_ERROR (-1) +/*! \brief error while checking the arguments */ #define UPNPCOMMAND_INVALID_ARGS (-2) +/*! \brief HTTP communication error */ #define UPNPCOMMAND_HTTP_ERROR (-3) +/*! \brief The response contains invalid values */ #define UPNPCOMMAND_INVALID_RESPONSE (-4) +/*! \brief Memory allocation error */ #define UPNPCOMMAND_MEM_ALLOC_ERROR (-5) #ifdef __cplusplus extern "C" { #endif +struct PortMappingParserData; + +/*! \brief WANCommonInterfaceConfig:GetTotalBytesSent + * + * Note: this is a 32bits unsigned value and rolls over to 0 after reaching + * the maximum value + * + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of + * a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + */ MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalBytesSent(const char * controlURL, const char * servicetype); +/*! \brief WANCommonInterfaceConfig:GetTotalBytesReceived + * + * Note: this is a 32bits unsigned value and rolls over to 0 after reaching + * the maximum value + * + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + */ MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalBytesReceived(const char * controlURL, const char * servicetype); +/*! \brief WANCommonInterfaceConfig:GetTotalPacketsSent + * + * Note: this is a 32bits unsigned value and rolls over to 0 after reaching + * the maximum value + * + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + */ MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalPacketsSent(const char * controlURL, const char * servicetype); +/*! \brief WANCommonInterfaceConfig:GetTotalBytesReceived + * + * Note: this is a 32bits unsigned value and rolls over to 0 after reaching + * the maximum value + * + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + */ MINIUPNP_LIBSPEC UNSIGNED_INTEGER UPNP_GetTotalPacketsReceived(const char * controlURL, const char * servicetype); -/* UPNP_GetStatusInfo() - * status and lastconnerror are 64 byte buffers - * Return values : - * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR - * or a UPnP Error code */ +/*! \brief WANIPConnection:GetStatusInfo() + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[out] status 64 bytes buffer : `Unconfigured`, `Connecting`, + * `Connected`, `PendingDisconnect`, `Disconnecting`, `Disconnected` + * \param[out] uptime time in seconds + * \param[out] lastconnerror 64 bytes buffer : `ERROR_NONE`, + * `ERROR_COMMAND_ABORTED`, `ERROR_NOT_ENABLED_FOR_INTERNET`, + * `ERROR_USER_DISCONNECT`, `ERROR_ISP_DISCONNECT`, + * `ERROR_IDLE_DISCONNECT`, `ERROR_FORCED_DISCONNECT`, + * `ERROR_NO_CARRIER`, `ERROR_IP_CONFIGURATION`, `ERROR_UNKNOWN` + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error code + */ MINIUPNP_LIBSPEC int UPNP_GetStatusInfo(const char * controlURL, const char * servicetype, @@ -52,76 +115,87 @@ UPNP_GetStatusInfo(const char * controlURL, unsigned int * uptime, char * lastconnerror); -/* UPNP_GetConnectionTypeInfo() - * argument connectionType is a 64 character buffer - * Return Values : - * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR - * or a UPnP Error code */ +/*! \brief WANIPConnection:GetConnectionTypeInfo() + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[out] connectionType 64 characters buffer : `Unconfigured`, + * `IP_Routed`, `IP_Bridged` + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error code + */ MINIUPNP_LIBSPEC int UPNP_GetConnectionTypeInfo(const char * controlURL, const char * servicetype, char * connectionType); -/* UPNP_GetExternalIPAddress() call the corresponding UPNP method. - * if the third arg is not null the value is copied to it. - * at least 16 bytes must be available - * - * Return values : - * 0 : SUCCESS - * NON ZERO : ERROR Either an UPnP error code or an unknown error. +/*! \brief WANIPConnection:GetExternalIPAddress() * * possible UPnP Errors : - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 501 Action Failed - See UPnP Device Architecture section on Control. */ + * - 402 Invalid Args - See UPnP Device Architecture section on Control. + * - 501 Action Failed - See UPnP Device Architecture section on Control. + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[out] extIpAdd 16 bytes buffer + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_UNKNOWN_ERROR, + * #UPNPCOMMAND_INVALID_ARGS, #UPNPCOMMAND_HTTP_ERROR or an + * UPnP error code + */ MINIUPNP_LIBSPEC int UPNP_GetExternalIPAddress(const char * controlURL, const char * servicetype, char * extIpAdd); -/* UPNP_GetLinkLayerMaxBitRates() - * call WANCommonInterfaceConfig:1#GetCommonLinkProperties +/*! \brief UPNP_GetLinkLayerMaxBitRates() + * call `WANCommonInterfaceConfig:GetCommonLinkProperties` * - * return values : - * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR - * or a UPnP Error Code. */ + * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of a WANDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 + * \param[out] bitrateDown bits per second + * \param[out] bitrateUp bits per second + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error Code. + */ MINIUPNP_LIBSPEC int UPNP_GetLinkLayerMaxBitRates(const char* controlURL, const char* servicetype, unsigned int * bitrateDown, unsigned int * bitrateUp); -/* UPNP_AddPortMapping() - * if desc is NULL, it will be defaulted to "libminiupnpc" - * remoteHost is usually NULL because IGD don't support it. - * - * Return values : - * 0 : SUCCESS - * NON ZERO : ERROR. Either an UPnP error code or an unknown error. +/*! \brief WANIPConnection:AddPortMapping() * * List of possible UPnP errors for AddPortMapping : - * errorCode errorDescription (short) - Description (long) - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 501 Action Failed - See UPnP Device Architecture section on Control. - * 606 Action not authorized - The action requested REQUIRES authorization and - * the sender was not authorized. - * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be - * wild-carded - * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded - * 718 ConflictInMappingEntry - The port mapping entry specified conflicts - * with a mapping assigned previously to another client - * 724 SamePortValuesRequired - Internal and External port values - * must be the same - * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports - * permanent lease times on port mappings - * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard - * and cannot be a specific IP address or DNS name - * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and - * cannot be a specific port value - * 728 NoPortMapsAvailable - There are not enough free ports available to - * complete port mapping. - * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed - * due to conflict with other mechanisms. - * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ----------------- + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 501 Action Failed | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 715 WildCardNotPermittedInSrcIP | The source IP address cannot be wild-carded + * 716 WildCardNotPermittedInExtPort | The external port cannot be wild-carded + * 718 ConflictInMappingEntry | The port mapping entry specified conflicts with a mapping assigned previously to another client + * 724 SamePortValuesRequired | Internal and External port values must be the same + * 725 OnlyPermanentLeasesSupported | The NAT implementation only supports permanent lease times on port mappings + * 726 RemoteHostOnlySupportsWildcard | RemoteHost must be a wildcard and cannot be a specific IP address or DNS name + * 727 ExternalPortOnlySupportsWildcard | ExternalPort must be a wildcard and cannot be a specific port value + * 728 NoPortMapsAvailable | There are not enough free ports available to complete port mapping. + * 729 ConflictWithOtherMechanisms | Attempted port mapping is not allowed due to conflict with other mechanisms. + * 732 WildCardNotPermittedInIntPort | The internal port cannot be wild-carded + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[in] extPort External port + * \param[in] inPort Internal port + * \param[in] inClient IP of Internal client. + * \param[in] desc Port Mapping description. if NULL, defaults to + * "libminiupnpc" + * \param[in] proto `TCP` or `UDP` + * \param[in] remoteHost IP or empty string for wildcard. Most IGD don't + * support it + * \param[in] leaseDuration between 0 and 604800 + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_MEM_ALLOC_ERROR, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP error code. */ MINIUPNP_LIBSPEC int UPNP_AddPortMapping(const char * controlURL, const char * servicetype, @@ -133,28 +207,38 @@ UPNP_AddPortMapping(const char * controlURL, const char * servicetype, const char * remoteHost, const char * leaseDuration); -/* UPNP_AddAnyPortMapping() - * if desc is NULL, it will be defaulted to "libminiupnpc" - * remoteHost is usually NULL because IGD don't support it. +/*! \brief WANIPConnection:AddAnyPortMapping() * - * Return values : - * 0 : SUCCESS - * NON ZERO : ERROR. Either an UPnP error code or an unknown error. + * Only in WANIPConnection:2 * * List of possible UPnP errors for AddPortMapping : - * errorCode errorDescription (short) - Description (long) - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 501 Action Failed - See UPnP Device Architecture section on Control. - * 606 Action not authorized - The action requested REQUIRES authorization and - * the sender was not authorized. - * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be - * wild-carded - * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded - * 728 NoPortMapsAvailable - There are not enough free ports available to - * complete port mapping. - * 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed - * due to conflict with other mechanisms. - * 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 501 Action Failed | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 715 WildCardNotPermittedInSrcIP | The source IP address cannot be wild-carded + * 716 WildCardNotPermittedInExtPort | The external port cannot be wild-carded + * 728 NoPortMapsAvailable | There are not enough free ports available to complete port mapping. + * 729 ConflictWithOtherMechanisms | Attempted port mapping is not allowed due to conflict with other mechanisms. + * 732 WildCardNotPermittedInIntPort | The internal port cannot be wild-carded + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:2 + * \param[in] extPort External port + * \param[in] inPort Internal port + * \param[in] inClient IP of Internal client. + * \param[in] desc Port Mapping description. if NULL, defaults to + * "libminiupnpc" + * \param[in] proto `TCP` or `UDP` + * \param[in] remoteHost IP or empty string for wildcard. Most IGD don't + * support it + * \param[in] leaseDuration between 0 and 604800 + * \param[out] reservedPort 6 bytes buffer + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_MEM_ALLOC_ERROR, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_INVALID_RESPONSE, #UPNPCOMMAND_UNKNOWN_ERROR + * or a UPnP error code. */ MINIUPNP_LIBSPEC int UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, @@ -167,24 +251,35 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, const char * leaseDuration, char * reservedPort); -/* UPNP_DeletePortMapping() - * Use same argument values as what was used for AddPortMapping(). - * remoteHost is usually NULL because IGD don't support it. - * Return Values : - * 0 : SUCCESS - * NON ZERO : error. Either an UPnP error code or an undefined error. +/*! \brief WANIPConnection:DeletePortMapping() * - * List of possible UPnP errors for DeletePortMapping : - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 606 Action not authorized - The action requested REQUIRES authorization - * and the sender was not authorized. - * 714 NoSuchEntryInArray - The specified value does not exist in the array */ + * Use same argument values as what was used for UPNP_AddPortMapping() + * + * List of possible UPnP errors for UPNP_DeletePortMapping() : + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 714 NoSuchEntryInArray | The specified value does not exist in the array + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[in] extPort External port + * \param[in] proto `TCP` or `UDP` + * \param[in] remoteHost IP or empty string for wildcard. Most IGD don't + * support it + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_MEM_ALLOC_ERROR, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP error code. + */ MINIUPNP_LIBSPEC int UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, const char * extPort, const char * proto, const char * remoteHost); -/* UPNP_DeletePortRangeMapping() +/*! \brief WANIPConnection:DeletePortRangeMapping() + * + * Only in WANIPConnection:2 * Use same argument values as what was used for AddPortMapping(). * remoteHost is usually NULL because IGD don't support it. * Return Values : @@ -192,46 +287,66 @@ UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, * NON ZERO : error. Either an UPnP error code or an undefined error. * * List of possible UPnP errors for DeletePortMapping : - * 606 Action not authorized - The action requested REQUIRES authorization - * and the sender was not authorized. - * 730 PortMappingNotFound - This error message is returned if no port - * mapping is found in the specified range. - * 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent. */ + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 730 PortMappingNotFound | This error message is returned if no port mapping is found in the specified range. + * 733 InconsistentParameters | NewStartPort and NewEndPort values are not consistent. + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:2 + * \param[in] extPortStart External port range start + * \param[in] extPortEnd External port range end + * \param[in] proto `TCP` or `UDP` + * \param[in] manage `0` to remove only the port mappings of this IGD, + * `1` to remove port mappings also for other clients + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_MEM_ALLOC_ERROR, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP error code. + */ MINIUPNP_LIBSPEC int UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype, const char * extPortStart, const char * extPortEnd, const char * proto, const char * manage); -/* UPNP_GetPortMappingNumberOfEntries() - * not supported by all routers */ +/*! \brief WANIPConnection:GetPortMappingNumberOfEntries() + * + * not supported by all routers + * + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[out] numEntries Port mappings count + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_HTTP_ERROR, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP error code. + */ MINIUPNP_LIBSPEC int -UPNP_GetPortMappingNumberOfEntries(const char* controlURL, - const char* servicetype, - unsigned int * num); +UPNP_GetPortMappingNumberOfEntries(const char * controlURL, + const char * servicetype, + unsigned int * numEntries); -/* UPNP_GetSpecificPortMappingEntry() - * retrieves an existing port mapping - * params : - * in extPort - * in proto - * in remoteHost - * out intClient (16 bytes) - * out intPort (6 bytes) - * out desc (80 bytes) - * out enabled (4 bytes) - * out leaseDuration (16 bytes) +/*! \brief retrieves an existing port mapping for a port:protocol * - * return value : - * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR - * or a UPnP Error Code. + * List of possible UPnP errors for UPNP_GetSpecificPortMappingEntry() : + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 501 Action Failed | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 714 NoSuchEntryInArray | The specified value does not exist in the array. * - * List of possible UPnP errors for _GetSpecificPortMappingEntry : - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 501 Action Failed - See UPnP Device Architecture section on Control. - * 606 Action not authorized - The action requested REQUIRES authorization - * and the sender was not authorized. - * 714 NoSuchEntryInArray - The specified value does not exist in the array. + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[in] extPort External port + * \param[in] proto `TCP` or `UDP` + * \param[in] remoteHost IP or empty string for wildcard. Most IGD don't + * support it + * \param[out] intClient 16 bytes buffer + * \param[out] intPort 6 bytes buffer + * \param[out] desc 80 bytes buffer + * \param[out] enabled 4 bytes buffer + * \param[out] leaseDuration 16 bytes + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error Code. */ MINIUPNP_LIBSPEC int UPNP_GetSpecificPortMappingEntry(const char * controlURL, @@ -245,27 +360,27 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL, char * enabled, char * leaseDuration); -/* UPNP_GetGenericPortMappingEntry() - * params : - * in index - * out extPort (6 bytes) - * out intClient (16 bytes) - * out intPort (6 bytes) - * out protocol (4 bytes) - * out desc (80 bytes) - * out enabled (4 bytes) - * out rHost (64 bytes) - * out duration (16 bytes) +/*! \brief WANIPConnection:GetGenericPortMappingEntry() * - * return value : - * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR - * or a UPnP Error Code. + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 402 Invalid Args | See UPnP Device Architecture section on Control. + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 713 SpecifiedArrayIndexInvalid | The specified array index is out of bounds * - * Possible UPNP Error codes : - * 402 Invalid Args - See UPnP Device Architecture section on Control. - * 606 Action not authorized - The action requested REQUIRES authorization - * and the sender was not authorized. - * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds + * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1 + * \param[in] index + * \param[out] extPort 6 bytes buffer + * \param[out] intClient 16 bytes buffer + * \param[out] intPort 6 bytes buffer + * \param[out] protocol 4 bytes buffer + * \param[out] desc 80 bytes buffer + * \param[out] enabled 4 bytes buffer + * \param[out] rHost 64 bytes buffer + * \param[out] duration 16 bytes buffer + * \return #UPNPCOMMAND_SUCCESS, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_UNKNOWN_ERROR or a UPnP Error Code. */ MINIUPNP_LIBSPEC int UPNP_GetGenericPortMappingEntry(const char * controlURL, @@ -280,14 +395,25 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL, char * rHost, char * duration); -/* UPNP_GetListOfPortMappings() Available in IGD v2 +/*! \brief retrieval of a list of existing port mappings * + * Available in IGD v2 : WANIPConnection:GetListOfPortMappings() * - * Possible UPNP Error codes : - * 606 Action not Authorized - * 730 PortMappingNotFound - no port mapping is found in the specified range. - * 733 InconsistantParameters - NewStartPort and NewEndPort values are not - * consistent. + * errorCode errorDescription (short) | Description (long) + * ---------------------------------- | ------------------ + * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized. + * 730 PortMappingNotFound | no port mapping is found in the specified range. + * 733 InconsistantParameters | NewStartPort and NewEndPort values are not consistent. + * + * \param[in] controlURL controlURL of the WANIPConnection of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:2 + * \param[in] startPort port interval start + * \param[in] endPort port interval end + * \param[in] protocol `TCP` or `UDP` + * \param[in] numberOfPorts size limit of the list returned. `0` to request + * all port mappings + * \param[out] data port mappings list */ MINIUPNP_LIBSPEC int UPNP_GetListOfPortMappings(const char * controlURL, @@ -298,15 +424,48 @@ UPNP_GetListOfPortMappings(const char * controlURL, const char * numberOfPorts, struct PortMappingParserData * data); -/* IGD:2, functions for service WANIPv6FirewallControl:1 */ +/*! \brief GetFirewallStatus() retrieves whether the firewall is enabled + * and pinhole can be created through UPnP + * + * IGD:2, functions for service WANIPv6FirewallControl:1 + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[out] firewallEnabled false (0) or true (1) + * \param[out] inboundPinholeAllowed false (0) or true (1) + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ MINIUPNP_LIBSPEC int UPNP_GetFirewallStatus(const char * controlURL, const char * servicetype, int * firewallEnabled, int * inboundPinholeAllowed); +/*! \brief retrieve default value after which automatically created pinholes + * expire + * + * The returned value may be specific to the \p proto, \p remoteHost, + * \p remotePort, \p intClient and \p intPort, but this behavior depends + * on the implementation of the firewall. + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] remoteHost + * \param[in] remotePort + * \param[in] intClient + * \param[in] intPort + * \param[in] proto `TCP` or `UDP` + * \param[out] opTimeout lifetime in seconds of an inbound "automatic" + * firewall pinhole created by an outbound traffic initiation. + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ MINIUPNP_LIBSPEC int -UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype, +UPNP_GetOutboundPinholeTimeout(const char * controlURL, + const char * servicetype, const char * remoteHost, const char * remotePort, const char * intClient, @@ -314,6 +473,24 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype const char * proto, int * opTimeout); +/*! \brief create a new pinhole that allows incoming traffic to pass + * through the firewall + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] remoteHost literal presentation of IPv6 address or domain name. + * empty string for wildcard + * \param[in] remotePort remote host port. Likely 0 (for wildcard) + * \param[in] intClient IP address of internal client. cannot be wildcarded + * \param[in] intPort client port. 0 for wildcard + * \param[in] proto IP protocol integer (6 for TCP, 17 for UDP, etc.) + * 65535 for wildcard. + * \param[in] leaseTime in seconds + * \param[out] uniqueID 8 bytes buffer + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ MINIUPNP_LIBSPEC int UPNP_AddPinhole(const char * controlURL, const char * servicetype, const char * remoteHost, @@ -324,18 +501,61 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype, const char * leaseTime, char * uniqueID); +/*! \brief update a pinhole’s lease time + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] uniqueID value obtained through UPNP_AddPinhole() + * \param[in] leaseTime in seconds + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ MINIUPNP_LIBSPEC int UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, const char * uniqueID, const char * leaseTime); +/*! \brief remove a pinhole + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] uniqueID value obtained through UPNP_AddPinhole() + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ MINIUPNP_LIBSPEC int -UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID); +UPNP_DeletePinhole(const char * controlURL, + const char * servicetype, + const char * uniqueID); +/*! \brief checking if a certain pinhole allows traffic to pass through the firewall + * + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] uniqueID value obtained through UPNP_AddPinhole() + * \param[out] isWorking `0` for false, `1` for true + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ MINIUPNP_LIBSPEC int UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, const char * uniqueID, int * isWorking); +/*! \brief get the total number of IP packets which have been going through + * the specified pinhole + * \todo \p packets should be #UNSIGNED_INTEGER + * \param[in] controlURL controlURL of the WANIPv6FirewallControl of a + * WANConnectionDevice + * \param[in] servicetype urn:schemas-upnp-org:service:WANIPv6FirewallControl:1 + * \param[in] uniqueID value obtained through UPNP_AddPinhole() + * \param[out] packets how many IP packets have been going through the + * specified pinhole + * \return #UPNPCOMMAND_UNKNOWN_ERROR, #UPNPCOMMAND_INVALID_ARGS, + * #UPNPCOMMAND_HTTP_ERROR, #UPNPCOMMAND_SUCCESS or an UPnP error code + */ MINIUPNP_LIBSPEC int UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, const char * uniqueID, int * packets); diff --git a/Externals/miniupnpc/src/upnpdev.h b/Externals/miniupnpc/src/upnpdev.h index f49fbe17ca..1a3f036072 100644 --- a/Externals/miniupnpc/src/upnpdev.h +++ b/Externals/miniupnpc/src/upnpdev.h @@ -1,30 +1,52 @@ -/* $Id: upnpdev.h,v 1.1 2015/08/28 12:14:19 nanard Exp $ */ +/* $Id: upnpdev.h,v 1.6 2025/02/08 23:15:17 nanard Exp $ */ /* Project : miniupnp - * Web : http://miniupnp.free.fr/ + * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * Author : Thomas BERNARD - * copyright (c) 2005-2015 Thomas Bernard + * copyright (c) 2005-2025 Thomas Bernard * This software is subjet to the conditions detailed in the * provided LICENSE file. */ #ifndef UPNPDEV_H_INCLUDED #define UPNPDEV_H_INCLUDED +/*! \file upnpdev.h + * \brief UPNPDev device linked-list structure + * \todo could be merged into miniupnpc.h + */ #include "miniupnpc_declspec.h" #ifdef __cplusplus extern "C" { #endif +/*! + * \brief UPnP device linked-list + */ struct UPNPDev { + /*! \brief pointer to the next element */ struct UPNPDev * pNext; + /*! \brief root description URL */ char * descURL; + /*! \brief ST: as advertised */ char * st; - unsigned int scope_id; + /*! \brief USN: as advertised */ char * usn; - char buffer[3]; + /*! \brief IPv6 scope id of the network interface */ + unsigned int scope_id; +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + /* C99 flexible array member */ + /*! \brief buffer for descURL, st and usn */ + char buffer[]; +#elif defined(__GNUC__) + char buffer[0]; +#else + /* Fallback to a hack */ + char buffer[1]; +#endif }; -/* freeUPNPDevlist() - * free list returned by upnpDiscover() */ +/*! \brief free list returned by upnpDiscover() + * \param[in] devlist linked list to free + */ MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist); diff --git a/Externals/miniupnpc/src/upnperrors.c b/Externals/miniupnpc/src/upnperrors.c index 40a2e7857f..c71068da50 100644 --- a/Externals/miniupnpc/src/upnperrors.c +++ b/Externals/miniupnpc/src/upnperrors.c @@ -1,9 +1,10 @@ -/* $Id: upnperrors.c,v 1.5 2011/04/10 11:19:36 nanard Exp $ */ -/* Project : miniupnp +/* $Id: upnperrors.c,v 1.12 2023/06/26 23:19:28 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * Project : miniupnp * Author : Thomas BERNARD - * copyright (c) 2007 Thomas Bernard + * copyright (c) 2007-2023 Thomas Bernard * All Right reserved. - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #include @@ -27,10 +28,14 @@ const char * strupnperror(int err) case UPNPCOMMAND_INVALID_RESPONSE: s = "Miniupnpc Invalid response"; break; + case UPNPCOMMAND_HTTP_ERROR: + s = "Miniupnpc HTTP error"; + break; case UPNPDISCOVER_SOCKET_ERROR: s = "Miniupnpc Socket error"; break; case UPNPDISCOVER_MEMORY_ERROR: + case UPNPCOMMAND_MEM_ALLOC_ERROR: s = "Miniupnpc Memory allocation error"; break; case 401: @@ -42,6 +47,24 @@ const char * strupnperror(int err) case 501: s = "Action Failed"; break; + case 600: + s = "Argument Value Invalid"; + break; + case 601: + s = "Argument Value Out of Range"; + break; + case 602: + s = "Optional Action Not Implemented"; + break; + case 603: + s = "Out of Memory"; + break; + case 604: + s = "Human Intervention Required"; + break; + case 605: + s = "String Argument Too Long"; + break; case 606: s = "Action not authorized"; break; @@ -67,10 +90,10 @@ const char * strupnperror(int err) s = "ProtocolWildcardingNotAllowed"; break; case 708: - s = "WildcardNotPermittedInSrcIP"; + s = "InvalidLayer2Address"; break; case 709: - s = "NoPacketSent"; + s = "NoTrafficReceived"; break; case 713: s = "SpecifiedArrayIndexInvalid"; @@ -99,6 +122,24 @@ const char * strupnperror(int err) case 727: s = "ExternalPortOnlySupportsWildcard"; break; + case 728: + s = "NoPortMapsAvailable"; + break; + case 729: + s = "ConflictWithOtherMechanisms"; + break; + case 730: + s = "PortMappingNotFound"; + break; + case 731: + s = "ReadOnly"; + break; + case 732: + s = "WildCardNotPermittedInIntPort"; + break; + case 733: + s = "InconsistentParameters"; + break; default: s = "UnknownError"; break; diff --git a/Externals/miniupnpc/src/upnperrors.h b/Externals/miniupnpc/src/upnperrors.h index 8499d9a1c9..e1f36bfb3e 100644 --- a/Externals/miniupnpc/src/upnperrors.h +++ b/Externals/miniupnpc/src/upnperrors.h @@ -1,22 +1,32 @@ -/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */ -/* (c) 2007-2015 Thomas Bernard +/* $Id: upnperrors.h,v 1.8 2025/02/08 23:15:17 nanard Exp $ */ +/* (c) 2007-2025 Thomas Bernard * All rights reserved. * MiniUPnP Project. - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ * This software is subjet to the conditions detailed in the * provided LICENCE file. */ #ifndef UPNPERRORS_H_INCLUDED #define UPNPERRORS_H_INCLUDED +/*! \file upnperrors.h + * \brief code to string API for errors + */ #include "miniupnpc_declspec.h" #ifdef __cplusplus extern "C" { #endif -/* strupnperror() - * Return a string description of the UPnP error code - * or NULL for undefinded errors */ +/*! + * \brief convert error code to string + * + * Work for both MiniUPnPc specific errors and UPnP standard defined + * errors. + * + * \param[in] err numerical error code + * \return a string description of the error code + * or NULL for undefinded errors + */ MINIUPNP_LIBSPEC const char * strupnperror(int err); #ifdef __cplusplus diff --git a/Externals/miniupnpc/src/upnpreplyparse.c b/Externals/miniupnpc/src/upnpreplyparse.c index 5de5796a39..24a5f1670a 100644 --- a/Externals/miniupnpc/src/upnpreplyparse.c +++ b/Externals/miniupnpc/src/upnpreplyparse.c @@ -1,7 +1,8 @@ -/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */ -/* MiniUPnP project - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2015 Thomas Bernard +/* $Id: upnpreplyparse.c,v 1.22 2025/02/08 23:12:26 nanard Exp $ */ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * MiniUPnP project + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * (c) 2006-2025 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ @@ -12,6 +13,23 @@ #include "upnpreplyparse.h" #include "minixml.h" +struct NameValue { + /*! \brief pointer to the next element */ + struct NameValue * l_next; + /*! \brief name */ + char name[64]; +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + /* C99 flexible array member */ + /*! \brief character value */ + char value[]; +#elif defined(__GNUC__) + char value[0]; +#else + /* Fallback to a hack */ + char value[1]; +#endif +}; + static void NameValueParserStartElt(void * d, const char * name, int l) { @@ -26,12 +44,12 @@ NameValueParserStartElt(void * d, const char * name, int l) } static void -NameValueParserEndElt(void * d, const char * name, int l) +NameValueParserEndElt(void * d, const char * name, int namelen) { struct NameValueParserData * data = (struct NameValueParserData *)d; struct NameValue * nv; (void)name; - (void)l; + (void)namelen; if(!data->topelt) return; if(strcmp(data->curelt, "NewPortListing") != 0) @@ -39,7 +57,7 @@ NameValueParserEndElt(void * d, const char * name, int l) int l; /* standard case. Limited to n chars strings */ l = data->cdatalen; - nv = malloc(sizeof(struct NameValue)); + nv = malloc(sizeof(struct NameValue) + l + 1); if(nv == NULL) { /* malloc error */ @@ -49,8 +67,6 @@ NameValueParserEndElt(void * d, const char * name, int l) #endif /* DEBUG */ return; } - if(l>=(int)sizeof(nv->value)) - l = sizeof(nv->value) - 1; strncpy(nv->name, data->curelt, 64); nv->name[63] = '\0'; if(data->cdata != NULL) @@ -77,6 +93,7 @@ NameValueParserGetData(void * d, const char * datas, int l) if(strcmp(data->curelt, "NewPortListing") == 0) { /* specific case for NewPortListing which is a XML Document */ + free(data->portListing); data->portListing = malloc(l + 1); if(!data->portListing) { @@ -104,9 +121,7 @@ ParseNameValue(const char * buffer, int bufsize, struct NameValueParserData * data) { struct xmlparser parser; - data->l_head = NULL; - data->portListing = NULL; - data->portListingLength = 0; + memset(data, 0, sizeof(struct NameValueParserData)); /* init xmlparser object */ parser.xmlstart = buffer; parser.xmlsize = bufsize; @@ -137,7 +152,7 @@ ClearNameValueList(struct NameValueParserData * pdata) char * GetValueFromNameValueList(struct NameValueParserData * pdata, - const char * Name) + const char * name) { struct NameValue * nv; char * p = NULL; @@ -145,37 +160,12 @@ GetValueFromNameValueList(struct NameValueParserData * pdata, (nv != NULL) && (p == NULL); nv = nv->l_next) { - if(strcmp(nv->name, Name) == 0) + if(strcmp(nv->name, name) == 0) p = nv->value; } return p; } -#if 0 -/* useless now that minixml ignores namespaces by itself */ -char * -GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata, - const char * Name) -{ - struct NameValue * nv; - char * p = NULL; - char * pname; - for(nv = pdata->head.lh_first; - (nv != NULL) && (p == NULL); - nv = nv->entries.le_next) - { - pname = strrchr(nv->name, ':'); - if(pname) - pname++; - else - pname = nv->name; - if(strcmp(pname, Name)==0) - p = nv->value; - } - return p; -} -#endif - /* debug all-in-one function * do parsing then display to stdout */ #ifdef DEBUG diff --git a/Externals/miniupnpc/src/upnpreplyparse.h b/Externals/miniupnpc/src/upnpreplyparse.h index 6badd15b26..8d4ca46430 100644 --- a/Externals/miniupnpc/src/upnpreplyparse.h +++ b/Externals/miniupnpc/src/upnpreplyparse.h @@ -1,53 +1,73 @@ -/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */ +/* $Id: upnpreplyparse.h,v 1.20 2025/02/08 23:15:17 nanard Exp $ */ /* MiniUPnP project - * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ - * (c) 2006-2013 Thomas Bernard + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * (c) 2006-2025 Thomas Bernard * This software is subject to the conditions detailed * in the LICENCE file provided within the distribution */ #ifndef UPNPREPLYPARSE_H_INCLUDED #define UPNPREPLYPARSE_H_INCLUDED +/*! \file upnpreplyparse.h + * \brief Parsing of UPnP SOAP responses + */ + #ifdef __cplusplus extern "C" { #endif -struct NameValue { - struct NameValue * l_next; - char name[64]; - char value[128]; -}; +/*! \brief Name/Value linked list + * not exposed in the public API + */ +struct NameValue; +/*! \brief data structure for parsing */ struct NameValueParserData { + /*! \brief name/value linked list */ struct NameValue * l_head; + /*! \brief current element name */ char curelt[64]; + /*! \brief port listing array */ char * portListing; + /*! \brief port listing array length */ int portListingLength; + /*! \brief flag indicating the current element is */ int topelt; + /*! \brief top element character data */ const char * cdata; + /*! \brief top element character data length */ int cdatalen; }; -/* ParseNameValue() */ +/*! + * \brief Parse XML and fill the structure + * + * \param[in] buffer XML data + * \param[in] bufsize buffer length + * \param[out] data structure to fill + */ void ParseNameValue(const char * buffer, int bufsize, struct NameValueParserData * data); -/* ClearNameValueList() */ +/*! + * \brief free memory + * + * \param[in,out] pdata data structure + */ void ClearNameValueList(struct NameValueParserData * pdata); -/* GetValueFromNameValueList() */ +/*! + * \brief get a value from the parsed data + * + * \param[in] pdata data structure + * \param[in] name name + * \return the value or NULL if not found + */ char * GetValueFromNameValueList(struct NameValueParserData * pdata, - const char * Name); - -#if 0 -/* GetValueFromNameValueListIgnoreNS() */ -char * -GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata, - const char * Name); -#endif + const char * name); /* DisplayNameValueList() */ #ifdef DEBUG @@ -60,4 +80,3 @@ DisplayNameValueList(char * buffer, int bufsize); #endif #endif - diff --git a/Externals/miniupnpc/src/win32_snprintf.h b/Externals/miniupnpc/src/win32_snprintf.h new file mode 100644 index 0000000000..5064df63bc --- /dev/null +++ b/Externals/miniupnpc/src/win32_snprintf.h @@ -0,0 +1,71 @@ +/* vim: tabstop=4 shiftwidth=4 noexpandtab + * MiniUPnP project + * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/ + * (c) 2020 Pali Rohár + * This software is subject to the conditions detailed + * in the LICENCE file provided within the distribution */ + +#ifndef WIN32_SNPRINTF_H +#define WIN32_SNPRINTF_H + +#ifdef _WIN32 + +#include + +/* snprintf is supported by: + * - Visual Studio 2015 or new + * - mingw32 with iso c ext + * - mingw-w64 with ansi stdio + * - mingw-w64 6.0.0 or new with ucrt + * - mingw-w64 8.0.0 or new with iso c ext + */ +#if ( \ + (defined(_MSC_VER) && _MSC_VER < 1900) /* Visual Studio older than 2015 */ || \ + (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) && defined(__NO_ISOCEXT)) /* mingw32 without iso c ext */ || \ + (defined(__MINGW64_VERSION_MAJOR) && /* mingw-w64 not ... */ !( \ + (defined (__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO != 0) /* ... with ansi stdio */ || \ + (__MINGW64_VERSION_MAJOR >= 6 && defined(_UCRT)) /* ... at least 6.0.0 with ucrt */ || \ + (__MINGW64_VERSION_MAJOR >= 8 && !defined(__NO_ISOCEXT))) /* ... at least 8.0.0 with iso c ext */ || \ + 0) || \ +0) + +/* _scprintf is supported by: + * - Visual Studio 2002 or new + * - msvcr70.dll or new + * - msvcrt.dll on Windows XP or new + */ +#if ( \ + (defined(_MSC_VER) && _MSC_VER < 1300) /* Visual Studio older than 2002 */ || \ + (defined(__MSVCRT_VERSION__) && __MSVCRT_VERSION__ < 0x700) /* msvcrt older than 7.0 */ || \ +0) +#define CHECK_SCPRINTF 0 +#define IF_SCPRINTF(expr) 0 +#define ELSE_SCPRINTF(expr) expr +#else +#define CHECK_SCPRINTF 1 +#define IF_SCPRINTF(expr) expr +#define ELSE_SCPRINTF(expr) 0 +#endif + +/* Emulation of snprintf for win32 */ +#define snprintf(buf, size, fmt, ...) ( \ + (((size) != 0 && (buf) != NULL) ? ( /* _snprintf does not work with NULL buffer */ \ + _snprintf((buf), (size), (fmt), __VA_ARGS__), /* _snprintf returns -1 on overflow, so ignore its value */ \ + (((char *)buf)[(size_t)(size)-1] = 0), /* _snprintf does not fill nul byte on overflow */ \ + 0) : 0), \ + (CHECK_SCPRINTF ? IF_SCPRINTF( \ + _scprintf((fmt), __VA_ARGS__) /* calculate return value for snprintf via _scprintf */ \ + ) : ELSE_SCPRINTF( \ + ((size) != 0 && (buf) != NULL) ? \ + strlen((buf)) /* return just length of buffer */ \ + : \ + 1 /* no buffer, impossible to calculate, return just non-zero number */ \ + ) \ + ) \ +) + +#endif + +#endif /* _WIN32 */ + +#endif /* WIN32_SNPRINTF_H */