Externals: Update miniupnp to v2.3.2

This commit is contained in:
JordanTheToaster 2025-03-12 09:56:49 +00:00
parent de997d616f
commit 946de7679c
53 changed files with 6440 additions and 1347 deletions

View file

@ -1,24 +1,72 @@
cmake_minimum_required(VERSION 2.8.12) cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project(miniupnpc C) project (miniupnpc
set(MINIUPNPC_VERSION 1.9) VERSION 2.3.2
set(MINIUPNPC_API_VERSION 14) DESCRIPTION "UPnP IGD client lightweight library"
HOMEPAGE_URL https://miniupnp.tuxfamily.org/
LANGUAGES C)
if(UNIX) set (MINIUPNPC_API_VERSION 20)
add_definitions(-DMINIUPNPC_SET_SOCKET_TIMEOUT)
add_definitions(-D_BSD_SOURCE -D_POSIX_C_SOURCE=1) option (UPNPC_BUILD_STATIC "Build static library" TRUE)
elseif(WIN32) option (UPNPC_BUILD_SHARED "Build shared library" FALSE)
add_definitions(-D_WIN32_WINNT=0x0501) option (UPNPC_BUILD_TESTS "Build test executables" FALSE)
find_library(WINSOCK2_LIBRARY NAMES ws2_32 WS2_32 Ws2_32) option (UPNPC_BUILD_SAMPLE "Build sample executables" FALSE)
find_library(IPHLPAPI_LIBRARY NAMES iphlpapi) option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE)
set(LDLIBS ${WINSOCK2_LIBRARY} ${IPHLPAPI_LIBRARY} ${LDLIBS}) 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() endif()
if(APPLE) configure_file (${CMAKE_CURRENT_SOURCE_DIR}/miniupnpcstrings.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h)
add_definitions(-DMACOSX -D_DARWIN_C_SOURCE) target_include_directories(miniupnpc-private INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
endif()
set(SRCS src/igd_desc_parse.c set (MINIUPNPC_SOURCES
src/igd_desc_parse.c
src/miniupnpc.c src/miniupnpc.c
src/minixml.c src/minixml.c
src/minisoap.c src/minisoap.c
@ -30,10 +78,241 @@ set(SRCS src/igd_desc_parse.c
src/upnperrors.c src/upnperrors.c
src/connecthostport.c src/connecthostport.c
src/portlistingparse.c src/portlistingparse.c
src/receivedata.c) src/receivedata.c
src/addr_is_reserved.c
${CMAKE_CURRENT_BINARY_DIR}/miniupnpcstrings.h
)
add_library(miniupnpc STATIC ${SRCS}) if (WIN32)
dolphin_disable_warnings(miniupnpc) target_link_libraries(miniupnpc-private INTERFACE ws2_32 iphlpapi)
target_include_directories(miniupnpc PUBLIC src) 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 ()
add_library(Miniupnpc::miniupnpc ALIAS miniupnpc)
if (UPNPC_BUILD_STATIC)
add_library (libminiupnpc-static STATIC ${MINIUPNPC_SOURCES})
target_include_directories (libminiupnpc-static PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/miniupnpc>)
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 $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include/miniupnpc>)
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 $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
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
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
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()
# vim: ts=2:sw=2:expandtab

View file

@ -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. 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: 2015/09/15:
Fix buffer overflow in igd_desc_parse.c/IGDstartelt() Fix buffer overflow in igd_desc_parse.c/IGDstartelt()
Discovered by Aleksandar Nikolic of Cisco Talos Discovered by Aleksandar Nikolic of Cisco Talos
@ -565,7 +756,7 @@ VERSION 1.0 :
upnpc now displays external ip address with -s or -l upnpc now displays external ip address with -s or -l
2007/04/11: 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: 2007/03/19:
cleanup in miniwget.c cleanup in miniwget.c

View file

@ -1,27 +1 @@
MiniUPnPc ../LICENSE
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.

View file

@ -1,23 +1,19 @@
Project: miniupnp 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 github: https://github.com/miniupnp/miniupnp
freecode: http://freecode.com/projects/miniupnp
Author: Thomas Bernard 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 This software is subject to the conditions detailed in the
LICENSE file provided within this distribution. 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 * * miniUPnP Client - miniUPnPc *
To compile, simply run 'gmake' (could be 'make' on your system). To compile, simply run 'gmake' (could be 'make' on your system).
Under win32, to compile with MinGW, type "mingw32make.bat". Under win32, to compile with MinGW, type "mingw32make.bat".
MS Visual C solution and project files are supplied in the msvc/ subdirectory. 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, The compilation is known to work under linux, FreeBSD,
OpenBSD, MacOS X, AmigaOS and cygwin. 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, libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h,
upnpcommands.h and miniwget.h : upnpcommands.h and miniwget.h :
- upnpDiscover() - upnpDiscover()
- UPNP_GetValidIGD()
- miniwget() - miniwget()
- parserootdesc() - parserootdesc()
- GetUPNPUrls() - GetUPNPUrls()
@ -62,5 +59,34 @@ If you are using libminiupnpc in your application, please
send me an email ! send me an email !
For any question, you can use the web forum : 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

View file

@ -1 +1 @@
1.9 2.3.2

View file

@ -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 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 API version 14
miniupnpc.h miniupnpc.h
add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice() add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
@ -9,6 +47,8 @@ miniupnpc.h
getDevicesFromMiniSSDPD() : getDevicesFromMiniSSDPD() :
connectToMiniSSDPD() / disconnectFromMiniSSDPD() connectToMiniSSDPD() / disconnectFromMiniSSDPD()
requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD() requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD()
updated macro :
#define MINIUPNPC_API_VERSION 14
API version 13 API version 13
miniupnpc.h: miniupnpc.h:

View file

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

303
Externals/miniupnpc/include/miniupnpc.h vendored Normal file
View file

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

View file

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

View file

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

54
Externals/miniupnpc/include/miniwget.h vendored Normal file
View file

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

View file

@ -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 :
*
* <p:PortMappingEntry>
* <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
* <p:NewExternalPort>2345</p:NewExternalPort>
* <p:NewProtocol>TCP</p:NewProtocol>
* <p:NewInternalPort>2345</p:NewInternalPort>
* <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
* <p:NewEnabled>1</p:NewEnabled>
* <p:NewDescription>dooom</p:NewDescription>
* <p:NewLeaseTime>345</p:NewLeaseTime>
* </p:PortMappingEntry>
*/
#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

View file

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

58
Externals/miniupnpc/include/upnpdev.h vendored Normal file
View file

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

View file

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

View file

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

View file

@ -1,63 +1,249 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project> <Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\Source\VSProps\Base.Macros.props" /> <ItemGroup Label="ProjectConfigurations">
<Import Project="$(VSPropsDir)Base.Targets.props" /> <ProjectConfiguration Include="Debug Dll|Win32">
<Configuration>Debug Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release Dll|Win32">
<Configuration>Release Dll</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{31643FDB-1BB8-4965-9DE7-000FC88D35AE}</ProjectGuid> <ProjectGuid>{D28CE435-CB33-4BAE-8A52-C6EF915956F5}</ProjectGuid>
<RootNamespace>miniupnpc</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VSPropsDir)Configuration.StaticLibrary.props" /> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" /> <ImportGroup Label="ExtensionSettings">
<ImportGroup Label="PropertySheets"> </ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="$(VSPropsDir)Base.props" />
<Import Project="$(VSPropsDir)ClDisableAllWarnings.props" />
</ImportGroup> </ImportGroup>
<PropertyGroup Label="UserMacros" /> <PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>14.0.25123.0</_ProjectFileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<IntDir>$(Configuration)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Dll|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>true</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>EditAndContinue</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<Link>
<AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader />
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Dll|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;MINIUPNP_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>..;..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<PreBuildEvent>
<Command>genminiupnpcstrings.vbs</Command>
</PreBuildEvent>
<Link>
<AdditionalDependencies>ws2_32.lib;IPHlpApi.Lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ResourceCompile>
<PreprocessorDefinitions>INTERNAL_NAME="\"miniupnpc.dll\0\"";%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="src\codelength.h" /> <ClCompile Include="..\src\addr_is_reserved.c" />
<ClInclude Include="src\connecthostport.h" /> <ClCompile Include="..\src\connecthostport.c" />
<ClInclude Include="src\igd_desc_parse.h" /> <ClCompile Include="..\src\igd_desc_parse.c" />
<ClInclude Include="src\minisoap.h" /> <ClCompile Include="..\src\minisoap.c" />
<ClInclude Include="src\minissdpc.h" /> <ClCompile Include="..\src\minissdpc.c" />
<ClInclude Include="src\miniupnpc.h" /> <ClCompile Include="..\src\miniupnpc.c" />
<ClInclude Include="src\miniupnpc_declspec.h" /> <ClCompile Include="..\src\miniwget.c" />
<ClInclude Include="src\miniupnpcstrings.h" /> <ClCompile Include="..\src\minixml.c" />
<ClInclude Include="src\miniupnpctypes.h" /> <ClCompile Include="..\src\portlistingparse.c" />
<ClInclude Include="src\miniwget.h" /> <ClCompile Include="..\src\receivedata.c" />
<ClInclude Include="src\minixml.h" /> <ClCompile Include="..\src\upnpcommands.c" />
<ClInclude Include="src\portlistingparse.h" /> <ClCompile Include="..\src\upnpdev.c" />
<ClInclude Include="src\receivedata.h" /> <ClCompile Include="..\src\upnperrors.c" />
<ClInclude Include="src\upnpcommands.h" /> <ClCompile Include="..\src\upnpreplyparse.c" />
<ClInclude Include="src\upnpdev.h" />
<ClInclude Include="src\upnperrors.h" />
<ClInclude Include="src\upnpreplyparse.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="src\connecthostport.c" /> <ClInclude Include="..\src\addr_is_reserved.h" />
<ClCompile Include="src\igd_desc_parse.c" /> <ClInclude Include="..\src\connecthostport.h" />
<ClCompile Include="src\minisoap.c" /> <ClInclude Include="..\include\igd_desc_parse.h" />
<ClCompile Include="src\minissdpc.c" /> <ClInclude Include="..\src\minisoap.h" />
<ClCompile Include="src\miniupnpc.c" /> <ClInclude Include="..\src\minissdpc.h" />
<ClCompile Include="src\miniwget.c" /> <ClInclude Include="..\include\miniupnpc.h" />
<ClCompile Include="src\minixml.c" /> <ClInclude Include="..\miniupnpcstrings.h" />
<ClCompile Include="src\portlistingparse.c" /> <ClInclude Include="..\include\miniupnpctypes.h" />
<ClCompile Include="src\receivedata.c" /> <ClInclude Include="..\include\miniupnpc_declspec.h" />
<ClCompile Include="src\upnpcommands.c" /> <ClInclude Include="..\include\miniwget.h" />
<ClCompile Include="src\upnpdev.c" /> <ClInclude Include="..\src\miniwget_private.h" />
<ClCompile Include="src\upnperrors.c" /> <ClInclude Include="..\src\minixml.h" />
<ClCompile Include="src\upnpreplyparse.c" /> <ClInclude Include="..\include\portlistingparse.h" />
<ClInclude Include="..\src\receivedata.h" />
<ClInclude Include="..\include\upnpcommands.h" />
<ClInclude Include="..\include\upnpdev.h" />
<ClInclude Include="..\include\upnperrors.h" />
<ClInclude Include="..\include\upnpreplyparse.h" />
<ClInclude Include="..\src\win32_snprintf.h" />
<ClInclude Include="..\rc_version.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="LICENSE" /> <None Include="genminiupnpcstrings.vbs" />
<None Include="README" />
<None Include="VERSION" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Text Include="apiversions.txt" /> <ResourceCompile Include="../miniupnpc.rc" />
<Text Include="Changelog.txt" />
<Text Include="CMakeLists.txt" />
</ItemGroup> </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">

View file

@ -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 <winsock2.h>
#include <ws2tcpip.h>
#if !defined(_MSC_VER)
#include <stdint.h>
#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 <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif /* _WIN32 */
#ifdef DEBUG
#include <stdio.h>
#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;
}

View file

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

View file

@ -1,7 +1,8 @@
/* $Id: connecthostport.c,v 1.13 2014/03/31 12:36:36 nanard Exp $ */ /* $Id: connecthostport.c,v 1.24 2020/11/09 19:26:53 nanard Exp $ */
/* Project : miniupnp /* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas Bernard * 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 * This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */ * LICENCE file provided in this distribution. */
@ -18,11 +19,15 @@
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <io.h> #include <io.h>
#define MAXHOSTNAMELEN 64 #define MAXHOSTNAMELEN 64
#define snprintf _snprintf #include "win32_snprintf.h"
#define herror #define herror
#define socklen_t int #define socklen_t int
#else /* #ifdef _WIN32 */ #else /* #ifdef _WIN32 */
#include <unistd.h> #include <unistd.h>
#include <sys/types.h>
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
#include <sys/time.h>
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
#include <sys/param.h> #include <sys/param.h>
#include <sys/select.h> #include <sys/select.h>
#include <errno.h> #include <errno.h>
@ -32,20 +37,10 @@
/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions /* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
* during the connect() call */ * during the connect() call */
#define MINIUPNPC_IGNORE_EINTR #define MINIUPNPC_IGNORE_EINTR
#ifndef USE_GETHOSTBYNAME
#include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/select.h> #include <sys/select.h>
#endif /* #ifndef USE_GETHOSTBYNAME */
#endif /* #else _WIN32 */ #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__) #if defined(__amigaos__) || defined(__amigaos4__)
#define herror(A) printf("%s\n", A) #define herror(A) printf("%s\n", A)
#endif #endif
@ -59,10 +54,11 @@
/* connecthostport() /* connecthostport()
* return a socket connected (TCP) to the host and port * return a socket connected (TCP) to the host and port
* or -1 in case of error */ * or -1 in case of error */
int connecthostport(const char * host, unsigned short port, SOCKET connecthostport(const char * host, unsigned short port,
unsigned int scope_id) unsigned int scope_id)
{ {
int s, n; SOCKET s;
int n;
#ifdef USE_GETHOSTBYNAME #ifdef USE_GETHOSTBYNAME
struct sockaddr_in dest; struct sockaddr_in dest;
struct hostent *hp; struct hostent *hp;
@ -81,15 +77,15 @@ int connecthostport(const char * host, unsigned short port,
if(hp == NULL) if(hp == NULL)
{ {
herror(host); herror(host);
return -1; return INVALID_SOCKET;
} }
memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr)); memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
memset(dest.sin_zero, 0, sizeof(dest.sin_zero)); memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
s = socket(PF_INET, SOCK_STREAM, 0); s = socket(PF_INET, SOCK_STREAM, 0);
if(s < 0) if(ISINVALID(s))
{ {
PRINT_SOCKET_ERROR("socket"); PRINT_SOCKET_ERROR("socket");
return -1; return INVALID_SOCKET;
} }
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
/* setting a 3 seconds timeout for the connect() call */ /* 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; timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 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_sec = 3;
timeout.tv_usec = 0; timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 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 */ #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
dest.sin_family = AF_INET; 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 /* EINTR The system call was interrupted by a signal that was caught
* EINPROGRESS The socket is nonblocking and the connection cannot * EINPROGRESS The socket is nonblocking and the connection cannot
* be completed immediately. */ * be completed immediately. */
while(n < 0 && (errno == EINTR || errno = EINPROGRESS)) while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
{ {
socklen_t len; socklen_t len;
fd_set wset; fd_set wset;
int err; int err;
FD_ZERO(&wset); FD_ZERO(&wset);
FD_SET(s, &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; continue;
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
if(n == 0) {
errno = ETIMEDOUT;
n = -1;
break;
}
#endif
/*len = 0;*/ /*len = 0;*/
/*n = getpeername(s, NULL, &len);*/ /*n = getpeername(s, NULL, &len);*/
len = sizeof(err); len = sizeof(err);
if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
PRINT_SOCKET_ERROR("getsockopt"); PRINT_SOCKET_ERROR("getsockopt");
closesocket(s); closesocket(s);
return -1; return INVALID_SOCKET;
} }
if(err != 0) { if(err != 0) {
errno = err; errno = err;
@ -140,7 +150,7 @@ int connecthostport(const char * host, unsigned short port,
{ {
PRINT_SOCKET_ERROR("connect"); PRINT_SOCKET_ERROR("connect");
closesocket(s); closesocket(s);
return -1; return INVALID_SOCKET;
} }
#else /* #ifdef USE_GETHOSTBYNAME */ #else /* #ifdef USE_GETHOSTBYNAME */
/* use getaddrinfo() instead of 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++) for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)
{ {
tmp_host[i] = host[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" */ j+=2; /* skip "25" */
} }
tmp_host[i] = '\0'; tmp_host[i] = '\0';
@ -178,13 +188,19 @@ int connecthostport(const char * host, unsigned short port,
#else #else
fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n)); fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
#endif #endif
return -1; return INVALID_SOCKET;
} }
s = -1; s = INVALID_SOCKET;
for(p = ai; p; p = p->ai_next) 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); s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(s < 0) if(ISINVALID(s))
continue; continue;
if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) { if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {
struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr; 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"); PRINT_SOCKET_ERROR("setsockopt");
} }
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ #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 #ifdef MINIUPNPC_IGNORE_EINTR
/* EINTR The system call was interrupted by a signal that was caught /* EINTR The system call was interrupted by a signal that was caught
* EINPROGRESS The socket is nonblocking and the connection cannot * EINPROGRESS The socket is nonblocking and the connection cannot
@ -217,8 +233,22 @@ int connecthostport(const char * host, unsigned short port,
int err; int err;
FD_ZERO(&wset); FD_ZERO(&wset);
FD_SET(s, &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; continue;
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
if(n == 0) {
errno = ETIMEDOUT;
n = -1;
break;
}
#endif
/*len = 0;*/ /*len = 0;*/
/*n = getpeername(s, NULL, &len);*/ /*n = getpeername(s, NULL, &len);*/
len = sizeof(err); len = sizeof(err);
@ -226,7 +256,7 @@ int connecthostport(const char * host, unsigned short port,
PRINT_SOCKET_ERROR("getsockopt"); PRINT_SOCKET_ERROR("getsockopt");
closesocket(s); closesocket(s);
freeaddrinfo(ai); freeaddrinfo(ai);
return -1; return INVALID_SOCKET;
} }
if(err != 0) { if(err != 0) {
errno = err; errno = err;
@ -234,28 +264,21 @@ int connecthostport(const char * host, unsigned short port,
} }
} }
#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */ #endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
if(n < 0) if(n >= 0) /* connect() was successful */
{
closesocket(s);
continue;
}
else
{
break; break;
} }
}
freeaddrinfo(ai); freeaddrinfo(ai);
if(s < 0) if(ISINVALID(s))
{ {
PRINT_SOCKET_ERROR("socket"); PRINT_SOCKET_ERROR("socket");
return -1; return INVALID_SOCKET;
} }
if(n < 0) if(n < 0)
{ {
PRINT_SOCKET_ERROR("connect"); PRINT_SOCKET_ERROR("connect");
return -1; closesocket(s);
return INVALID_SOCKET;
} }
#endif /* #ifdef USE_GETHOSTBYNAME */ #endif /* #ifdef USE_GETHOSTBYNAME */
return s; return s;
} }

View file

@ -2,16 +2,18 @@
/* Project: miniupnp /* Project: miniupnp
* http://miniupnp.free.fr/ * http://miniupnp.free.fr/
* Author: Thomas Bernard * Author: Thomas Bernard
* Copyright (c) 2010-2012 Thomas Bernard * Copyright (c) 2010-2018 Thomas Bernard
* This software is subjects to the conditions detailed * This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */ * in the LICENCE file provided within this distribution */
#ifndef CONNECTHOSTPORT_H_INCLUDED #ifndef CONNECTHOSTPORT_H_INCLUDED
#define CONNECTHOSTPORT_H_INCLUDED #define CONNECTHOSTPORT_H_INCLUDED
#include "miniupnpc_socketdef.h"
/* connecthostport() /* connecthostport()
* return a socket connected (TCP) to the host and port * return a socket connected (TCP) to the host and port
* or -1 in case of error */ * or INVALID_SOCKET in case of error */
int connecthostport(const char * host, unsigned short port, SOCKET connecthostport(const char * host, unsigned short port,
unsigned int scope_id); unsigned int scope_id);
#endif #endif

View file

@ -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 /* Project : miniupnp
* http://miniupnp.free.fr/ * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas Bernard * 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 * This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. * LICENCE file provided in this distribution.
* */ * */
#ifndef IGD_DESC_PARSE_H_INCLUDED #ifndef IGD_DESC_PARSE_H_INCLUDED
#define IGD_DESC_PARSE_H_INCLUDED #define IGD_DESC_PARSE_H_INCLUDED
/* Structure to store the result of the parsing of UPnP /*! \file igd_desc_parse.h
* descriptions of Internet Gateway Devices */ * \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) #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 { struct IGDdatas_service {
/*! \brief controlURL for the service */
char controlurl[MINIUPNPC_URL_MAXSIZE]; char controlurl[MINIUPNPC_URL_MAXSIZE];
/*! \brief eventSubURL for the service */
char eventsuburl[MINIUPNPC_URL_MAXSIZE]; char eventsuburl[MINIUPNPC_URL_MAXSIZE];
/*! \brief SCPDURL for the service */
char scpdurl[MINIUPNPC_URL_MAXSIZE]; char scpdurl[MINIUPNPC_URL_MAXSIZE];
/*! \brief serviceType */
char servicetype[MINIUPNPC_URL_MAXSIZE]; char servicetype[MINIUPNPC_URL_MAXSIZE];
/*char devicetype[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 { struct IGDdatas {
/*! \brief current element name */
char cureltname[MINIUPNPC_URL_MAXSIZE]; char cureltname[MINIUPNPC_URL_MAXSIZE];
/*! \brief URLBase */
char urlbase[MINIUPNPC_URL_MAXSIZE]; char urlbase[MINIUPNPC_URL_MAXSIZE];
/*! \brief presentationURL */
char presentationurl[MINIUPNPC_URL_MAXSIZE]; char presentationurl[MINIUPNPC_URL_MAXSIZE];
/*! \brief depth into the XML tree */
int level; int level;
/*int state;*/ /*! \brief "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
/* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
struct IGDdatas_service CIF; struct IGDdatas_service CIF;
/* "urn:schemas-upnp-org:service:WANIPConnection:1" /*! \brief first of "urn:schemas-upnp-org:service:WANIPConnection:1"
* "urn:schemas-upnp-org:service:WANPPPConnection:1" */ * or "urn:schemas-upnp-org:service:WANPPPConnection:1" */
struct IGDdatas_service first; 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; struct IGDdatas_service second;
/* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */ /*! \brief "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
struct IGDdatas_service IPv6FC; struct IGDdatas_service IPv6FC;
/* tmp */ /*! \brief currently parsed service */
struct IGDdatas_service tmp; struct IGDdatas_service tmp;
}; };
/*!
* \brief XML start element handler
*/
void IGDstartelt(void *, const char *, int); void IGDstartelt(void *, const char *, int);
/*!
* \brief XML end element handler
*/
void IGDendelt(void *, const char *, int); void IGDendelt(void *, const char *, int);
/*!
* \brief XML characted data handler
*/
void IGDdata(void *, const char *, int); void IGDdata(void *, const char *, int);
#ifdef DEBUG #ifdef DEBUG
void printIGD(struct IGDdatas *); void printIGD(struct IGDdatas *);

198
Externals/miniupnpc/src/listdevices.c vendored Normal file
View file

@ -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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <winsock2.h>
#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 <device1> <device2> ...]\n", argv[0]);
printf("options :\n");
printf(" -6 : use IPv6\n");
printf(" -m address/ifname : network interface to use for multicast\n");
printf(" -d <device string> : search only for this type of device\n");
printf(" -l <device1> <device2> ... : 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;
}

View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#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;
}

View file

@ -1,7 +1,8 @@
/* $Id: minisoap.c,v 1.23 2014/11/04 22:31:55 nanard Exp $ */ /* $Id: minisoap.c,v 1.32 2023/07/05 22:43:50 nanard Exp $ */
/* Project : miniupnp /* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas Bernard * 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 * This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. * LICENCE file provided in this distribution.
* *
@ -12,7 +13,7 @@
#ifdef _WIN32 #ifdef _WIN32
#include <io.h> #include <io.h>
#include <winsock2.h> #include <winsock2.h>
#define snprintf _snprintf #include "win32_snprintf.h"
#else #else
#include <unistd.h> #include <unistd.h>
#include <sys/types.h> #include <sys/types.h>
@ -24,16 +25,10 @@
/* only for malloc */ /* only for malloc */
#include <stdlib.h> #include <stdlib.h>
#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 /* httpWrite sends the headers and the body to the socket
* and returns the number of bytes sent */ * and returns the number of bytes sent */
static int static int
httpWrite(int fd, const char * body, int bodysize, httpWrite(SOCKET fd, const char * body, int bodysize,
const char * headers, int headerssize) const char * headers, int headerssize)
{ {
int n = 0; int n = 0;
@ -43,10 +38,10 @@ httpWrite(int fd, const char * body, int bodysize,
/* Note : my old linksys router only took into account /* Note : my old linksys router only took into account
* soap request that are sent into only one packet */ * soap request that are sent into only one packet */
char * p; char * p;
/* TODO: AVOID MALLOC */ /* TODO: AVOID MALLOC, we could use writev() for that */
p = malloc(headerssize+bodysize); p = malloc(headerssize+bodysize);
if(!p) if(!p)
return 0; return -1;
memcpy(p, headers, headerssize); memcpy(p, headers, headerssize);
memcpy(p+headerssize, body, bodysize); memcpy(p+headerssize, body, bodysize);
/*n = write(fd, p, headerssize+bodysize);*/ /*n = write(fd, p, headerssize+bodysize);*/
@ -55,7 +50,7 @@ httpWrite(int fd, const char * body, int bodysize,
PRINT_SOCKET_ERROR("send"); PRINT_SOCKET_ERROR("send");
} }
/* disable send on the socket */ /* disable send on the socket */
/* draytek routers dont seems to like that... */ /* draytek routers don't seem to like that... */
#if 0 #if 0
#ifdef _WIN32 #ifdef _WIN32
if(shutdown(fd, SD_SEND)<0) { if(shutdown(fd, SD_SEND)<0) {
@ -70,7 +65,7 @@ httpWrite(int fd, const char * body, int bodysize,
} }
/* self explanatory */ /* self explanatory */
int soapPostSubmit(int fd, int soapPostSubmit(SOCKET fd,
const char * url, const char * url,
const char * host, const char * host,
unsigned short port, unsigned short port,
@ -78,33 +73,37 @@ int soapPostSubmit(int fd,
const char * body, const char * body,
const char * httpversion) const char * httpversion)
{ {
int bodysize;
char headerbuf[512]; char headerbuf[512];
int headerssize; int headerssize;
char portstr[8]; char portstr[8];
bodysize = (int)strlen(body); int bodysize = (int)strlen(body);
/* We are not using keep-alive HTTP connections. /* We are not using keep-alive HTTP connections.
* HTTP/1.1 needs the header Connection: close to do that. * HTTP/1.1 needs the header Connection: close to do that.
* This is the default with HTTP/1.0 * This is the default with HTTP/1.0
* Using HTTP/1.1 means we need to support chunked transfer-encoding : * Using HTTP/1.1 means we need to support chunked transfer-encoding :
* When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
* transfer encoding. */ * 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'; portstr[0] = '\0';
if(port != 80) if(port != 80)
snprintf(portstr, sizeof(portstr), ":%hu", port); snprintf(portstr, sizeof(portstr), ":%hu", port);
headerssize = snprintf(headerbuf, sizeof(headerbuf), headerssize = snprintf(headerbuf, sizeof(headerbuf),
"POST %s HTTP/%s\r\n" "POST %s HTTP/%s\r\n"
"Host: %s%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" "Content-Length: %d\r\n"
#if (UPNP_VERSION_MAJOR == 1) && (UPNP_VERSION_MINOR == 0)
"Content-Type: text/xml\r\n" "Content-Type: text/xml\r\n"
#else
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
#endif
"SOAPAction: \"%s\"\r\n" "SOAPAction: \"%s\"\r\n"
"Connection: Close\r\n" "Connection: close\r\n"
"Cache-Control: no-cache\r\n" /* ??? */ "Cache-Control: no-cache\r\n" /* ??? */
"Pragma: no-cache\r\n"
"\r\n", "\r\n",
url, httpversion, host, portstr, bodysize, action); url, httpversion, host, portstr, bodysize, action);
if ((unsigned int)headerssize >= sizeof(headerbuf))
return -1;
#ifdef DEBUG #ifdef DEBUG
/*printf("SOAP request : headersize=%d bodysize=%d\n", /*printf("SOAP request : headersize=%d bodysize=%d\n",
headerssize, bodysize); headerssize, bodysize);

View file

@ -1,14 +1,16 @@
/* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */ /* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */
/* Project : miniupnp /* Project : miniupnp
* Author : Thomas Bernard * Author : Thomas Bernard
* Copyright (c) 2005 Thomas Bernard * Copyright (c) 2005-2018 Thomas Bernard
* This software is subject to the conditions detailed in the * This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */ * LICENCE file provided in this distribution. */
#ifndef MINISOAP_H_INCLUDED #ifndef MINISOAP_H_INCLUDED
#define MINISOAP_H_INCLUDED #define MINISOAP_H_INCLUDED
#include "miniupnpc_socketdef.h"
/*int httpWrite(int, const char *, int, const char *);*/ /*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 *); const char *, const char *, const char *);
#endif #endif

View file

@ -1,23 +1,26 @@
/* $Id: minissdpc.c,v 1.28 2015/09/18 13:05:39 nanard Exp $ */ /* $Id: minissdpc.c,v 1.52 2025/01/12 15:47:17 nanard Exp $ */
/* Project : miniupnp /* vim: tabstop=4 shiftwidth=4 noexpandtab
* Web : http://miniupnp.free.fr/ * Project : miniupnp
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas BERNARD * 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 * This software is subjet to the conditions detailed in the
* provided LICENCE file. */ * provided LICENCE file. */
/*#include <syslog.h>*/
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h>
#include <sys/types.h> #include <sys/types.h>
#if defined (__NetBSD__)
#include <net/if.h>
#endif
#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) #if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <io.h> #include <io.h>
#include <iphlpapi.h> #include <iphlpapi.h>
#include <winsock.h> #include "win32_snprintf.h"
#define snprintf _snprintf
#if !defined(_MSC_VER) #if !defined(_MSC_VER)
#include <stdint.h> #include <stdint.h>
#else /* !defined(_MSC_VER) */ #else /* !defined(_MSC_VER) */
@ -30,6 +33,15 @@ typedef unsigned short uint16_t;
#define strncasecmp memicmp #define strncasecmp memicmp
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */ #endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
#endif /* #ifndef strncasecmp */ #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 */ #endif /* _WIN32 */
#if defined(__amigaos__) || defined(__amigaos4__) #if defined(__amigaos__) || defined(__amigaos4__)
#include <sys/socket.h> #include <sys/socket.h>
@ -57,14 +69,17 @@ struct sockaddr_un {
#define closesocket close #define closesocket close
#endif #endif
#ifdef _WIN32 #include "miniupnpc_socketdef.h"
#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
#else #if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__) && !defined(__HAIKU__)
#define PRINT_SOCKET_ERROR(x) perror(x) #define HAS_IP_MREQN
#endif #endif
#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__) #ifndef _WIN32
#define HAS_IP_MREQN #include <sys/ioctl.h>
#if defined(__sun) || defined(__HAIKU__)
#include <sys/sockio.h>
#endif
#endif #endif
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN) #if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
@ -84,6 +99,7 @@ struct ip_mreqn
#endif #endif
#include "minissdpc.h" #include "minissdpc.h"
#include "miniupnpc.h"
#include "receivedata.h" #include "receivedata.h"
#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) #if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
@ -160,7 +176,7 @@ connectToMiniSSDPD(const char * socketpath)
{ {
int s; int s;
struct sockaddr_un addr; struct sockaddr_un addr;
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT #if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
struct timeval timeout; struct timeval timeout;
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
@ -171,23 +187,25 @@ connectToMiniSSDPD(const char * socketpath)
perror("socket(unix)"); perror("socket(unix)");
return MINISSDPC_SOCKET_ERROR; return MINISSDPC_SOCKET_ERROR;
} }
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT #if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
/* setting a 3 seconds timeout */ /* setting a 3 seconds timeout */
/* not supported for AF_UNIX sockets under Solaris */
timeout.tv_sec = 3; timeout.tv_sec = 3;
timeout.tv_usec = 0; timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 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_sec = 3;
timeout.tv_usec = 0; timeout.tv_usec = 0;
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 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 */ #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
if(!socketpath) if(!socketpath)
socketpath = "/var/run/minissdpd.sock"; socketpath = "/var/run/minissdpd.sock";
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path)); strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
/* TODO : check if we need to handle the EINTR */ /* TODO : check if we need to handle the EINTR */
@ -323,7 +341,7 @@ receiveDevicesFromMiniSSDPD(int s, int * error)
#ifdef DEBUG #ifdef DEBUG
printf(" usnsize=%u\n", usnsize); printf(" usnsize=%u\n", usnsize);
#endif /* DEBUG */ #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(tmp == NULL) {
if (error) if (error)
*error = MINISSDPC_MEMORY_ERROR; *error = MINISSDPC_MEMORY_ERROR;
@ -372,6 +390,7 @@ free_tmp_and_return:
* the last 4 arguments are filled during the parsing : * the last 4 arguments are filled during the parsing :
* - location/locationsize : "location:" field of the SSDP reply packet * - location/locationsize : "location:" field of the SSDP reply packet
* - st/stsize : "st:" 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 */ * The strings are NOT null terminated */
static void static void
parseMSEARCHReply(const char * reply, int size, parseMSEARCHReply(const char * reply, int size,
@ -409,17 +428,17 @@ parseMSEARCHReply(const char * reply, int size,
putchar('\n');*/ putchar('\n');*/
/* skip the colon and white spaces */ /* skip the colon and white spaces */
do { b++; } while(reply[b]==' '); do { b++; } while(reply[b]==' ');
if(0==strncasecmp(reply+a, "location", 8)) if(0==strncasecmp(reply+a, "location:", 9))
{ {
*location = reply+b; *location = reply+b;
*locationsize = i-b; *locationsize = i-b;
} }
else if(0==strncasecmp(reply+a, "st", 2)) else if(0==strncasecmp(reply+a, "st:", 3))
{ {
*st = reply+b; *st = reply+b;
*stsize = i-b; *stsize = i-b;
} }
else if(0==strncasecmp(reply+a, "usn", 3)) else if(0==strncasecmp(reply+a, "usn:", 4))
{ {
*usn = reply+b; *usn = reply+b;
*usnsize = i-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 */ /* port upnp discover : SSDP protocol */
#define PORT 1900 #define SSDP_PORT 1900
#define XSTR(s) STR(s) #define XSTR(s) STR(s)
#define STR(s) #s #define STR(s) #s
#define UPNP_MCAST_ADDR "239.255.255.250" #define UPNP_MCAST_ADDR "239.255.255.250"
@ -456,25 +520,25 @@ parseMSEARCHReply(const char * reply, int size,
struct UPNPDev * struct UPNPDev *
ssdpDiscoverDevices(const char * const deviceTypes[], ssdpDiscoverDevices(const char * const deviceTypes[],
int delay, const char * multicastif, int delay, const char * multicastif,
int sameport, int localport,
int ipv6, unsigned char ttl, int ipv6, unsigned char ttl,
int * error, int * error,
int searchalltypes) int searchalltypes)
{ {
struct UPNPDev * tmp; struct UPNPDev * tmp;
struct UPNPDev * devlist = 0; struct UPNPDev * devlist = NULL;
unsigned int scope_id = 0; unsigned int scope_id = 0;
int opt = 1; int opt = 1;
static const char MSearchMsgFmt[] = static const char MSearchMsgFmt[] =
"M-SEARCH * HTTP/1.1\r\n" "M-SEARCH * HTTP/1.1\r\n"
"HOST: %s:" XSTR(PORT) "\r\n" "HOST: %s:" XSTR(SSDP_PORT) "\r\n"
"ST: %s\r\n" "ST: %s\r\n"
"MAN: \"ssdp:discover\"\r\n" "MAN: \"ssdp:discover\"\r\n"
"MX: %u\r\n" "MX: %u\r\n"
"\r\n"; "\r\n";
int deviceIndex; int deviceIndex;
char bufr[1536]; /* reception and emission buffer */ char bufr[1536]; /* reception and emission buffer */
int sudp; SOCKET sudp;
int n; int n;
struct sockaddr_storage sockudp_r; struct sockaddr_storage sockudp_r;
unsigned int mx; unsigned int mx;
@ -482,23 +546,26 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
struct sockaddr_storage sockudp_w; struct sockaddr_storage sockudp_w;
#else #else
int rv; int rv;
struct addrinfo hints, *servinfo, *p; struct addrinfo hints, *servinfo;
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
MIB_IPFORWARDROW ip_forward;
unsigned long _ttl = (unsigned long)ttl; unsigned long _ttl = (unsigned long)ttl;
#endif #endif
int linklocal = 1; int linklocal = 0; /* try first with site-local multicast */
int sentok;
if(error) if(error)
*error = MINISSDPC_UNKNOWN_ERROR; *error = MINISSDPC_UNKNOWN_ERROR;
if(localport==UPNP_LOCAL_PORT_SAME)
localport = SSDP_PORT;
#ifdef _WIN32 #ifdef _WIN32
sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP); sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
#else #else
sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0); sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
#endif #endif
if(sudp < 0) if(ISINVALID(sudp))
{ {
if(error) if(error)
*error = MINISSDPC_SOCKET_ERROR; *error = MINISSDPC_SOCKET_ERROR;
@ -510,14 +577,14 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
if(ipv6) { if(ipv6) {
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r; struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
p->sin6_family = AF_INET6; p->sin6_family = AF_INET6;
if(sameport) if(localport > 0 && localport < 65536)
p->sin6_port = htons(PORT); p->sin6_port = htons((unsigned short)localport);
p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */ p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
} else { } else {
struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r; struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
p->sin_family = AF_INET; p->sin_family = AF_INET;
if(sameport) if(localport > 0 && localport < 65536)
p->sin_port = htons(PORT); p->sin_port = htons((unsigned short)localport);
p->sin_addr.s_addr = INADDR_ANY; p->sin_addr.s_addr = INADDR_ANY;
} }
#ifdef _WIN32 #ifdef _WIN32
@ -525,61 +592,98 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
* SSDP multicast traffic */ * SSDP multicast traffic */
/* Get IP associated with the index given in the ip_forward struct /* 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) */ * in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
if(!ipv6 if(!ipv6) {
&& (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) { DWORD ifbestidx;
DWORD dwRetVal = 0; #if _WIN32_WINNT >= _WIN32_WINNT_VISTA
PMIB_IPADDRTABLE pIPAddrTable; // While we don't need IPv6 support, the IPv4 only function is not available in UWP apps.
DWORD dwSize = 0; SOCKADDR_IN destAddr;
#ifdef DEBUG memset(&destAddr, 0, sizeof(destAddr));
IN_ADDR IPAddr; 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 #endif
int i; DWORD dwRetVal = NO_ERROR;
#ifdef DEBUG PIP_ADAPTER_ADDRESSES pAddresses = NULL;
printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop); ULONG outBufLen = 15360;
#endif int Iterations;
pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE)); PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
if(pIPAddrTable) { PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
free(pIPAddrTable); for (Iterations = 0; Iterations < 3; Iterations++) {
pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize); 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;
} }
if(pIPAddrTable) { HeapFree(GetProcessHeap(), 0, pAddresses);
dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 ); pAddresses = NULL;
}
if (dwRetVal == NO_ERROR) { if (dwRetVal == NO_ERROR) {
pCurrAddresses = pAddresses;
while (pCurrAddresses) {
#ifdef DEBUG #ifdef DEBUG
printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries); int i;
#endif PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) { PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
#ifdef DEBUG
printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex); printf("\tIfIndex (IPv4 interface): %u\n", pCurrAddresses->IfIndex);
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr; printf("\tAdapter name: %s\n", pCurrAddresses->AdapterName);
printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); pUnicast = pCurrAddresses->FirstUnicastAddress;
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask; if (pUnicast != NULL) {
printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) ); for (i = 0; pUnicast != NULL; i++) {
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr; printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(((PSOCKADDR_IN)pUnicast->Address.lpSockaddr)->sin_addr) );
printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr); pUnicast = pUnicast->Next;
printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize); }
printf("\tType and State[%d]:", i); 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"); printf("\n");
#endif #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 */ /* Set the address of this interface to be used */
struct in_addr mc_if; struct in_addr mc_if;
memset(&mc_if, 0, sizeof(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) { if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
PRINT_SOCKET_ERROR("setsockopt"); 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 #ifndef DEBUG
break; break;
#endif #endif
} }
pCurrAddresses = pCurrAddresses->Next;
} }
} }
free(pIPAddrTable); if (pAddresses != NULL) {
pIPAddrTable = NULL; HeapFree(GetProcessHeap(), 0, pAddresses);
pAddresses = NULL;
}
} }
} }
#endif /* _WIN32 */ #endif /* _WIN32 */
@ -593,9 +697,21 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
if(error) if(error)
*error = MINISSDPC_SOCKET_ERROR; *error = MINISSDPC_SOCKET_ERROR;
PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)"); PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)");
return NULL; goto error;
} }
if(ipv6) {
#ifdef _WIN32
DWORD mcastHops = ttl;
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (const char *)&mcastHops, sizeof(mcastHops)) < 0)
#else /* _WIN32 */
int mcastHops = ttl;
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &mcastHops, sizeof(mcastHops)) < 0)
#endif /* _WIN32 */
{
PRINT_SOCKET_ERROR("setsockopt(IPV6_MULTICAST_HOPS,...)");
}
} else {
#ifdef _WIN32 #ifdef _WIN32
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0) if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
#else /* _WIN32 */ #else /* _WIN32 */
@ -605,8 +721,9 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
/* not a fatal error */ /* not a fatal error */
PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)"); PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
} }
}
if(multicastif) if(multicastif && multicastif[0] != '\0')
{ {
if(ipv6) { if(ipv6) {
#if !defined(_WIN32) #if !defined(_WIN32)
@ -614,9 +731,16 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
* MS Windows Vista and MS Windows Server 2008. * MS Windows Vista and MS Windows Server 2008.
* http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */ * http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */ 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) if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
{ {
PRINT_SOCKET_ERROR("setsockopt"); PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
} }
#else #else
#ifdef DEBUG #ifdef DEBUG
@ -625,29 +749,67 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
#endif #endif
} else { } else {
struct in_addr mc_if; 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) if(mc_if.s_addr != INADDR_NONE)
{ {
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr; ((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) 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 { } else {
#ifdef HAS_IP_MREQN
/* was not an ip address, try with an interface name */ /* 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 */ 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)); memset(&reqn, 0, sizeof(struct ip_mreqn));
reqn.imr_address.s_addr = mc_if.s_addr;
reqn.imr_ifindex = if_nametoindex(multicastif); 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) 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 #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 #ifdef DEBUG
printf("Setting of multicast interface not supported with interface name.\n"); printf("Setting of multicast interface not supported with interface name.\n");
#endif #endif
#endif #endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */
} }
} }
} }
@ -674,6 +836,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
} }
/* receiving SSDP response packet */ /* receiving SSDP response packet */
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) { for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
sentok = 0;
/* sending the SSDP M-SEARCH packet */ /* sending the SSDP M-SEARCH packet */
n = snprintf(bufr, sizeof(bufr), n = snprintf(bufr, sizeof(bufr),
MSearchMsgFmt, MSearchMsgFmt,
@ -681,6 +844,11 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
(linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]") (linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
: UPNP_MCAST_ADDR, : UPNP_MCAST_ADDR,
deviceTypes[deviceIndex], mx); deviceTypes[deviceIndex], mx);
if ((unsigned int)n >= sizeof(bufr)) {
if(error)
*error = MINISSDPC_MEMORY_ERROR;
goto error;
}
#ifdef DEBUG #ifdef DEBUG
/*printf("Sending %s", bufr);*/ /*printf("Sending %s", bufr);*/
printf("Sending M-SEARCH request to %s with ST: %s\n", printf("Sending M-SEARCH request to %s with ST: %s\n",
@ -696,14 +864,14 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
if(ipv6) { if(ipv6) {
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w; struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
p->sin6_family = AF_INET6; p->sin6_family = AF_INET6;
p->sin6_port = htons(PORT); p->sin6_port = htons(SSDP_PORT);
inet_pton(AF_INET6, inet_pton(AF_INET6,
linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR, linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
&(p->sin6_addr)); &(p->sin6_addr));
} else { } else {
struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w; struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
p->sin_family = AF_INET; 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); p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
} }
n = sendto(sudp, bufr, n, 0, &sockudp_w, n = sendto(sudp, bufr, n, 0, &sockudp_w,
@ -712,7 +880,8 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
if(error) if(error)
*error = MINISSDPC_SOCKET_ERROR; *error = MINISSDPC_SOCKET_ERROR;
PRINT_SOCKET_ERROR("sendto"); PRINT_SOCKET_ERROR("sendto");
break; } else {
sentok = 1;
} }
#else /* #ifdef NO_GETADDRINFO */ #else /* #ifdef NO_GETADDRINFO */
memset(&hints, 0, sizeof(hints)); memset(&hints, 0, sizeof(hints));
@ -722,7 +891,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
if ((rv = getaddrinfo(ipv6 if ((rv = getaddrinfo(ipv6
? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR) ? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
: UPNP_MCAST_ADDR, : UPNP_MCAST_ADDR,
XSTR(PORT), &hints, &servinfo)) != 0) { XSTR(SSDP_PORT), &hints, &servinfo)) != 0) {
if(error) if(error)
*error = MINISSDPC_SOCKET_ERROR; *error = MINISSDPC_SOCKET_ERROR;
#ifdef _WIN32 #ifdef _WIN32
@ -731,32 +900,38 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
#endif #endif
break; break;
} } else {
struct addrinfo *p;
for(p = servinfo; p; p = p->ai_next) { for(p = servinfo; p; p = p->ai_next) {
n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen); n = sendto(sudp, bufr, n, 0, p->ai_addr, MSC_CAST_INT p->ai_addrlen);
if (n < 0) { if (n < 0) {
#ifdef DEBUG #ifdef DEBUG
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf, if (getnameinfo(p->ai_addr, (socklen_t)p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) { sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf); fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
} }
#endif #endif
PRINT_SOCKET_ERROR("sendto"); PRINT_SOCKET_ERROR("sendto");
continue; continue;
} else {
sentok = 1;
} }
} }
freeaddrinfo(servinfo); freeaddrinfo(servinfo);
if(n < 0) { }
if(!sentok) {
if(error) if(error)
*error = MINISSDPC_SOCKET_ERROR; *error = MINISSDPC_SOCKET_ERROR;
break;
} }
#endif /* #ifdef NO_GETADDRINFO */ #endif /* #ifdef NO_GETADDRINFO */
/* Waiting for SSDP REPLY packet to M-SEARCH /* Waiting for SSDP REPLY packet to M-SEARCH
* if searchalltypes is set, enter the loop only * if searchalltypes is set, enter the loop only
* when the last deviceType is reached */ * when the last deviceType is reached */
if(!searchalltypes || !deviceTypes[deviceIndex + 1]) do { 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); n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
if (n < 0) { if (n < 0) {
/* error */ /* error */
@ -788,11 +963,11 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
stsize, st, usnsize, (usn?usn:""), urlsize, descURL); stsize, st, usnsize, (usn?usn:""), urlsize, descURL);
#endif /* DEBUG */ #endif /* DEBUG */
for(tmp=devlist; tmp; tmp = tmp->pNext) { for(tmp=devlist; tmp; tmp = tmp->pNext) {
if(memcmp(tmp->descURL, descURL, urlsize) == 0 && if(strncmp(tmp->descURL, descURL, urlsize) == 0 &&
tmp->descURL[urlsize] == '\0' && tmp->descURL[urlsize] == '\0' &&
memcmp(tmp->st, st, stsize) == 0 && strncmp(tmp->st, st, stsize) == 0 &&
tmp->st[stsize] == '\0' && tmp->st[stsize] == '\0' &&
(usnsize == 0 || memcmp(tmp->usn, usn, usnsize) == 0) && (usnsize == 0 || strncmp(tmp->usn, usn, usnsize) == 0) &&
tmp->usn[usnsize] == '\0') tmp->usn[usnsize] == '\0')
break; break;
} }
@ -800,7 +975,7 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
* no duplicate device was found */ * no duplicate device was found */
if(tmp) if(tmp)
continue; continue;
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize); tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3);
if(!tmp) { if(!tmp) {
/* memory allocation error */ /* memory allocation error */
if(error) if(error)
@ -821,15 +996,24 @@ ssdpDiscoverDevices(const char * const deviceTypes[],
tmp->scope_id = scope_id; tmp->scope_id = scope_id;
devlist = tmp; devlist = tmp;
} }
if (upnp_gettimeofday(&current) >= 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) { if(ipv6) {
/* switch linklocal flag */ /* switch linklocal flag */
if(linklocal) { if(linklocal) {
linklocal = 0; linklocal = 0;
--deviceIndex;
} else { } else {
/* try again with linklocal multicast */
linklocal = 1; linklocal = 1;
--deviceIndex;
} }
} }
} }
@ -837,4 +1021,3 @@ error:
closesocket(sudp); closesocket(sudp);
return devlist; return devlist;
} }

View file

@ -32,20 +32,20 @@ MINIUPNP_LIBSPEC int
connectToMiniSSDPD(const char * socketpath); connectToMiniSSDPD(const char * socketpath);
MINIUPNP_LIBSPEC int MINIUPNP_LIBSPEC int
disconnectFromMiniSSDPD(int fd); disconnectFromMiniSSDPD(int s);
MINIUPNP_LIBSPEC int MINIUPNP_LIBSPEC int
requestDevicesFromMiniSSDPD(int fd, const char * devtype); requestDevicesFromMiniSSDPD(int s, const char * devtype);
MINIUPNP_LIBSPEC struct UPNPDev * MINIUPNP_LIBSPEC struct UPNPDev *
receiveDevicesFromMiniSSDPD(int fd, int * error); receiveDevicesFromMiniSSDPD(int s, int * error);
#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */ #endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
MINIUPNP_LIBSPEC struct UPNPDev * MINIUPNP_LIBSPEC struct UPNPDev *
ssdpDiscoverDevices(const char * const deviceTypes[], ssdpDiscoverDevices(const char * const deviceTypes[],
int delay, const char * multicastif, int delay, const char * multicastif,
int sameport, int localport,
int ipv6, unsigned char ttl, int ipv6, unsigned char ttl,
int * error, int * error,
int searchalltypes); int searchalltypes);

View file

@ -1,8 +1,9 @@
/* $Id: miniupnpc.c,v 1.135 2015/07/23 20:40:08 nanard Exp $ */ /* $Id: miniupnpc.c,v 1.165 2025/01/10 22:57:21 nanard Exp $ */
/* Project : miniupnp /* vim: tabstop=4 shiftwidth=4 noexpandtab
* Web : http://miniupnp.free.fr/ * Project : miniupnp
* Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas BERNARD * 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 * This software is subjet to the conditions detailed in the
* provided LICENSE file. */ * provided LICENSE file. */
#include <stdlib.h> #include <stdlib.h>
@ -14,7 +15,7 @@
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <io.h> #include <io.h>
#include <iphlpapi.h> #include <iphlpapi.h>
#define snprintf _snprintf #include "win32_snprintf.h"
#define strdup _strdup #define strdup _strdup
#ifndef strncasecmp #ifndef strncasecmp
#if defined(_MSC_VER) && (_MSC_VER >= 1400) #if defined(_MSC_VER) && (_MSC_VER >= 1400)
@ -55,13 +56,15 @@
#include "miniupnpc.h" #include "miniupnpc.h"
#include "minissdpc.h" #include "minissdpc.h"
#include "miniwget.h" #include "miniwget.h"
#include "miniwget_private.h"
#include "minisoap.h" #include "minisoap.h"
#include "minixml.h" #include "minixml.h"
#include "upnpcommands.h" #include "upnpcommands.h"
#include "connecthostport.h" #include "connecthostport.h"
#include "addr_is_reserved.h"
/* compare the begining of a string with a constant string */ /* compare the beginning of a string with a constant string */
#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1)) #define COMPARE(str, cstr) (0==strncmp(str, cstr, sizeof(cstr) - 1))
#ifndef MAXHOSTNAMELEN #ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64 #define MAXHOSTNAMELEN 64
@ -89,28 +92,32 @@ MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGD
#endif #endif
} }
/* simpleUPnPcommand2 : /* simpleUPnPcommand :
* not so simple ! * not so simple !
* return values : * return values :
* pointer - OK * pointer - OK
* NULL - error */ * NULL - error */
char * simpleUPnPcommand2(int s, const char * url, const char * service, char *
const char * action, struct UPNParg * args, simpleUPnPcommand(const char * url, const char * service,
int * bufsize, const char * httpversion) const char * action, const struct UPNParg * args,
int * bufsize)
{ {
char hostname[MAXHOSTNAMELEN+1]; char hostname[MAXHOSTNAMELEN+1];
unsigned short port = 0; unsigned short port = 0;
char * path; char * path;
char soapact[128]; char soapact[128];
char soapbody[2048]; char soapbody[2048];
int soapbodylen;
char * buf; char * buf;
int n; int n;
int status_code;
SOCKET s;
*bufsize = 0; *bufsize = 0;
snprintf(soapact, sizeof(soapact), "%s#%s", service, action); snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
if(args==NULL) if(args==NULL)
{ {
/*soapbodylen = */snprintf(soapbody, sizeof(soapbody), soapbodylen = snprintf(soapbody, sizeof(soapbody),
"<?xml version=\"1.0\"?>\r\n" "<?xml version=\"1.0\"?>\r\n"
"<" SOAPPREFIX ":Envelope " "<" SOAPPREFIX ":Envelope "
"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" " "xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
@ -120,12 +127,14 @@ char * simpleUPnPcommand2(int s, const char * url, const char * service,
"</" SERVICEPREFIX ":%s>" "</" SERVICEPREFIX ":%s>"
"</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>" "</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
"\r\n", action, service, action); "\r\n", action, service, action);
if ((unsigned int)soapbodylen >= sizeof(soapbody))
return NULL;
} }
else else
{ {
char * p; char * p;
const char * pe, * pv; const char * pe, * pv;
int soapbodylen; const char * const pend = soapbody + sizeof(soapbody);
soapbodylen = snprintf(soapbody, sizeof(soapbody), soapbodylen = snprintf(soapbody, sizeof(soapbody),
"<?xml version=\"1.0\"?>\r\n" "<?xml version=\"1.0\"?>\r\n"
"<" SOAPPREFIX ":Envelope " "<" SOAPPREFIX ":Envelope "
@ -134,53 +143,68 @@ char * simpleUPnPcommand2(int s, const char * url, const char * service,
"<" SOAPPREFIX ":Body>" "<" SOAPPREFIX ":Body>"
"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">", "<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
action, service); action, service);
if ((unsigned int)soapbodylen >= sizeof(soapbody))
return NULL;
p = soapbody + soapbodylen; p = soapbody + soapbodylen;
while(args->elt) while(args->elt)
{ {
/* check that we are never overflowing the string... */ if(p >= pend) /* check for space to write next byte */
if(soapbody + sizeof(soapbody) <= p + 100)
{
/* we keep a margin of at least 100 bytes */
return NULL; return NULL;
}
*(p++) = '<'; *(p++) = '<';
pe = args->elt; pe = args->elt;
while(*pe) while(p < pend && *pe)
*(p++) = *(pe++); *(p++) = *(pe++);
if(p >= pend) /* check for space to write next byte */
return NULL;
*(p++) = '>'; *(p++) = '>';
if((pv = args->val)) if((pv = args->val))
{ {
while(*pv) while(p < pend && *pv)
*(p++) = *(pv++); *(p++) = *(pv++);
} }
if((p+2) > pend) /* check for space to write next 2 bytes */
return NULL;
*(p++) = '<'; *(p++) = '<';
*(p++) = '/'; *(p++) = '/';
pe = args->elt; pe = args->elt;
while(*pe) while(p < pend && *pe)
*(p++) = *(pe++); *(p++) = *(pe++);
if(p >= pend) /* check for space to write next byte */
return NULL;
*(p++) = '>'; *(p++) = '>';
args++; args++;
} }
if((p+4) > pend) /* check for space to write next 4 bytes */
return NULL;
*(p++) = '<'; *(p++) = '<';
*(p++) = '/'; *(p++) = '/';
*(p++) = SERVICEPREFIX2; *(p++) = SERVICEPREFIX2;
*(p++) = ':'; *(p++) = ':';
pe = action; pe = action;
while(*pe) while(p < pend && *pe)
*(p++) = *(pe++); *(p++) = *(pe++);
strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n", strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\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(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
if(s < 0) {
s = connecthostport(hostname, port, 0); s = connecthostport(hostname, port, 0);
if(s < 0) { if(ISINVALID(s)) {
/* failed to connect */ /* failed to connect */
return NULL; return NULL;
} }
}
n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion); n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, "1.1");
if(n<=0) { if(n<=0) {
#ifdef DEBUG #ifdef DEBUG
printf("Error sending SOAP request\n"); printf("Error sending SOAP request\n");
@ -189,43 +213,21 @@ char * simpleUPnPcommand2(int s, const char * url, const char * service,
return NULL; return NULL;
} }
buf = getHTTPResponse(s, bufsize); buf = getHTTPResponse(s, bufsize, &status_code);
#ifdef DEBUG #ifdef DEBUG
if(*bufsize > 0 && buf) 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 #endif
closesocket(s); closesocket(s);
return buf; 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() : /* upnpDiscoverDevices() :
* return a chained list of all devices found or NULL if * return a chained list of all devices found or NULL if
* no devices was found. * no devices was found.
@ -237,7 +239,7 @@ char * simpleUPnPcommand(int s, const char * url, const char * service,
MINIUPNP_LIBSPEC struct UPNPDev * MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevices(const char * const deviceTypes[], upnpDiscoverDevices(const char * const deviceTypes[],
int delay, const char * multicastif, int delay, const char * multicastif,
const char * minissdpdsock, int sameport, const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl, int ipv6, unsigned char ttl,
int * error, int * error,
int searchalltypes) int searchalltypes)
@ -254,6 +256,7 @@ upnpDiscoverDevices(const char * const deviceTypes[],
/* first try to get infos from minissdpd ! */ /* first try to get infos from minissdpd ! */
if(!minissdpdsock) if(!minissdpdsock)
minissdpdsock = "/var/run/minissdpd.sock"; minissdpdsock = "/var/run/minissdpd.sock";
if(minissdpdsock[0] != '\0') {
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) { for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
struct UPNPDev * minissdpd_devlist; struct UPNPDev * minissdpd_devlist;
int only_rootdevice = 1; int only_rootdevice = 1;
@ -280,6 +283,7 @@ upnpDiscoverDevices(const char * const deviceTypes[],
break; break;
} }
} }
}
for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) { for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) {
/* We return what we have found if it was not only a rootdevice */ /* We return what we have found if it was not only a rootdevice */
if(!strstr(tmp->st, "rootdevice")) { if(!strstr(tmp->st, "rootdevice")) {
@ -288,12 +292,14 @@ upnpDiscoverDevices(const char * const deviceTypes[],
return devlist; return devlist;
} }
} }
#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
(void)minissdpdsock; /* unused */
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ #endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
/* direct discovery if minissdpd responses are not sufficient */ /* direct discovery if minissdpd responses are not sufficient */
{ {
struct UPNPDev * discovered_devlist; struct UPNPDev * discovered_devlist;
discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, sameport, discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport,
ipv6, ttl, error, searchalltypes); ipv6, ttl, error, searchalltypes);
if(devlist == NULL) if(devlist == NULL)
devlist = discovered_devlist; devlist = discovered_devlist;
@ -308,7 +314,7 @@ upnpDiscoverDevices(const char * const deviceTypes[],
/* upnpDiscover() Discover IGD device */ /* upnpDiscover() Discover IGD device */
MINIUPNP_LIBSPEC struct UPNPDev * MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscover(int delay, const char * multicastif, upnpDiscover(int delay, const char * multicastif,
const char * minissdpdsock, int sameport, const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl, int ipv6, unsigned char ttl,
int * error) int * error)
{ {
@ -325,14 +331,14 @@ upnpDiscover(int delay, const char * multicastif,
0 0
}; };
return upnpDiscoverDevices(deviceList, return upnpDiscoverDevices(deviceList,
delay, multicastif, minissdpdsock, sameport, delay, multicastif, minissdpdsock, localport,
ipv6, ttl, error, 0); ipv6, ttl, error, 0);
} }
/* upnpDiscoverAll() Discover all UPnP devices */ /* upnpDiscoverAll() Discover all UPnP devices */
MINIUPNP_LIBSPEC struct UPNPDev * MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverAll(int delay, const char * multicastif, upnpDiscoverAll(int delay, const char * multicastif,
const char * minissdpdsock, int sameport, const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl, int ipv6, unsigned char ttl,
int * error) int * error)
{ {
@ -342,14 +348,14 @@ upnpDiscoverAll(int delay, const char * multicastif,
0 0
}; };
return upnpDiscoverDevices(deviceList, return upnpDiscoverDevices(deviceList,
delay, multicastif, minissdpdsock, sameport, delay, multicastif, minissdpdsock, localport,
ipv6, ttl, error, 0); ipv6, ttl, error, 0);
} }
/* upnpDiscoverDevice() Discover a specific device */ /* upnpDiscoverDevice() Discover a specific device */
MINIUPNP_LIBSPEC struct UPNPDev * MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevice(const char * device, int delay, const char * multicastif, 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 ipv6, unsigned char ttl,
int * error) int * error)
{ {
@ -358,7 +364,7 @@ upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
0 0
}; };
return upnpDiscoverDevices(deviceList, return upnpDiscoverDevices(deviceList,
delay, multicastif, minissdpdsock, sameport, delay, multicastif, minissdpdsock, localport,
ipv6, ttl, error, 0); ipv6, ttl, error, 0);
} }
@ -366,7 +372,7 @@ static char *
build_absolute_url(const char * baseurl, const char * descURL, build_absolute_url(const char * baseurl, const char * descURL,
const char * url, unsigned int scope_id) const char * url, unsigned int scope_id)
{ {
int l, n; size_t l, n;
char * s; char * s;
const char * base; const char * base;
char * p; char * p;
@ -409,7 +415,7 @@ build_absolute_url(const char * baseurl, const char * descURL,
memcpy(s, base, n); memcpy(s, base, n);
if(scope_id != 0) { if(scope_id != 0) {
s[n] = '\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 */ /* this is a linklocal IPv6 address */
p = strchr(s, ']'); p = strchr(s, ']');
if(p) { if(p) {
@ -498,23 +504,29 @@ UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
/* UPNP_GetValidIGD() : /* UPNP_GetValidIGD() :
* return values : * return values :
* -1 = Internal error * -1 = Internal error
* 0 = NO IGD found * 0 = NO IGD found (UPNP_NO_IGD)
* 1 = A valid connected IGD has been found * 1 = A valid connected IGD has been found (UPNP_CONNECTED_IGD)
* 2 = A valid IGD has been found but it reported as * 2 = A valid connected IGD has been found but its
* not connected * IP address is reserved (non routable) (UPNP_PRIVATEIP_IGD)
* 3 = an UPnP device has been found but was not recognized as an 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 * 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. * free allocated memory.
*/ */
MINIUPNP_LIBSPEC int MINIUPNP_LIBSPEC int
UPNP_GetValidIGD(struct UPNPDev * devlist, UPNP_GetValidIGD(struct UPNPDev * devlist,
struct UPNPUrls * urls, struct UPNPUrls * urls,
struct IGDdatas * data, struct IGDdatas * data,
char * lanaddr, int lanaddrlen) char * lanaddr, int lanaddrlen,
char * wanaddr, int wanaddrlen)
{ {
struct xml_desc { struct xml_desc {
char lanaddr[40];
char wanaddr[40];
char * xml; char * xml;
int size; int size;
int is_igd; int is_igd;
@ -522,9 +534,10 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
struct UPNPDev * dev; struct UPNPDev * dev;
int ndev = 0; int ndev = 0;
int i; int i;
int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */ int state = -1; /* state 1 : IGD connected. State 2 : connected with reserved IP.
int n_igd = 0; * State 3 : IGD. State 4 : anything */
char extIpAddr[16]; int status_code = -1;
if(!devlist) if(!devlist)
{ {
#ifdef DEBUG #ifdef DEBUG
@ -535,20 +548,18 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
/* counting total number of devices in the list */ /* counting total number of devices in the list */
for(dev = devlist; dev; dev = dev->pNext) for(dev = devlist; dev; dev = dev->pNext)
ndev++; ndev++;
if(ndev > 0) /* ndev is always > 0 */
{
desc = calloc(ndev, sizeof(struct xml_desc)); desc = calloc(ndev, sizeof(struct xml_desc));
if(!desc) if(!desc)
return -1; /* memory allocation error */ return -1; /* memory allocation error */
}
/* Step 1 : downloading descriptions and testing type */ /* Step 1 : downloading descriptions and testing type */
for(dev = devlist, i = 0; dev; dev = dev->pNext, i++) for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
{ {
/* we should choose an internet gateway device. /* we should choose an internet gateway device.
* with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */ * with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size), desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
lanaddr, lanaddrlen, desc[i].lanaddr, sizeof(desc[i].lanaddr),
dev->scope_id); dev->scope_id, &status_code);
#ifdef DEBUG #ifdef DEBUG
if(!desc[i].xml) if(!desc[i].xml)
{ {
@ -564,12 +575,11 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))
{ {
desc[i].is_igd = 1; desc[i].is_igd = 1;
n_igd++;
} }
} }
} }
/* iterate the list to find a device depending on state */ /* 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++) 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(data, 0, sizeof(struct IGDdatas));
memset(urls, 0, sizeof(struct UPNPUrls)); memset(urls, 0, sizeof(struct UPNPUrls));
parserootdesc(desc[i].xml, desc[i].size, data); 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); GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
/* in state 2 and 3 we dont test if device is connected ! */ /* in state 3 and 4 we don't test if device is connected ! */
if(state >= 2) if(state >= 3)
goto free_and_return; goto free_and_return;
is_connected = UPNPIGD_IsConnected(urls, data);
#ifdef DEBUG #ifdef DEBUG
printf("UPNPIGD_IsConnected(%s) = %d\n", printf("UPNPIGD_IsConnected(%s) = %d\n",
urls->controlURL, urls->controlURL, is_connected);
UPNPIGD_IsConnected(urls, data));
#endif #endif
/* checks that status is connected AND there is a external IP address assigned */ /* checks that status is connected AND there is a external IP address assigned */
if(UPNPIGD_IsConnected(urls, data) if(is_connected) {
&& (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) if(state >= 2)
goto free_and_return; 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); FreeUPNPUrls(urls);
if(data->second.servicetype[0] != '\0') { if(data->second.servicetype[0] != '\0') {
#ifdef DEBUG #ifdef DEBUG
@ -605,14 +621,18 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service)); memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service)); memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id); GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
is_connected = UPNPIGD_IsConnected(urls, data);
#ifdef DEBUG #ifdef DEBUG
printf("UPNPIGD_IsConnected(%s) = %d\n", printf("UPNPIGD_IsConnected(%s) = %d\n",
urls->controlURL, urls->controlURL, is_connected);
UPNPIGD_IsConnected(urls, data));
#endif #endif
if(UPNPIGD_IsConnected(urls, data) if(is_connected) {
&& (UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) if(state >= 2)
goto free_and_return; 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); FreeUPNPUrls(urls);
} }
} }
@ -622,14 +642,15 @@ UPNP_GetValidIGD(struct UPNPDev * devlist,
} }
state = 0; state = 0;
free_and_return: free_and_return:
if(desc) { if (state >= 1 && state <= 4 && i < ndev) {
for(i = 0; i < ndev; i++) { if (lanaddr != NULL)
if(desc[i].xml) { 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[i].xml);
}
}
free(desc); free(desc);
}
return state; return state;
} }
@ -646,18 +667,17 @@ UPNP_GetIGDFromUrl(const char * rootdescurl,
{ {
char * descXML; char * descXML;
int descXMLsize = 0; int descXMLsize = 0;
descXML = miniwget_getaddr(rootdescurl, &descXMLsize, descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
lanaddr, lanaddrlen, 0); lanaddr, lanaddrlen, 0, NULL);
if(descXML) { if(descXML) {
memset(data, 0, sizeof(struct IGDdatas)); memset(data, 0, sizeof(struct IGDdatas));
memset(urls, 0, sizeof(struct UPNPUrls)); memset(urls, 0, sizeof(struct UPNPUrls));
parserootdesc(descXML, descXMLsize, data); parserootdesc(descXML, descXMLsize, data);
free(descXML); free(descXML);
descXML = NULL;
GetUPNPUrls(urls, data, rootdescurl, 0); GetUPNPUrls(urls, data, rootdescurl, 0);
return 1; return 1;
} else { } else {
return 0; return 0;
} }
} }

View file

@ -1,139 +1,298 @@
/* $Id: miniupnpc.h,v 1.44 2015/07/23 20:40:10 nanard Exp $ */ /* $Id: miniupnpc.h,v 1.76 2025/03/05 10:35:57 nanard Exp $ */
/* Project: miniupnp /* vim: tabstop=4 shiftwidth=4 noexpandtab
* http://miniupnp.free.fr/ * Project: miniupnp
* http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author: Thomas Bernard * Author: Thomas Bernard
* Copyright (c) 2005-2015 Thomas Bernard * Copyright (c) 2005-2025 Thomas Bernard
* This software is subjects to the conditions detailed * This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */ * in the LICENCE file provided within this distribution */
#ifndef MINIUPNPC_H_INCLUDED #ifndef MINIUPNPC_H_INCLUDED
#define 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 "miniupnpc_declspec.h"
#include "igd_desc_parse.h" #include "igd_desc_parse.h"
#include "upnpdev.h" #include "upnpdev.h"
/* error codes : */ /* error codes : */
/*! \brief value for success */
#define UPNPDISCOVER_SUCCESS (0) #define UPNPDISCOVER_SUCCESS (0)
/*! \brief value for unknown error */
#define UPNPDISCOVER_UNKNOWN_ERROR (-1) #define UPNPDISCOVER_UNKNOWN_ERROR (-1)
/*! \brief value for a socket error */
#define UPNPDISCOVER_SOCKET_ERROR (-101) #define UPNPDISCOVER_SOCKET_ERROR (-101)
/*! \brief value for a memory allocation error */
#define UPNPDISCOVER_MEMORY_ERROR (-102) #define UPNPDISCOVER_MEMORY_ERROR (-102)
/* versions : */ /*! \brief software version */
#define MINIUPNPC_VERSION "1.9" #define MINIUPNPC_VERSION "2.3.2"
#define MINIUPNPC_API_VERSION 14 /*! \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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/* Structures definitions : */ /* 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 * char *
simpleUPnPcommand(int, const char *, const char *, simpleUPnPcommand(const char * url, const char * service,
const char *, struct UPNParg *, const char * action, const struct UPNParg * args,
int *); 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. * The discovered devices are returned as a chained list.
* It is up to the caller to free the list with freeUPNPDevlist(). * 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. * If available, device list will be obtained from MiniSSDPd.
* Default path for minissdpd socket will be used if minissdpdsock argument *
* is NULL. * \param[in] delay (in millisecond) maximum time for waiting any device
* If multicastif is not NULL, it will be used instead of the default * response
* multicast interface for sending SSDP discover packets. * \param[in] multicastif If not NULL, used instead of the default
* If sameport is not null, SSDP packets will be sent from the source port * multicast interface for sending SSDP discover packets
* 1900 (same as destination port) otherwise system assign a source port. * \param[in] minissdpdsock Path to minissdpd socket, default is used if
* "searchalltypes" parameter is useful when searching several types, * NULL
* if 0, the discovery will stop with the first type returning results. * \param[in] localport Source port to send SSDP packets.
* TTL should default to 2. */ * #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 * MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscover(int delay, const char * multicastif, upnpDiscover(int delay, const char * multicastif,
const char * minissdpdsock, int sameport, const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl, int ipv6, unsigned char ttl,
int * error); 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 * MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverAll(int delay, const char * multicastif, upnpDiscoverAll(int delay, const char * multicastif,
const char * minissdpdsock, int sameport, const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl, int ipv6, unsigned char ttl,
int * error); 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 * MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevice(const char * device, int delay, const char * multicastif, 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 ipv6, unsigned char ttl,
int * error); 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 * MINIUPNP_LIBSPEC struct UPNPDev *
upnpDiscoverDevices(const char * const deviceTypes[], upnpDiscoverDevices(const char * const deviceTypes[],
int delay, const char * multicastif, int delay, const char * multicastif,
const char * minissdpdsock, int sameport, const char * minissdpdsock, int localport,
int ipv6, unsigned char ttl, int ipv6, unsigned char ttl,
int * error, int * error,
int searchalltypes); int searchalltypes);
/* parserootdesc() : /*!
* parse root XML description of a UPnP device and fill the IGDdatas * \brief parse root XML description of a UPnP device
* structure. */ *
MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *); * 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 * \brief structure used to get fast access to urls
* ipcondescURL: url of the description of the WANIPConnection
* controlURL_CIF: controlURL of the WANCommonInterfaceConfig
* controlURL_6FC: controlURL of the WANIPv6FirewallControl
*/ */
struct UPNPUrls { struct UPNPUrls {
/*! \brief controlURL of the WANIPConnection */
char * controlURL; char * controlURL;
/*! \brief url of the description of the WANIPConnection */
char * ipcondescURL; char * ipcondescURL;
/*! \brief controlURL of the WANCommonInterfaceConfig */
char * controlURL_CIF; char * controlURL_CIF;
/*! \brief controlURL of the WANIPv6FirewallControl */
char * controlURL_6FC; char * controlURL_6FC;
/*! \brief url of the root description */
char * rootdescURL; char * rootdescURL;
}; };
/* UPNP_GetValidIGD() : /*! \brief NO IGD found */
* return values : #define UPNP_NO_IGD (0)
* 0 = NO IGD found /*! \brief valid and connected IGD */
* 1 = A valid connected IGD has been found #define UPNP_CONNECTED_IGD (1)
* 2 = A valid IGD has been found but it reported as /*! \brief valid and connected IGD but with a reserved address
* not connected * (non routable) */
* 3 = an UPnP device has been found but was not recognized as an IGD #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 * In any 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. Donc forget to call FreeUPNPUrls(urls) to
* free allocated memory. * 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 MINIUPNP_LIBSPEC int
UPNP_GetValidIGD(struct UPNPDev * devlist, UPNP_GetValidIGD(struct UPNPDev * devlist,
struct UPNPUrls * urls, struct UPNPUrls * urls,
struct IGDdatas * data, struct IGDdatas * data,
char * lanaddr, int lanaddrlen); char * lanaddr, int lanaddrlen,
char * wanaddr, int wanaddrlen);
/* UPNP_GetIGDFromUrl() /*!
* \brief Get IGD URLs and data for URL
*
* Used when skipping the discovery process. * Used when skipping the discovery process.
* When succeding, urls, data, and lanaddr arguments are set. * \param[in] rootdescurl Root description URL of the device
* return value : * \param[out] urls Urls for the IGD found
* 0 - Not ok * \param[out] data datas for the IGD found
* 1 - OK */ * \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 MINIUPNP_LIBSPEC int
UPNP_GetIGDFromUrl(const char * rootdescurl, UPNP_GetIGDFromUrl(const char * rootdescurl,
struct UPNPUrls * urls, struct UPNPUrls * urls,
struct IGDdatas * data, struct IGDdatas * data,
char * lanaddr, int lanaddrlen); 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 MINIUPNP_LIBSPEC void
GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
const char *, unsigned int); 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 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 #ifdef __cplusplus

View file

@ -1,12 +1,22 @@
#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED #ifndef MINIUPNPC_DECLSPEC_H_INCLUDED
#define MINIUPNPC_DECLSPEC_H_INCLUDED #define MINIUPNPC_DECLSPEC_H_INCLUDED
#if defined(__GNUC__) && __GNUC__ >= 4 /*! \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 */ /* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */
#define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default"))) #define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default")))
#else #else
#define MINIUPNP_LIBSPEC #define MINIUPNP_LIBSPEC
#endif
#endif #endif
#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */ #endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */

View file

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

View file

@ -1,17 +1,22 @@
/* $Id: miniupnpcmodule.c,v 1.24 2014/06/10 09:48:11 nanard Exp $*/ /* $Id: miniupnpcmodule.c,v 1.40 2024/05/09 15:10:29 nanard Exp $*/
/* Project : miniupnp /* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas BERNARD * Author : Thomas BERNARD
* website : http://miniupnp.tuxfamily.org/ * website : https://miniupnp.tuxfamily.org/
* copyright (c) 2007-2014 Thomas Bernard * copyright (c) 2007-2024 Thomas Bernard
* This software is subjet to the conditions detailed in the * This software is subjet to the conditions detailed in the
* provided LICENCE file. */ * provided LICENCE file. */
#include <Python.h> #include <Python.h>
#define MINIUPNP_STATICLIB #define MINIUPNP_STATICLIB
#include "structmember.h" #include <structmember.h>
#include "miniupnpc.h" #include "miniupnpc.h"
#include "upnpcommands.h" #include "upnpcommands.h"
#include "upnperrors.h" #include "upnperrors.h"
#ifdef _WIN32
#include <winsock2.h>
#endif
/* for compatibility with Python < 2.4 */ /* for compatibility with Python < 2.4 */
#ifndef Py_RETURN_NONE #ifndef Py_RETURN_NONE
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
@ -42,7 +47,9 @@ typedef struct {
struct UPNPUrls urls; struct UPNPUrls urls;
struct IGDdatas data; struct IGDdatas data;
unsigned int discoverdelay; /* value passed to upnpDiscover() */ unsigned int discoverdelay; /* value passed to upnpDiscover() */
unsigned int localport; /* value passed to upnpDiscover() */
char lanaddr[40]; /* our ip address on the LAN */ char lanaddr[40]; /* our ip address on the LAN */
char wanaddr[40]; /* the ExternalIPAddress returned by the IGD */
char * multicastif; char * multicastif;
char * minissdpdsocket; char * minissdpdsocket;
} UPnPObject; } UPnPObject;
@ -51,10 +58,22 @@ static PyMemberDef UPnP_members[] = {
{"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr), {"lanaddr", T_STRING_INPLACE, offsetof(UPnPObject, lanaddr),
READONLY, "ip address on the LAN" 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), {"discoverdelay", T_UINT, offsetof(UPnPObject, discoverdelay),
0/*READWRITE*/, "value in ms used to wait for SSDP responses" 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), {"multicastif", T_STRING, offsetof(UPnPObject, multicastif),
0, "IP of the network interface to be used for multicast operations" 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* multicastif = NULL;
char* minissdpdsocket = NULL; char* minissdpdsocket = NULL;
static char *kwlist[] = { 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, &multicastif,
&minissdpdsocket, &minissdpdsocket,
&self->discoverdelay)) &self->discoverdelay,
&self->localport))
return -1; return -1;
if(self->localport>1 &&
(self->localport>65534||self->localport<1024)) {
PyErr_SetString(PyExc_Exception, "Invalid localport value");
return -1;
}
if(multicastif) if(multicastif)
self->multicastif = strdup(multicastif); self->multicastif = strdup(multicastif);
if(minissdpdsocket) if(minissdpdsocket)
@ -100,9 +126,9 @@ UPnPObject_dealloc(UPnPObject *self)
static PyObject * static PyObject *
UPnP_discover(UPnPObject *self) UPnP_discover(UPnPObject *self)
{ {
struct UPNPDev * dev; int error = 0;
int i;
PyObject *res = NULL; PyObject *res = NULL;
if(self->devlist) if(self->devlist)
{ {
freeUPNPDevlist(self->devlist); freeUPNPDevlist(self->devlist);
@ -112,25 +138,42 @@ UPnP_discover(UPnPObject *self)
self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/, self->devlist = upnpDiscover((int)self->discoverdelay/*timeout in ms*/,
self->multicastif, self->multicastif,
self->minissdpdsocket, self->minissdpdsocket,
0/*sameport flag*/, (int)self->localport,
0/*ip v6*/, 0/*ip v6*/,
2/* TTL */, 2/* TTL */,
0/*error */); &error);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
/* Py_RETURN_NONE ??? */ /* Py_RETURN_NONE ??? */
for(dev = self->devlist, i = 0; dev; dev = dev->pNext) if (self->devlist != NULL) {
struct UPNPDev * dev;
int i = 0;
for(dev = self->devlist; dev; dev = dev->pNext)
i++; i++;
res = Py_BuildValue("i", i); res = Py_BuildValue("i", i);
return res; return res;
} else {
PyErr_SetString(PyExc_Exception, strupnperror(error));
return NULL;
}
} }
static PyObject * static PyObject *
UPnP_selectigd(UPnPObject *self) UPnP_selectigd(UPnPObject *self, PyObject *args)
{ {
const char * rootDescUrl = NULL;
int r; int r;
if(!PyArg_ParseTuple(args, "|z", &rootDescUrl))
return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
if (rootDescUrl == NULL) {
r = UPNP_GetValidIGD(self->devlist, &self->urls, &self->data, 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)); self->lanaddr, sizeof(self->lanaddr));
}
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if(r) if(r)
{ {
@ -152,7 +195,11 @@ Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF, i = UPNP_GetTotalBytesSent(self->urls.controlURL_CIF,
self->data.CIF.servicetype); self->data.CIF.servicetype);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i); return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
} }
static PyObject * static PyObject *
@ -163,7 +210,11 @@ Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF, i = UPNP_GetTotalBytesReceived(self->urls.controlURL_CIF,
self->data.CIF.servicetype); self->data.CIF.servicetype);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i); return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
} }
static PyObject * static PyObject *
@ -174,7 +225,11 @@ Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF, i = UPNP_GetTotalPacketsSent(self->urls.controlURL_CIF,
self->data.CIF.servicetype); self->data.CIF.servicetype);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i); return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
} }
static PyObject * static PyObject *
@ -185,7 +240,11 @@ Py_BEGIN_ALLOW_THREADS
i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF, i = UPNP_GetTotalPacketsReceived(self->urls.controlURL_CIF,
self->data.CIF.servicetype); self->data.CIF.servicetype);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", i); return Py_BuildValue("I", i);
#else
return Py_BuildValue("i", (int)i);
#endif
} }
static PyObject * static PyObject *
@ -202,7 +261,11 @@ Py_BEGIN_ALLOW_THREADS
status, &uptime, lastconnerror); status, &uptime, lastconnerror);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) { 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); return Py_BuildValue("(s,I,s)", status, uptime, lastconnerror);
#else
return Py_BuildValue("(s,i,s)", status, (int)uptime, lastconnerror);
#endif
} else { } else {
/* TODO: have our own exception type ! */ /* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r)); PyErr_SetString(PyExc_Exception, strupnperror(r));
@ -251,7 +314,7 @@ Py_END_ALLOW_THREADS
} }
/* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc, /* AddPortMapping(externalPort, protocol, internalHost, internalPort, desc,
* remoteHost) * remoteHost, leaseDuration)
* protocol is 'UDP' or 'TCP' */ * protocol is 'UDP' or 'TCP' */
static PyObject * static PyObject *
UPnP_addportmapping(UPnPObject *self, PyObject *args) UPnP_addportmapping(UPnPObject *self, PyObject *args)
@ -264,17 +327,24 @@ UPnP_addportmapping(UPnPObject *self, PyObject *args)
const char * host; const char * host;
const char * desc; const char * desc;
const char * remoteHost; const char * remoteHost;
const char * leaseDuration = "0"; unsigned int intLeaseDuration = 0;
char strLeaseDuration[12];
int r; int r;
if (!PyArg_ParseTuple(args, "HssHss", &ePort, &proto, #if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
&host, &iPort, &desc, &remoteHost)) 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; return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort); sprintf(extPort, "%hu", ePort);
sprintf(inPort, "%hu", iPort); sprintf(inPort, "%hu", iPort);
sprintf(strLeaseDuration, "%u", intLeaseDuration);
r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype, r = UPNP_AddPortMapping(self->urls.controlURL, self->data.first.servicetype,
extPort, inPort, host, desc, proto, extPort, inPort, host, desc, proto,
remoteHost, leaseDuration); remoteHost, strLeaseDuration);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) if(r==UPNPCOMMAND_SUCCESS)
{ {
@ -308,7 +378,7 @@ UPnP_addanyportmapping(UPnPObject *self, PyObject *args)
const char * remoteHost; const char * remoteHost;
const char * leaseDuration = "0"; const char * leaseDuration = "0";
int r; 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; return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
sprintf(extPort, "%hu", ePort); sprintf(extPort, "%hu", ePort);
@ -364,14 +434,14 @@ UPnP_deleteportmappingrange(UPnPObject *self, PyObject *args)
unsigned short ePortEnd; unsigned short ePortEnd;
const char * proto; const char * proto;
unsigned char manage; unsigned char manage;
char manageStr[1]; char manageStr[6];
int r; int r;
if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage)) if(!PyArg_ParseTuple(args, "HHsb", &ePortStart, &ePortEnd, &proto, &manage))
return NULL; return NULL;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
sprintf(extPortStart, "%hu", ePortStart); sprintf(extPortStart, "%hu", ePortStart);
sprintf(extPortEnd, "%hu", ePortEnd); sprintf(extPortEnd, "%hu", ePortEnd);
sprintf(manageStr, "%hhu", manage); sprintf(manageStr, "%hu", (unsigned short)manage);
r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype, r = UPNP_DeletePortMappingRange(self->urls.controlURL, self->data.first.servicetype,
extPortStart, extPortEnd, proto, manageStr); extPortStart, extPortEnd, proto, manageStr);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
@ -395,7 +465,11 @@ Py_BEGIN_ALLOW_THREADS
&n); &n);
Py_END_ALLOW_THREADS Py_END_ALLOW_THREADS
if(r==UPNPCOMMAND_SUCCESS) { if(r==UPNPCOMMAND_SUCCESS) {
#if (PY_MAJOR_VERSION >= 3) || (PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION > 3)
return Py_BuildValue("I", n); return Py_BuildValue("I", n);
#else
return Py_BuildValue("i", (int)n);
#endif
} else { } else {
/* TODO: have our own exception type ! */ /* TODO: have our own exception type ! */
PyErr_SetString(PyExc_Exception, strupnperror(r)); PyErr_SetString(PyExc_Exception, strupnperror(r));
@ -480,9 +554,15 @@ Py_END_ALLOW_THREADS
ePort = (unsigned short)atoi(extPort); ePort = (unsigned short)atoi(extPort);
iPort = (unsigned short)atoi(intPort); iPort = (unsigned short)atoi(intPort);
dur = (unsigned int)strtoul(duration, 0, 0); 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)", return Py_BuildValue("(H,s,(s,H),s,s,s,I)",
ePort, protocol, intClient, iPort, ePort, protocol, intClient, iPort,
desc, enabled, rHost, dur); 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 else
{ {
@ -495,7 +575,7 @@ static PyMethodDef UPnP_methods[] = {
{"discover", (PyCFunction)UPnP_discover, METH_NOARGS, {"discover", (PyCFunction)UPnP_discover, METH_NOARGS,
"discover UPnP IGD devices on the network" "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" "select a valid UPnP IGD among discovered devices"
}, },
{"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS, {"totalbytesent", (PyCFunction)UPnP_totalbytesent, METH_NOARGS,
@ -585,8 +665,9 @@ static PyTypeObject UPnPType = {
#ifndef _WIN32 #ifndef _WIN32
PyType_GenericNew,/*UPnP_new,*/ /* tp_new */ PyType_GenericNew,/*UPnP_new,*/ /* tp_new */
#else #else
0, 0, /* tp_new */
#endif #endif
0, /* tp_free */
}; };
/* module methods */ /* module methods */
@ -622,6 +703,20 @@ initminiupnpc(void)
PyObject* m; PyObject* m;
#ifdef _WIN32 #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; UPnPType.tp_new = PyType_GenericNew;
#endif #endif
if (PyType_Ready(&UPnPType) < 0) if (PyType_Ready(&UPnPType) < 0)

View file

@ -1,19 +1,27 @@
/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */ /* $Id: miniupnpctypes.h,v 1.4 2025/02/08 23:15:16 nanard Exp $ */
/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org /* Project: miniupnp
* Author : Thomas Bernard * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org
* Copyright (c) 2011 Thomas Bernard * Author: Thomas Bernard
* Copyright (c) 2021-2025 Thomas Bernard
* This software is subject to the conditions detailed in the * This software is subject to the conditions detailed in the
* LICENCE file provided within this distribution */ * LICENCE file provided within this distribution */
#ifndef MINIUPNPCTYPES_H_INCLUDED #ifndef MINIUPNPCTYPES_H_INCLUDED
#define 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 UNSIGNED_INTEGER unsigned long long
#define STRTOUI strtoull
#else #else
#define UNSIGNED_INTEGER unsigned int #define UNSIGNED_INTEGER unsigned int
#define STRTOUI strtoul
#endif #endif
#endif #endif

View file

@ -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 /* Project : miniupnp
* Website : http://miniupnp.free.fr/ * Website : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas Bernard * 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 * This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */ * LICENCE file provided in this distribution. */
@ -15,7 +15,7 @@
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <io.h> #include <io.h>
#define MAXHOSTNAMELEN 64 #define MAXHOSTNAMELEN 64
#define snprintf _snprintf #include "win32_snprintf.h"
#define socklen_t int #define socklen_t int
#ifndef strncasecmp #ifndef strncasecmp
#if defined(_MSC_VER) && (_MSC_VER >= 1400) #if defined(_MSC_VER) && (_MSC_VER >= 1400)
@ -65,7 +65,7 @@
* to the length parameter. * to the length parameter.
*/ */
void * void *
getHTTPResponse(int s, int * size) getHTTPResponse(SOCKET s, int * size, int * status_code)
{ {
char buf[2048]; char buf[2048];
int n; int n;
@ -83,7 +83,12 @@ getHTTPResponse(int s, int * size)
unsigned int content_buf_used = 0; unsigned int content_buf_used = 0;
char chunksize_buf[32]; char chunksize_buf[32];
unsigned int chunksize_buf_index; 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); header_buf = malloc(header_buf_len);
if(header_buf == NULL) if(header_buf == NULL)
{ {
@ -106,7 +111,7 @@ getHTTPResponse(int s, int * size)
chunksize_buf[0] = '\0'; chunksize_buf[0] = '\0';
chunksize_buf_index = 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) if(endofheaders == 0)
{ {
@ -155,7 +160,7 @@ getHTTPResponse(int s, int * size)
continue; continue;
/* parse header lines */ /* parse header lines */
for(i = 0; i < endofheaders - 1; i++) { for(i = 0; i < endofheaders - 1; i++) {
if(colon <= linestart && header_buf[i]==':') if(linestart > 0 && colon <= linestart && header_buf[i]==':')
{ {
colon = i; colon = i;
while(i < (endofheaders-1) while(i < (endofheaders-1)
@ -166,7 +171,34 @@ getHTTPResponse(int s, int * size)
/* detecting end of line */ /* detecting end of line */
else if(header_buf[i]=='\r' || header_buf[i]=='\n') 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 #ifdef DEBUG
printf("header='%.*s', value='%.*s'\n", printf("header='%.*s', value='%.*s'\n",
@ -201,8 +233,8 @@ getHTTPResponse(int s, int * size)
memcpy(buf, header_buf + endofheaders, n); memcpy(buf, header_buf + endofheaders, n);
/* if(headers) */ /* if(headers) */
} }
if(endofheaders) /* if we get there, endofheaders != 0.
{ * In the other case, there was a continue above */
/* content */ /* content */
if(chunked) if(chunked)
{ {
@ -214,7 +246,7 @@ getHTTPResponse(int s, int * size)
/* reading chunk size */ /* reading chunk size */
if(chunksize_buf_index == 0) { if(chunksize_buf_index == 0) {
/* skipping any leading CR LF */ /* skipping any leading CR LF */
if(i<n && buf[i] == '\r') i++; if(buf[i] == '\r') i++;
if(i<n && buf[i] == '\n') i++; if(i<n && buf[i] == '\n') i++;
} }
while(i<n && isxdigit(buf[i]) while(i<n && isxdigit(buf[i])
@ -255,11 +287,12 @@ getHTTPResponse(int s, int * size)
goto end_of_stream; goto end_of_stream;
} }
} }
bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i); /* 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) if((content_buf_used + bytestocopy) > content_buf_len)
{ {
char * tmp; char * tmp;
if(content_length >= (int)(content_buf_used + bytestocopy)) { if((content_length >= 0) && ((unsigned int)content_length >= (content_buf_used + bytestocopy))) {
content_buf_len = content_length; content_buf_len = content_length;
} else { } else {
content_buf_len = content_buf_used + bytestocopy; content_buf_len = content_buf_used + bytestocopy;
@ -284,14 +317,15 @@ getHTTPResponse(int s, int * size)
{ {
/* not chunked */ /* not chunked */
if(content_length > 0 if(content_length > 0
&& (int)(content_buf_used + n) > content_length) { && (content_buf_used + n) > (unsigned int)content_length) {
/* skipping additional bytes */ /* skipping additional bytes */
n = content_length - content_buf_used; n = content_length - content_buf_used;
} }
if(content_buf_used + n > content_buf_len) if(content_buf_used + n > content_buf_len)
{ {
char * tmp; char * tmp;
if(content_length >= (int)(content_buf_used + n)) { if(content_length >= 0
&& (unsigned int)content_length >= (content_buf_used + n)) {
content_buf_len = content_length; content_buf_len = content_length;
} else { } else {
content_buf_len = content_buf_used + n; content_buf_len = content_buf_used + n;
@ -309,9 +343,8 @@ getHTTPResponse(int s, int * size)
memcpy(content_buf + content_buf_used, buf, n); memcpy(content_buf + content_buf_used, buf, n);
content_buf_used += n; content_buf_used += n;
} }
}
/* use the Content-Length header value if available */ /* 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 #ifdef DEBUG
printf("End of HTTP content\n"); printf("End of HTTP content\n");
@ -320,7 +353,7 @@ getHTTPResponse(int s, int * size)
} }
} }
end_of_stream: end_of_stream:
free(header_buf); header_buf = NULL; free(header_buf);
*size = content_buf_used; *size = content_buf_used;
if(content_buf_used == 0) if(content_buf_used == 0)
{ {
@ -337,10 +370,11 @@ static void *
miniwget3(const char * host, miniwget3(const char * host,
unsigned short port, const char * path, unsigned short port, const char * path,
int * size, char * addr_str, int addr_str_len, 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]; char buf[2048];
int s; SOCKET s;
int n; int n;
int len; int len;
int sent; int sent;
@ -348,7 +382,7 @@ miniwget3(const char * host,
*size = 0; *size = 0;
s = connecthostport(host, port, scope_id); s = connecthostport(host, port, scope_id);
if(s < 0) if(ISINVALID(s))
return NULL; return NULL;
/* get address for caller ! */ /* get address for caller ! */
@ -409,11 +443,15 @@ miniwget3(const char * host,
len = snprintf(buf, sizeof(buf), len = snprintf(buf, sizeof(buf),
"GET %s HTTP/%s\r\n" "GET %s HTTP/%s\r\n"
"Host: %s:%d\r\n" "Host: %s:%d\r\n"
"Connection: Close\r\n" "Connection: close\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"
"\r\n", "\r\n",
path, httpversion, host, port); path, httpversion, host, port);
if ((unsigned int)len >= sizeof(buf))
{
closesocket(s);
return NULL;
}
sent = 0; sent = 0;
/* sending the HTTP request */ /* sending the HTTP request */
while(sent < len) while(sent < len)
@ -430,43 +468,11 @@ miniwget3(const char * host,
sent += n; sent += n;
} }
} }
content = getHTTPResponse(s, size); content = getHTTPResponse(s, size, status_code);
closesocket(s); closesocket(s);
return content; 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() /* parseURL()
* arguments : * arguments :
* url : source string not modified * url : source string not modified
@ -520,7 +526,7 @@ parseURL(const char * url,
#else #else
/* under windows, scope is numerical */ /* under windows, scope is numerical */
char tmp[8]; char tmp[8];
int l; size_t l;
scope++; scope++;
/* "%25" is just '%' in URL encoding */ /* "%25" is just '%' in URL encoding */
if(scope[0] == '2' && scope[1] == '5') if(scope[0] == '2' && scope[1] == '5')
@ -583,7 +589,8 @@ parseURL(const char * url,
} }
void * 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; unsigned short port;
char * path; 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", printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id); hostname, port, path, scope_id);
#endif #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 * void *
miniwget_getaddr(const char * url, int * size, 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; unsigned short port;
char * path; 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", printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
hostname, port, path, scope_id); hostname, port, path, scope_id);
#endif #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);
} }

View file

@ -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 /* Project : miniupnp
* Author : Thomas Bernard * 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 * This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. * LICENCE file provided in this distribution.
* */ * */
#ifndef MINIWGET_H_INCLUDED #ifndef MINIWGET_H_INCLUDED
#define MINIWGET_H_INCLUDED #define MINIWGET_H_INCLUDED
/*! \file miniwget.h
* \brief Lightweight HTTP client API
*/
#include "miniupnpc_declspec.h" #include "miniupnpc_declspec.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #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); /*! \brief perform HTTP GET on an URL
*
MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int); * Also get the local address used to reach the HTTP server
*
int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *); * \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 #ifdef __cplusplus
} }
#endif #endif
#endif #endif

View file

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

View file

@ -1,10 +1,11 @@
/* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 nanard Exp $ */ /* $Id: minixml.c,v 1.12 2017/12/12 11:17:40 nanard Exp $ */
/* minixml.c : the minimum size a xml parser can be ! */ /* vim: tabstop=4 shiftwidth=4 noexpandtab
* minixml.c : the minimum size a xml parser can be ! */
/* Project : miniupnp /* Project : miniupnp
* webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author : Thomas Bernard * Author : Thomas Bernard
Copyright (c) 2005-2014, Thomas BERNARD Copyright (c) 2005-2017, Thomas BERNARD
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without 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) if (p->xml >= p->xmlend)
return; return;
} }
if(memcmp(p->xml, "<![CDATA[", 9) == 0) /* CDATA are at least 9 + 3 characters long : <![CDATA[ ]]> */
if((p->xmlend >= (p->xml + (9 + 3))) && (memcmp(p->xml, "<![CDATA[", 9) == 0))
{ {
/* CDATA handling */ /* CDATA handling */
p->xml += 9; p->xml += 9;

View file

@ -10,7 +10,7 @@
* */ * */
#ifndef MINIXML_H_INCLUDED #ifndef MINIXML_H_INCLUDED
#define 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, /* if a callback function pointer is set to NULL,
* the function is not called */ * the function is not called */

163
Externals/miniupnpc/src/minixmlvalid.c vendored Normal file
View file

@ -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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#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; i<a->n; 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[] =
"<xmlroot>\n"
" <elt1 att1=\"attvalue1\" att2=\"attvalue2\">"
"character data"
"</elt1> \n \t"
"<elt1b/>"
"<elt1>\n<![CDATA[ <html>stuff !\n ]]> \n</elt1>\n"
"<elt2a> \t<elt2b>chardata1</elt2b><elt2b> chardata2 </elt2b></elt2a>"
"</xmlroot>";
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, " <html>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;
}

View file

@ -1,7 +1,7 @@
/* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */ /* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */
/* MiniUPnP project /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * 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 * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
#include <string.h> #include <string.h>
@ -12,6 +12,11 @@
#include "portlistingparse.h" #include "portlistingparse.h"
#include "minixml.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 */ /* list of the elements */
static const struct { static const struct {
const portMappingElt code; const portMappingElt code;
@ -55,7 +60,7 @@ startelt(void * d, const char * name, int l)
pdata->curelt = PortMappingEltNone; pdata->curelt = PortMappingEltNone;
for(i = 0; elements[i].str; i++) 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; pdata->curelt = elements[i].code;
break; break;

View file

@ -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 /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * 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 * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
#ifndef PORTLISTINGPARSE_H_INCLUDED #ifndef PORTLISTINGPARSE_H_INCLUDED
#define 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 :
*
* <p:PortMappingEntry>
* <p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
* <p:NewExternalPort>2345</p:NewExternalPort>
* <p:NewProtocol>TCP</p:NewProtocol>
* <p:NewInternalPort>2345</p:NewInternalPort>
* <p:NewInternalClient>192.168.1.137</p:NewInternalClient>
* <p:NewEnabled>1</p:NewEnabled>
* <p:NewDescription>dooom</p:NewDescription>
* <p:NewLeaseTime>345</p:NewLeaseTime>
* </p:PortMappingEntry>
*/
#include "miniupnpc_declspec.h" #include "miniupnpc_declspec.h"
/* for the definition of UNSIGNED_INTEGER */ /* for the definition of UNSIGNED_INTEGER */
#include "miniupnpctypes.h" #include "miniupnpctypes.h"
@ -15,17 +32,8 @@
extern "C" { extern "C" {
#endif #endif
/* sample of PortMappingEntry : /*!
<p:PortMappingEntry> * \brief enum of all XML elements
<p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
<p:NewExternalPort>2345</p:NewExternalPort>
<p:NewProtocol>TCP</p:NewProtocol>
<p:NewInternalPort>2345</p:NewInternalPort>
<p:NewInternalClient>192.168.1.137</p:NewInternalClient>
<p:NewEnabled>1</p:NewEnabled>
<p:NewDescription>dooom</p:NewDescription>
<p:NewLeaseTime>345</p:NewLeaseTime>
</p:PortMappingEntry>
*/ */
typedef enum { PortMappingEltNone, typedef enum { PortMappingEltNone,
PortMappingEntry, NewRemoteHost, PortMappingEntry, NewRemoteHost,
@ -34,27 +42,45 @@ typedef enum { PortMappingEltNone,
NewEnabled, NewDescription, NewEnabled, NewDescription,
NewLeaseTime } portMappingElt; NewLeaseTime } portMappingElt;
/*!
* \brief linked list of port mappings
*/
struct PortMapping { struct PortMapping {
struct PortMapping * l_next; /* list next element */ struct PortMapping * l_next; /*!< \brief next list element */
UNSIGNED_INTEGER leaseTime; UNSIGNED_INTEGER leaseTime; /*!< \brief in seconds */
unsigned short externalPort; unsigned short externalPort; /*!< \brief external port */
unsigned short internalPort; unsigned short internalPort; /*!< \brief internal port */
char remoteHost[64]; char remoteHost[64]; /*!< \brief empty for wildcard */
char internalClient[64]; char internalClient[64]; /*!< \brief internal IP address */
char description[64]; char description[64]; /*!< \brief description */
char protocol[4]; char protocol[4]; /*!< \brief `TCP` or `UDP` */
unsigned char enabled; unsigned char enabled; /*!< \brief 0 (false) or 1 (true) */
}; };
/*!
* \brief structure for ParsePortListing()
*/
struct PortMappingParserData { struct PortMappingParserData {
struct PortMapping * l_head; /* list head */ struct PortMapping * l_head; /*!< \brief list head */
portMappingElt curelt; 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 MINIUPNP_LIBSPEC void
ParsePortListing(const char * buffer, int bufsize, ParsePortListing(const char * buffer, int bufsize,
struct PortMappingParserData * pdata); struct PortMappingParserData * pdata);
/*!
* \brief free parsed data structure
*
* \param[in] pdata Parsed data to free
*/
MINIUPNP_LIBSPEC void MINIUPNP_LIBSPEC void
FreePortListing(struct PortMappingParserData * pdata); FreePortListing(struct PortMappingParserData * pdata);

View file

@ -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 /* Project : miniupnp
* Website : http://miniupnp.free.fr/ * Website : http://miniupnp.free.fr/
* Author : Thomas Bernard * 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 * This software is subject to the conditions detailed in the
* LICENCE file provided in this distribution. */ * LICENCE file provided in this distribution. */
@ -27,20 +27,14 @@
#define MINIUPNPC_IGNORE_EINTR #define MINIUPNPC_IGNORE_EINTR
#endif /* _WIN32 */ #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" #include "receivedata.h"
int int
receivedata(int socket, receivedata(SOCKET socket,
char * data, int length, char * data, int length,
int timeout, unsigned int * scope_id) int timeout, unsigned int * scope_id)
{ {
#if MINIUPNPC_GET_SRC_ADDR #ifdef MINIUPNPC_GET_SRC_ADDR
struct sockaddr_storage src_addr; struct sockaddr_storage src_addr;
socklen_t src_addr_len = sizeof(src_addr); socklen_t src_addr_len = sizeof(src_addr);
#endif /* MINIUPNPC_GET_SRC_ADDR */ #endif /* MINIUPNPC_GET_SRC_ADDR */
@ -80,7 +74,7 @@ receivedata(int socket,
return 0; return 0;
} }
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ #endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
#if MINIUPNPC_GET_SRC_ADDR #ifdef MINIUPNPC_GET_SRC_ADDR
memset(&src_addr, 0, sizeof(src_addr)); memset(&src_addr, 0, sizeof(src_addr));
n = recvfrom(socket, data, length, 0, n = recvfrom(socket, data, length, 0,
(struct sockaddr *)&src_addr, &src_addr_len); (struct sockaddr *)&src_addr, &src_addr_len);
@ -90,7 +84,7 @@ receivedata(int socket,
if(n<0) { if(n<0) {
PRINT_SOCKET_ERROR("recv"); PRINT_SOCKET_ERROR("recv");
} }
#if MINIUPNPC_GET_SRC_ADDR #ifdef MINIUPNPC_GET_SRC_ADDR
if (src_addr.ss_family == AF_INET6) { if (src_addr.ss_family == AF_INET6) {
const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr; const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr;
#ifdef DEBUG #ifdef DEBUG
@ -98,7 +92,13 @@ receivedata(int socket,
#endif /* DEBUG */ #endif /* DEBUG */
if(scope_id) if(scope_id)
*scope_id = src_addr6->sin6_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 */ #endif /* MINIUPNPC_GET_SRC_ADDR */
return n; return n;
} }

View file

@ -2,16 +2,18 @@
/* Project: miniupnp /* Project: miniupnp
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
* Author: Thomas Bernard * Author: Thomas Bernard
* Copyright (c) 2011-2012 Thomas Bernard * Copyright (c) 2011-2018 Thomas Bernard
* This software is subjects to the conditions detailed * This software is subjects to the conditions detailed
* in the LICENCE file provided within this distribution */ * in the LICENCE file provided within this distribution */
#ifndef RECEIVEDATA_H_INCLUDED #ifndef RECEIVEDATA_H_INCLUDED
#define RECEIVEDATA_H_INCLUDED #define RECEIVEDATA_H_INCLUDED
#include "miniupnpc_socketdef.h"
/* Reads data from the specified socket. /* Reads data from the specified socket.
* Returns the number of bytes read if successful, zero if no bytes were * 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. */ * 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, char * data, int length,
int timeout, unsigned int * scope_id); int timeout, unsigned int * scope_id);

944
Externals/miniupnpc/src/upnpc.c vendored Normal file
View file

@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
#include <winsock2.h>
#include "win32_snprintf.h"
#else
/* for IPPROTO_TCP / IPPROTO_UDP */
#include <netinet/in.h>
#endif
#include <ctype.h>
#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(&timestarted));
}
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; i<argc; i++)
{
if(0 == strcmp(argv[i], "--help") || 0 == strcmp(argv[i], "-h"))
{
usage(stdout, argv[0]);
return 0;
}
if(argv[i][0] == '-')
{
if(argv[i][1] == 'u')
rootdescurl = argv[++i];
else if(argv[i][1] == 'm')
{
multicastif = argv[++i];
minissdpdpath = ""; /* Disable usage of minissdpd */
}
else if(argv[i][1] == 'z')
{
char junk;
if(sscanf(argv[++i], "%d%c", &localport, &junk)!=1 ||
localport<0 || localport>65535 ||
(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<commandargc)
{
if(!is_int(commandargv[i])) {
/* 1st parameter not an integer : error */
fprintf(stderr, "command -r : %s is not an port number\n", commandargv[i]);
retcode = 1;
break;
} else if(is_int(commandargv[i+1])){
/* 2nd parameter is an integer : <port> <external_port> <protocol> */
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 : <port> <protocol> */
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<commandargc)
{
if(!is_int(commandargv[i])) {
/* 1st parameter not an integer : error */
fprintf(stderr, "command -f : %s is not an port number\n", commandargv[i]);
retcode = 1;
break;
} else if(i+1 == commandargc){
/* too few arguments */
fprintf(stderr, "command -f : too few arguments\n");
retcode = 2;
break;
} else {
/* <port> <protocol> */
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<commandargc; i++)
{
CheckPinhole(&urls, &data, commandargv[i]);
}
break;
case 'K':
for(i=0; i<commandargc; i++)
{
GetPinholePackets(&urls, &data, commandargv[i]);
}
break;
case 'D':
for(i=0; i<commandargc; i++)
{
if (RemovePinhole(&urls, &data, commandargv[i]) < 0)
retcode = 2;
}
break;
case 'S':
GetFirewallStatus(&urls, &data);
break;
case 'G':
GetPinholeOutboundTimeout(&urls, &data,
commandargv[0], commandargv[1],
commandargv[2], commandargv[3],
commandargv[4]);
break;
case 'P':
printf("Presentation URL found:\n");
printf(" %s\n", data.presentationurl);
break;
default:
fprintf(stderr, "Unknown switch -%c\n", command);
retcode = 1;
}
} else {
fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n");
retcode = 1;
}
FreeUPNPUrls(&urls);
}
else
{
fprintf(stderr, "No valid UPNP Internet Gateway Device found.\n");
retcode = 1;
}
freeUPNPDevlist(devlist); devlist = 0;
}
else
{
fprintf(stderr, "No IGD UPnP Device found on the network !\n");
retcode = 1;
}
#ifdef _WIN32
nResult = WSACleanup();
if(nResult != NO_ERROR) {
fprintf(stderr, "WSACleanup() failed.\n");
}
#endif /* _WIN32 */
return retcode;
}

File diff suppressed because it is too large Load diff

View file

@ -1,50 +1,113 @@
/* $Id: upnpcommands.h,v 1.30 2015/07/15 12:21:28 nanard Exp $ */ /* $Id: upnpcommands.h,v 1.34 2025/02/08 23:15:17 nanard Exp $ */
/* Miniupnp project : http://miniupnp.free.fr/ /* vim: tabstop=4 shiftwidth=4 noexpandtab
* Author : Thomas Bernard * Project: miniupnp
* Copyright (c) 2005-2015 Thomas Bernard * 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 * This software is subject to the conditions detailed in the
* LICENCE file provided within this distribution */ * LICENCE file provided within this distribution */
#ifndef UPNPCOMMANDS_H_INCLUDED #ifndef UPNPCOMMANDS_H_INCLUDED
#define UPNPCOMMANDS_H_INCLUDED #define UPNPCOMMANDS_H_INCLUDED
#include "upnpreplyparse.h" /*! \file upnpcommands.h
#include "portlistingparse.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 "miniupnpc_declspec.h"
#include "miniupnpctypes.h" #include "miniupnpctypes.h"
/* MiniUPnPc return codes : */ /* MiniUPnPc return codes : */
/*! \brief value for success */
#define UPNPCOMMAND_SUCCESS (0) #define UPNPCOMMAND_SUCCESS (0)
/*! \brief value for unknown error */
#define UPNPCOMMAND_UNKNOWN_ERROR (-1) #define UPNPCOMMAND_UNKNOWN_ERROR (-1)
/*! \brief error while checking the arguments */
#define UPNPCOMMAND_INVALID_ARGS (-2) #define UPNPCOMMAND_INVALID_ARGS (-2)
/*! \brief HTTP communication error */
#define UPNPCOMMAND_HTTP_ERROR (-3) #define UPNPCOMMAND_HTTP_ERROR (-3)
/*! \brief The response contains invalid values */
#define UPNPCOMMAND_INVALID_RESPONSE (-4) #define UPNPCOMMAND_INVALID_RESPONSE (-4)
/*! \brief Memory allocation error */
#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5) #define UPNPCOMMAND_MEM_ALLOC_ERROR (-5)
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #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 MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalBytesSent(const char * controlURL, UPNP_GetTotalBytesSent(const char * controlURL,
const char * servicetype); 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 MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalBytesReceived(const char * controlURL, UPNP_GetTotalBytesReceived(const char * controlURL,
const char * servicetype); 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 MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalPacketsSent(const char * controlURL, UPNP_GetTotalPacketsSent(const char * controlURL,
const char * servicetype); 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 MINIUPNP_LIBSPEC UNSIGNED_INTEGER
UPNP_GetTotalPacketsReceived(const char * controlURL, UPNP_GetTotalPacketsReceived(const char * controlURL,
const char * servicetype); const char * servicetype);
/* UPNP_GetStatusInfo() /*! \brief WANIPConnection:GetStatusInfo()
* status and lastconnerror are 64 byte buffers *
* Return values : * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1
* or a UPnP Error code */ * \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 MINIUPNP_LIBSPEC int
UPNP_GetStatusInfo(const char * controlURL, UPNP_GetStatusInfo(const char * controlURL,
const char * servicetype, const char * servicetype,
@ -52,76 +115,87 @@ UPNP_GetStatusInfo(const char * controlURL,
unsigned int * uptime, unsigned int * uptime,
char * lastconnerror); char * lastconnerror);
/* UPNP_GetConnectionTypeInfo() /*! \brief WANIPConnection:GetConnectionTypeInfo()
* argument connectionType is a 64 character buffer *
* Return Values : * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1
* or a UPnP Error code */ * \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 MINIUPNP_LIBSPEC int
UPNP_GetConnectionTypeInfo(const char * controlURL, UPNP_GetConnectionTypeInfo(const char * controlURL,
const char * servicetype, const char * servicetype,
char * connectionType); char * connectionType);
/* UPNP_GetExternalIPAddress() call the corresponding UPNP method. /*! \brief WANIPConnection:GetExternalIPAddress()
* 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.
* *
* possible UPnP Errors : * possible UPnP Errors :
* 402 Invalid Args - 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. */ * - 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 MINIUPNP_LIBSPEC int
UPNP_GetExternalIPAddress(const char * controlURL, UPNP_GetExternalIPAddress(const char * controlURL,
const char * servicetype, const char * servicetype,
char * extIpAdd); char * extIpAdd);
/* UPNP_GetLinkLayerMaxBitRates() /*! \brief UPNP_GetLinkLayerMaxBitRates()
* call WANCommonInterfaceConfig:1#GetCommonLinkProperties * call `WANCommonInterfaceConfig:GetCommonLinkProperties`
* *
* return values : * \param[in] controlURL controlURL of the WANCommonInterfaceConfig of a WANDevice
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR * \param[in] servicetype urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
* or a UPnP Error Code. */ * \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 MINIUPNP_LIBSPEC int
UPNP_GetLinkLayerMaxBitRates(const char* controlURL, UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
const char* servicetype, const char* servicetype,
unsigned int * bitrateDown, unsigned int * bitrateDown,
unsigned int * bitrateUp); unsigned int * bitrateUp);
/* UPNP_AddPortMapping() /*! \brief WANIPConnection: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.
* *
* List of possible UPnP errors for AddPortMapping : * List of possible UPnP errors for AddPortMapping :
* errorCode errorDescription (short) - Description (long) * 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. * 402 Invalid Args | See UPnP Device Architecture section on Control.
* 606 Action not authorized - The action requested REQUIRES authorization and * 501 Action Failed | See UPnP Device Architecture section on Control.
* the sender was not authorized. * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized.
* 715 WildCardNotPermittedInSrcIP - The source IP address cannot be * 715 WildCardNotPermittedInSrcIP | The source IP address cannot be wild-carded
* wild-carded * 716 WildCardNotPermittedInExtPort | The external port 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
* 718 ConflictInMappingEntry - The port mapping entry specified conflicts * 724 SamePortValuesRequired | Internal and External port values must be the same
* with a mapping assigned previously to another client * 725 OnlyPermanentLeasesSupported | The NAT implementation only supports permanent lease times on port mappings
* 724 SamePortValuesRequired - Internal and External port values * 726 RemoteHostOnlySupportsWildcard | RemoteHost must be a wildcard and cannot be a specific IP address or DNS name
* must be the same * 727 ExternalPortOnlySupportsWildcard | ExternalPort must be a wildcard and cannot be a specific port value
* 725 OnlyPermanentLeasesSupported - The NAT implementation only supports * 728 NoPortMapsAvailable | There are not enough free ports available to complete port mapping.
* permanent lease times on port mappings * 729 ConflictWithOtherMechanisms | Attempted port mapping is not allowed due to conflict with other mechanisms.
* 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard * 732 WildCardNotPermittedInIntPort | The internal port cannot be wild-carded
* and cannot be a specific IP address or DNS name *
* 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice
* cannot be a specific port value * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1
* 728 NoPortMapsAvailable - There are not enough free ports available to * \param[in] extPort External port
* complete port mapping. * \param[in] inPort Internal port
* 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed * \param[in] inClient IP of Internal client.
* due to conflict with other mechanisms. * \param[in] desc Port Mapping description. if NULL, defaults to
* 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded * "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 MINIUPNP_LIBSPEC int
UPNP_AddPortMapping(const char * controlURL, const char * servicetype, 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 * remoteHost,
const char * leaseDuration); const char * leaseDuration);
/* UPNP_AddAnyPortMapping() /*! \brief WANIPConnection:AddAnyPortMapping()
* if desc is NULL, it will be defaulted to "libminiupnpc"
* remoteHost is usually NULL because IGD don't support it.
* *
* Return values : * Only in WANIPConnection:2
* 0 : SUCCESS
* NON ZERO : ERROR. Either an UPnP error code or an unknown error.
* *
* List of possible UPnP errors for AddPortMapping : * List of possible UPnP errors for AddPortMapping :
* errorCode errorDescription (short) - Description (long) * 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. * 402 Invalid Args | See UPnP Device Architecture section on Control.
* 606 Action not authorized - The action requested REQUIRES authorization and * 501 Action Failed | See UPnP Device Architecture section on Control.
* the sender was not authorized. * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized.
* 715 WildCardNotPermittedInSrcIP - The source IP address cannot be * 715 WildCardNotPermittedInSrcIP | The source IP address cannot be wild-carded
* wild-carded * 716 WildCardNotPermittedInExtPort | The external port 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.
* 728 NoPortMapsAvailable - There are not enough free ports available to * 729 ConflictWithOtherMechanisms | Attempted port mapping is not allowed due to conflict with other mechanisms.
* complete port mapping. * 732 WildCardNotPermittedInIntPort | The internal port cannot be wild-carded
* 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed *
* due to conflict with other mechanisms. * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice
* 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded * \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 MINIUPNP_LIBSPEC int
UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype, UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
@ -167,24 +251,35 @@ UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
const char * leaseDuration, const char * leaseDuration,
char * reservedPort); char * reservedPort);
/* UPNP_DeletePortMapping() /*! \brief WANIPConnection: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.
* *
* List of possible UPnP errors for DeletePortMapping : * Use same argument values as what was used for UPNP_AddPortMapping()
* 402 Invalid Args - See UPnP Device Architecture section on Control. *
* 606 Action not authorized - The action requested REQUIRES authorization * List of possible UPnP errors for UPNP_DeletePortMapping() :
* and the sender was not authorized. * errorCode errorDescription (short) | Description (long)
* 714 NoSuchEntryInArray - The specified value does not exist in the array */ * ---------------------------------- | ------------------
* 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 MINIUPNP_LIBSPEC int
UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
const char * extPort, const char * proto, const char * extPort, const char * proto,
const char * remoteHost); const char * remoteHost);
/* UPNP_DeletePortRangeMapping() /*! \brief WANIPConnection:DeletePortRangeMapping()
*
* Only in WANIPConnection:2
* Use same argument values as what was used for AddPortMapping(). * Use same argument values as what was used for AddPortMapping().
* remoteHost is usually NULL because IGD don't support it. * remoteHost is usually NULL because IGD don't support it.
* Return Values : * 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. * NON ZERO : error. Either an UPnP error code or an undefined error.
* *
* List of possible UPnP errors for DeletePortMapping : * List of possible UPnP errors for DeletePortMapping :
* 606 Action not authorized - The action requested REQUIRES authorization * errorCode errorDescription (short) | Description (long)
* and the sender was not authorized. * ---------------------------------- | ------------------
* 730 PortMappingNotFound - This error message is returned if no port * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized.
* mapping is found in the specified range. * 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. */ * 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 MINIUPNP_LIBSPEC int
UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype, UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
const char * extPortStart, const char * extPortEnd, const char * extPortStart, const char * extPortEnd,
const char * proto, const char * proto,
const char * manage); const char * manage);
/* UPNP_GetPortMappingNumberOfEntries() /*! \brief WANIPConnection:GetPortMappingNumberOfEntries()
* not supported by all routers */ *
* 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 MINIUPNP_LIBSPEC int
UPNP_GetPortMappingNumberOfEntries(const char* controlURL, UPNP_GetPortMappingNumberOfEntries(const char * controlURL,
const char* servicetype, const char * servicetype,
unsigned int * num); unsigned int * numEntries);
/* UPNP_GetSpecificPortMappingEntry() /*! \brief retrieves an existing port mapping for a port:protocol
* 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)
* *
* return value : * List of possible UPnP errors for UPNP_GetSpecificPortMappingEntry() :
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR * errorCode errorDescription (short) | Description (long)
* or a UPnP Error Code. * ---------------------------------- | ------------------
* 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 : * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice
* 402 Invalid Args - See UPnP Device Architecture section on Control. * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1
* 501 Action Failed - See UPnP Device Architecture section on Control. * \param[in] extPort External port
* 606 Action not authorized - The action requested REQUIRES authorization * \param[in] proto `TCP` or `UDP`
* and the sender was not authorized. * \param[in] remoteHost IP or empty string for wildcard. Most IGD don't
* 714 NoSuchEntryInArray - The specified value does not exist in the array. * 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 MINIUPNP_LIBSPEC int
UPNP_GetSpecificPortMappingEntry(const char * controlURL, UPNP_GetSpecificPortMappingEntry(const char * controlURL,
@ -245,27 +360,27 @@ UPNP_GetSpecificPortMappingEntry(const char * controlURL,
char * enabled, char * enabled,
char * leaseDuration); char * leaseDuration);
/* UPNP_GetGenericPortMappingEntry() /*! \brief WANIPConnection: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)
* *
* return value : * errorCode errorDescription (short) | Description (long)
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR * ---------------------------------- | ------------------
* or a UPnP Error Code. * 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 : * \param[in] controlURL controlURL of the WANIPConnection of a WANConnectionDevice
* 402 Invalid Args - See UPnP Device Architecture section on Control. * \param[in] servicetype urn:schemas-upnp-org:service:WANIPConnection:1
* 606 Action not authorized - The action requested REQUIRES authorization * \param[in] index
* and the sender was not authorized. * \param[out] extPort 6 bytes buffer
* 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds * \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 MINIUPNP_LIBSPEC int
UPNP_GetGenericPortMappingEntry(const char * controlURL, UPNP_GetGenericPortMappingEntry(const char * controlURL,
@ -280,14 +395,25 @@ UPNP_GetGenericPortMappingEntry(const char * controlURL,
char * rHost, char * rHost,
char * duration); 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 : * errorCode errorDescription (short) | Description (long)
* 606 Action not Authorized * ---------------------------------- | ------------------
* 730 PortMappingNotFound - no port mapping is found in the specified range. * 606 Action not authorized | The action requested REQUIRES authorization and the sender was not authorized.
* 733 InconsistantParameters - NewStartPort and NewEndPort values are not * 730 PortMappingNotFound | no port mapping is found in the specified range.
* consistent. * 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 MINIUPNP_LIBSPEC int
UPNP_GetListOfPortMappings(const char * controlURL, UPNP_GetListOfPortMappings(const char * controlURL,
@ -298,15 +424,48 @@ UPNP_GetListOfPortMappings(const char * controlURL,
const char * numberOfPorts, const char * numberOfPorts,
struct PortMappingParserData * data); 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 MINIUPNP_LIBSPEC int
UPNP_GetFirewallStatus(const char * controlURL, UPNP_GetFirewallStatus(const char * controlURL,
const char * servicetype, const char * servicetype,
int * firewallEnabled, int * firewallEnabled,
int * inboundPinholeAllowed); 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 MINIUPNP_LIBSPEC int
UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype, UPNP_GetOutboundPinholeTimeout(const char * controlURL,
const char * servicetype,
const char * remoteHost, const char * remoteHost,
const char * remotePort, const char * remotePort,
const char * intClient, const char * intClient,
@ -314,6 +473,24 @@ UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype
const char * proto, const char * proto,
int * opTimeout); 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 MINIUPNP_LIBSPEC int
UPNP_AddPinhole(const char * controlURL, const char * servicetype, UPNP_AddPinhole(const char * controlURL, const char * servicetype,
const char * remoteHost, const char * remoteHost,
@ -324,18 +501,61 @@ UPNP_AddPinhole(const char * controlURL, const char * servicetype,
const char * leaseTime, const char * leaseTime,
char * uniqueID); char * uniqueID);
/*! \brief update a pinholes 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 MINIUPNP_LIBSPEC int
UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
const char * uniqueID, const char * uniqueID,
const char * leaseTime); 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 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 MINIUPNP_LIBSPEC int
UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
const char * uniqueID, int * isWorking); 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 MINIUPNP_LIBSPEC int
UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
const char * uniqueID, int * packets); const char * uniqueID, int * packets);

View file

@ -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 /* Project : miniupnp
* Web : http://miniupnp.free.fr/ * Web : http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* Author : Thomas BERNARD * 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 * This software is subjet to the conditions detailed in the
* provided LICENSE file. */ * provided LICENSE file. */
#ifndef UPNPDEV_H_INCLUDED #ifndef UPNPDEV_H_INCLUDED
#define 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" #include "miniupnpc_declspec.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/*!
* \brief UPnP device linked-list
*/
struct UPNPDev { struct UPNPDev {
/*! \brief pointer to the next element */
struct UPNPDev * pNext; struct UPNPDev * pNext;
/*! \brief root description URL */
char * descURL; char * descURL;
/*! \brief ST: as advertised */
char * st; char * st;
unsigned int scope_id; /*! \brief USN: as advertised */
char * usn; 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() /*! \brief free list returned by upnpDiscover()
* free list returned by upnpDiscover() */ * \param[in] devlist linked list to free
*/
MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist); MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);

View file

@ -1,9 +1,10 @@
/* $Id: upnperrors.c,v 1.5 2011/04/10 11:19:36 nanard Exp $ */ /* $Id: upnperrors.c,v 1.12 2023/06/26 23:19:28 nanard Exp $ */
/* Project : miniupnp /* vim: tabstop=4 shiftwidth=4 noexpandtab
* Project : miniupnp
* Author : Thomas BERNARD * Author : Thomas BERNARD
* copyright (c) 2007 Thomas Bernard * copyright (c) 2007-2023 Thomas Bernard
* All Right reserved. * 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 * This software is subjet to the conditions detailed in the
* provided LICENCE file. */ * provided LICENCE file. */
#include <string.h> #include <string.h>
@ -27,10 +28,14 @@ const char * strupnperror(int err)
case UPNPCOMMAND_INVALID_RESPONSE: case UPNPCOMMAND_INVALID_RESPONSE:
s = "Miniupnpc Invalid response"; s = "Miniupnpc Invalid response";
break; break;
case UPNPCOMMAND_HTTP_ERROR:
s = "Miniupnpc HTTP error";
break;
case UPNPDISCOVER_SOCKET_ERROR: case UPNPDISCOVER_SOCKET_ERROR:
s = "Miniupnpc Socket error"; s = "Miniupnpc Socket error";
break; break;
case UPNPDISCOVER_MEMORY_ERROR: case UPNPDISCOVER_MEMORY_ERROR:
case UPNPCOMMAND_MEM_ALLOC_ERROR:
s = "Miniupnpc Memory allocation error"; s = "Miniupnpc Memory allocation error";
break; break;
case 401: case 401:
@ -42,6 +47,24 @@ const char * strupnperror(int err)
case 501: case 501:
s = "Action Failed"; s = "Action Failed";
break; 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: case 606:
s = "Action not authorized"; s = "Action not authorized";
break; break;
@ -67,10 +90,10 @@ const char * strupnperror(int err)
s = "ProtocolWildcardingNotAllowed"; s = "ProtocolWildcardingNotAllowed";
break; break;
case 708: case 708:
s = "WildcardNotPermittedInSrcIP"; s = "InvalidLayer2Address";
break; break;
case 709: case 709:
s = "NoPacketSent"; s = "NoTrafficReceived";
break; break;
case 713: case 713:
s = "SpecifiedArrayIndexInvalid"; s = "SpecifiedArrayIndexInvalid";
@ -99,6 +122,24 @@ const char * strupnperror(int err)
case 727: case 727:
s = "ExternalPortOnlySupportsWildcard"; s = "ExternalPortOnlySupportsWildcard";
break; 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: default:
s = "UnknownError"; s = "UnknownError";
break; break;

View file

@ -1,22 +1,32 @@
/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */ /* $Id: upnperrors.h,v 1.8 2025/02/08 23:15:17 nanard Exp $ */
/* (c) 2007-2015 Thomas Bernard /* (c) 2007-2025 Thomas Bernard
* All rights reserved. * All rights reserved.
* MiniUPnP Project. * 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 * This software is subjet to the conditions detailed in the
* provided LICENCE file. */ * provided LICENCE file. */
#ifndef UPNPERRORS_H_INCLUDED #ifndef UPNPERRORS_H_INCLUDED
#define UPNPERRORS_H_INCLUDED #define UPNPERRORS_H_INCLUDED
/*! \file upnperrors.h
* \brief code to string API for errors
*/
#include "miniupnpc_declspec.h" #include "miniupnpc_declspec.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
/* strupnperror() /*!
* Return a string description of the UPnP error code * \brief convert error code to string
* or NULL for undefinded errors */ *
* 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); MINIUPNP_LIBSPEC const char * strupnperror(int err);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -1,7 +1,8 @@
/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */ /* $Id: upnpreplyparse.c,v 1.22 2025/02/08 23:12:26 nanard Exp $ */
/* MiniUPnP project /* vim: tabstop=4 shiftwidth=4 noexpandtab
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * MiniUPnP project
* (c) 2006-2015 Thomas Bernard * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* (c) 2006-2025 Thomas Bernard
* This software is subject to the conditions detailed * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
@ -12,6 +13,23 @@
#include "upnpreplyparse.h" #include "upnpreplyparse.h"
#include "minixml.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 static void
NameValueParserStartElt(void * d, const char * name, int l) NameValueParserStartElt(void * d, const char * name, int l)
{ {
@ -26,12 +44,12 @@ NameValueParserStartElt(void * d, const char * name, int l)
} }
static void 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 NameValueParserData * data = (struct NameValueParserData *)d;
struct NameValue * nv; struct NameValue * nv;
(void)name; (void)name;
(void)l; (void)namelen;
if(!data->topelt) if(!data->topelt)
return; return;
if(strcmp(data->curelt, "NewPortListing") != 0) if(strcmp(data->curelt, "NewPortListing") != 0)
@ -39,7 +57,7 @@ NameValueParserEndElt(void * d, const char * name, int l)
int l; int l;
/* standard case. Limited to n chars strings */ /* standard case. Limited to n chars strings */
l = data->cdatalen; l = data->cdatalen;
nv = malloc(sizeof(struct NameValue)); nv = malloc(sizeof(struct NameValue) + l + 1);
if(nv == NULL) if(nv == NULL)
{ {
/* malloc error */ /* malloc error */
@ -49,8 +67,6 @@ NameValueParserEndElt(void * d, const char * name, int l)
#endif /* DEBUG */ #endif /* DEBUG */
return; return;
} }
if(l>=(int)sizeof(nv->value))
l = sizeof(nv->value) - 1;
strncpy(nv->name, data->curelt, 64); strncpy(nv->name, data->curelt, 64);
nv->name[63] = '\0'; nv->name[63] = '\0';
if(data->cdata != NULL) if(data->cdata != NULL)
@ -77,6 +93,7 @@ NameValueParserGetData(void * d, const char * datas, int l)
if(strcmp(data->curelt, "NewPortListing") == 0) if(strcmp(data->curelt, "NewPortListing") == 0)
{ {
/* specific case for NewPortListing which is a XML Document */ /* specific case for NewPortListing which is a XML Document */
free(data->portListing);
data->portListing = malloc(l + 1); data->portListing = malloc(l + 1);
if(!data->portListing) if(!data->portListing)
{ {
@ -104,9 +121,7 @@ ParseNameValue(const char * buffer, int bufsize,
struct NameValueParserData * data) struct NameValueParserData * data)
{ {
struct xmlparser parser; struct xmlparser parser;
data->l_head = NULL; memset(data, 0, sizeof(struct NameValueParserData));
data->portListing = NULL;
data->portListingLength = 0;
/* init xmlparser object */ /* init xmlparser object */
parser.xmlstart = buffer; parser.xmlstart = buffer;
parser.xmlsize = bufsize; parser.xmlsize = bufsize;
@ -137,7 +152,7 @@ ClearNameValueList(struct NameValueParserData * pdata)
char * char *
GetValueFromNameValueList(struct NameValueParserData * pdata, GetValueFromNameValueList(struct NameValueParserData * pdata,
const char * Name) const char * name)
{ {
struct NameValue * nv; struct NameValue * nv;
char * p = NULL; char * p = NULL;
@ -145,37 +160,12 @@ GetValueFromNameValueList(struct NameValueParserData * pdata,
(nv != NULL) && (p == NULL); (nv != NULL) && (p == NULL);
nv = nv->l_next) nv = nv->l_next)
{ {
if(strcmp(nv->name, Name) == 0) if(strcmp(nv->name, name) == 0)
p = nv->value; p = nv->value;
} }
return p; 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 /* debug all-in-one function
* do parsing then display to stdout */ * do parsing then display to stdout */
#ifdef DEBUG #ifdef DEBUG

View file

@ -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 /* MiniUPnP project
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ * http://miniupnp.free.fr/ or https://miniupnp.tuxfamily.org/
* (c) 2006-2013 Thomas Bernard * (c) 2006-2025 Thomas Bernard
* This software is subject to the conditions detailed * This software is subject to the conditions detailed
* in the LICENCE file provided within the distribution */ * in the LICENCE file provided within the distribution */
#ifndef UPNPREPLYPARSE_H_INCLUDED #ifndef UPNPREPLYPARSE_H_INCLUDED
#define UPNPREPLYPARSE_H_INCLUDED #define UPNPREPLYPARSE_H_INCLUDED
/*! \file upnpreplyparse.h
* \brief Parsing of UPnP SOAP responses
*/
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
struct NameValue { /*! \brief Name/Value linked list
struct NameValue * l_next; * not exposed in the public API
char name[64]; */
char value[128]; struct NameValue;
};
/*! \brief data structure for parsing */
struct NameValueParserData { struct NameValueParserData {
/*! \brief name/value linked list */
struct NameValue * l_head; struct NameValue * l_head;
/*! \brief current element name */
char curelt[64]; char curelt[64];
/*! \brief port listing array */
char * portListing; char * portListing;
/*! \brief port listing array length */
int portListingLength; int portListingLength;
/*! \brief flag indicating the current element is */
int topelt; int topelt;
/*! \brief top element character data */
const char * cdata; const char * cdata;
/*! \brief top element character data length */
int cdatalen; 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 void
ParseNameValue(const char * buffer, int bufsize, ParseNameValue(const char * buffer, int bufsize,
struct NameValueParserData * data); struct NameValueParserData * data);
/* ClearNameValueList() */ /*!
* \brief free memory
*
* \param[in,out] pdata data structure
*/
void void
ClearNameValueList(struct NameValueParserData * pdata); 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 * char *
GetValueFromNameValueList(struct NameValueParserData * pdata, GetValueFromNameValueList(struct NameValueParserData * pdata,
const char * Name); const char * name);
#if 0
/* GetValueFromNameValueListIgnoreNS() */
char *
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
const char * Name);
#endif
/* DisplayNameValueList() */ /* DisplayNameValueList() */
#ifdef DEBUG #ifdef DEBUG
@ -60,4 +80,3 @@ DisplayNameValueList(char * buffer, int bufsize);
#endif #endif
#endif #endif

View file

@ -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 <stdio.h>
/* 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 */