From 60515d893a031bc420ad5752dbcd34e99d0dd717 Mon Sep 17 00:00:00 2001 From: Eden Abitbol Date: Mon, 3 Jun 2019 12:23:00 -0400 Subject: [PATCH] upnp: support libupnp and libnatnatpmp simultaneously Update libupnp to version 1.8.4. For windows, the IPV6 preprocessor must be undefined. Or else libupnp won't initialize. Added visual studio 2017 support via one patch for windows that also combines previous windows patches. UPnPController: Class that the jami classes use to control the opening and closing of ports. Every service has it's own upnp controller. The controller does it's actions by using the upnp context class. Also refactored the functions used to add mappings. Instead of using two different functions with different types of parameters, we now use one function with parameters that have default values. The logic stays the same but the function call is more clear. UPnPContext: Class that holds a (linked) list of discovered IGDs and their corresponding protocols (which discovered them). Whenever the controller wants to add or remove a mapping, the context picks a valid IGD in it's list and uses the correct protocol to complete the required action. This class also has the ability to swap protocols for an IGD that was discovered by more then one protocol. UPnPProtocol: Virtual base class that defines the functions needed by the context to use the corresponding protocol. PUPnP: UPnPProtocol derived class that represents a upnp client that uses the portable upnp library (libupnp). Every time the client discovers a new IGD it uses a callback to add it to the context's main IGD linked list. It also has an internal list of IGDs that it discovered. Added features to this class include: - IGD event subscription. - Use UpnpInit2 function instead of deprecated UpnpInit function. It's also supposed to support IPv6. NatPmp: UPnPProtocol derived class that represents a upnp client that uses the NAT-PMP library (libnatpmp). Unlike libupnp, libnatpmp only supports discovering one IGD. Also uses callbacks to add the IGD it finds to the context's main IGD class. Also inclided debug warning prints whenever a controller opens and closes the ports. That way we can keep track of whenever the application opens and closes ports on the internet gateway device. Gitlab: #96 Change-Id: I199271edac2c6d93dc60c24e2e2aefe36de7950c --- MSVC/ring-daemon.vcxproj | 42 +- MSVC/ring-daemon.vcxproj.filters | 80 +- configure.ac | 5 + contrib/build_all.bat | 10 +- contrib/src/upnp/SHA512SUMS | 2 +- contrib/src/upnp/fetch_and_patch.bat | 9 +- contrib/src/upnp/libupnp-ipv6.patch | 8 +- contrib/src/upnp/libupnp-vs2017.patch | 904 ----- contrib/src/upnp/libupnp-win32.patch | 68 - contrib/src/upnp/libupnp-win64.patch | 41 - contrib/src/upnp/libupnp-windows.patch | 3531 +++++++++++++++-- contrib/src/upnp/miniserver.patch | 24 +- contrib/src/upnp/rules.mak | 18 +- contrib/src/upnp/threadpool.patch | 53 +- contrib/src/upnp/uuid_upnp.patch | 124 - src/account.cpp | 4 +- src/account.h | 2 +- src/ice_transport.cpp | 6 +- src/jamidht/jamiaccount.cpp | 4 +- src/sip/sipaccount.cpp | 6 +- src/sip/sipcall.cpp | 8 +- src/upnp/Makefile.am | 11 +- src/upnp/protocol/Makefile.am | 33 + src/upnp/protocol/global_mapping.h | 55 + src/upnp/protocol/igd.cpp | 38 + src/upnp/protocol/igd.h | 60 + .../{upnp_igd.cpp => protocol/mapping.cpp} | 30 +- src/upnp/protocol/mapping.h | 91 + src/upnp/protocol/natpmp/Makefile.am | 12 + src/upnp/protocol/natpmp/nat_pmp.cpp | 334 ++ src/upnp/protocol/natpmp/nat_pmp.h | 93 + src/upnp/protocol/natpmp/pmp_igd.cpp | 76 + src/upnp/protocol/natpmp/pmp_igd.h | 71 + src/upnp/protocol/pupnp/Makefile.am | 12 + src/upnp/protocol/pupnp/pupnp.cpp | 924 +++++ src/upnp/protocol/pupnp/pupnp.h | 143 + src/upnp/protocol/pupnp/upnp_igd.cpp | 63 + src/upnp/protocol/pupnp/upnp_igd.h | 76 + src/upnp/protocol/upnp_protocol.h | 99 + src/upnp/upnp_context.cpp | 1413 ++----- src/upnp/upnp_context.h | 266 +- src/upnp/upnp_control.cpp | 79 +- src/upnp/upnp_control.h | 105 +- src/upnp/upnp_igd.h | 243 -- 44 files changed, 5890 insertions(+), 3386 deletions(-) delete mode 100644 contrib/src/upnp/libupnp-vs2017.patch delete mode 100644 contrib/src/upnp/libupnp-win32.patch delete mode 100644 contrib/src/upnp/libupnp-win64.patch delete mode 100644 contrib/src/upnp/uuid_upnp.patch create mode 100644 src/upnp/protocol/Makefile.am create mode 100644 src/upnp/protocol/global_mapping.h create mode 100644 src/upnp/protocol/igd.cpp create mode 100644 src/upnp/protocol/igd.h rename src/upnp/{upnp_igd.cpp => protocol/mapping.cpp} (75%) create mode 100644 src/upnp/protocol/mapping.h create mode 100644 src/upnp/protocol/natpmp/Makefile.am create mode 100644 src/upnp/protocol/natpmp/nat_pmp.cpp create mode 100644 src/upnp/protocol/natpmp/nat_pmp.h create mode 100644 src/upnp/protocol/natpmp/pmp_igd.cpp create mode 100644 src/upnp/protocol/natpmp/pmp_igd.h create mode 100644 src/upnp/protocol/pupnp/Makefile.am create mode 100644 src/upnp/protocol/pupnp/pupnp.cpp create mode 100644 src/upnp/protocol/pupnp/pupnp.h create mode 100644 src/upnp/protocol/pupnp/upnp_igd.cpp create mode 100644 src/upnp/protocol/pupnp/upnp_igd.h create mode 100644 src/upnp/protocol/upnp_protocol.h delete mode 100644 src/upnp/upnp_igd.h diff --git a/MSVC/ring-daemon.vcxproj b/MSVC/ring-daemon.vcxproj index eb9501c10..1f777b471 100644 --- a/MSVC/ring-daemon.vcxproj +++ b/MSVC/ring-daemon.vcxproj @@ -244,7 +244,7 @@ TurnOffAllWarnings Disabled false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) RING_UWP;_USE_MATH_DEFINES;DEBUG_FPS;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) @@ -279,7 +279,7 @@ TurnOffAllWarnings Disabled false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) RING_UWP;_USE_MATH_DEFINES;DEBUG_FPS;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) @@ -322,7 +322,7 @@ TurnOffAllWarnings Disabled false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) RING_UWP;_USE_MATH_DEFINES;DEBUG_FPS;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) @@ -365,7 +365,7 @@ TurnOffAllWarnings Disabled false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) RING_UWP;_USE_MATH_DEFINES;DEBUG_FPS;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) 4996;4503;4180;4244;4267; true @@ -392,7 +392,7 @@ TurnOffAllWarnings Disabled false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) RING_UWP;_USE_MATH_DEFINES;DEBUG_FPS;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) 4996;4503;4180;4244;4267; true @@ -424,7 +424,7 @@ TurnOffAllWarnings Disabled false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) RING_UWP;_USE_MATH_DEFINES;DEBUG_FPS;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) 4996;4503;4180;4244;4267; true @@ -458,7 +458,7 @@ true true false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) RING_UWP;_USE_MATH_DEFINES;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) @@ -494,7 +494,7 @@ true true false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) _USE_MATH_DEFINES;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) @@ -536,7 +536,7 @@ true true false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x86\include;%(AdditionalIncludeDirectories) _USE_MATH_DEFINES;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) @@ -578,7 +578,7 @@ true true false - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\pupnp\upnp\inc;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) RING_UWP;STATICLIB;_USE_MATH_DEFINES;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;WIN32_NATIVE;_MBCS;%(PreprocessorDefinitions) 4996;4503;4180;4244;4267; true @@ -601,7 +601,7 @@ false - false + true @@ -616,7 +616,7 @@ true - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\include;%(AdditionalIncludeDirectories) STATIC_GETOPT;_USE_MATH_DEFINES;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;RING_UWP;WIN32_NATIVE;_MBCS;OPENDHT_PROXY_CLIENT;OPENDHT_PROXY_SERVER;OPENDHT_PUSH_NOTIFICATIONS;%(PreprocessorDefinitions) 4996;4503;4180;4244;4267; true @@ -644,7 +644,7 @@ - ws2_32.lib;iphlpapi.lib;advapi32.lib;avcodec.lib;avdevice.lib;avfilter.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;libgnutls.lib;lib_json.lib;libopendht.lib;Argon2Ref.lib;secp256k1.lib;libyaml-cppmd.lib;portaudio.lib;libupnp.lib;pjsip-core-x86_64-x64-vc15-Release.lib;pjsip-simple-x86_64-x64-vc15-Release.lib;pjsua2-lib-x86_64-x64-vc15-Release.lib;pjsua-lib-x86_64-x64-vc15-Release.lib;pjsip-ua-x86_64-x64-vc15-Release.lib;pjmedia-x86_64-x64-vc15-Release.lib;pjlib-util-x86_64-x64-vc15-Release.lib;pjlib-x86_64-x64-vc15-Release.lib;pjnath-x86_64-x64-vc15-Release.lib;restbed.lib;libeay32.lib;ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;ixml.lib;pthreads.lib;advapi32.lib;avcodec.lib;avdevice.lib;avfilter.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;libgnutls.lib;lib_json.lib;libopendht.lib;Argon2Ref.lib;secp256k1.lib;libyaml-cppmd.lib;portaudio.lib;libupnp.lib;pjsip-core-x86_64-x64-vc15-Release.lib;pjsip-simple-x86_64-x64-vc15-Release.lib;pjsua2-lib-x86_64-x64-vc15-Release.lib;pjsua-lib-x86_64-x64-vc15-Release.lib;pjsip-ua-x86_64-x64-vc15-Release.lib;pjmedia-x86_64-x64-vc15-Release.lib;pjlib-util-x86_64-x64-vc15-Release.lib;pjlib-x86_64-x64-vc15-Release.lib;pjnath-x86_64-x64-vc15-Release.lib;restbed.lib;libeay32.lib;ssleay32.lib;%(AdditionalDependencies) $(ProjectDir)..\contrib\build\ffmpeg\Build\Windows10\x64\bin;$(ProjectDir)..\contrib\msvc\lib\x64;$(ProjectDir)..\contrib\build\boost\stage\lib;$(ProjectDir)..\contrib\build\pjproject\pjsip\lib;$(ProjectDir)..\contrib\build\pjproject\pjmedia\lib;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\lib;$(ProjectDir)..\contrib\build\pjproject\third_party\lib;$(ProjectDir)..\contrib\build\pjproject\pjlib\lib;$(ProjectDir)..\contrib\build\pjproject\pjnath\lib;$(ProjectDir)..\contrib\build\restbed\build\Release;$(ProjectDir)..\contrib\build\restbed\dependency\openssl\out32dll;$(ProjectDir)..\contrib\build\argon2\vs2015\Argon2Ref\vs2015\build;$(ProjectDir)..\contrib\build\yaml-cpp\msvc\Release /ignore:4006 /ignore:4221 %(AdditionalOptions) true @@ -658,7 +658,7 @@ true - $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\win32\x64\include;$(ProjectDir)..\contrib\build\sndfile\src;%(AdditionalIncludeDirectories) + $(ProjectDir);$(ProjectDir)..\;$(ProjectDir)..\src;$(ProjectDir)..\src\client;$(ProjectDir)..\src\config;$(ProjectDir)..\src\dring;$(ProjectDir)..\src\hooks;$(ProjectDir)..\src\im;$(ProjectDir)..\src\media;$(ProjectDir)..\src\jamidht;$(ProjectDir)..\src\security;$(ProjectDir)..\src\sip;$(ProjectDir)..\src\upnp;$(ProjectDir)..\src\upnp\igd;$(ProjectDir)..\src\upnp\protocol;$(ProjectDir)..\src\upnp\mapping;$(ProjectDir)..\src\jamidht\eth;$(ProjectDir)..\contrib\msvc;$(ProjectDir)..\contrib\msvc\include;$(ProjectDir)..\contrib\msvc\include\upnp;$(ProjectDir)..\contrib\build\msgpack-c\include;$(ProjectDir)..\contrib\build\jsoncpp\include;$(ProjectDir)..\contrib\build\yaml-cpp\include;$(ProjectDir)..\contrib\build\pjproject\pjlib\include;$(ProjectDir)..\contrib\build\pjproject\pjnath\include;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\include;$(ProjectDir)..\contrib\build\pjproject\pjsip\include;$(ProjectDir)..\contrib\build\pjproject\third_party;$(ProjectDir)..\contrib\build\pjproject\pjmedia\include;$(ProjectDir)..\contrib\build\restbed\source;$(ProjectDir)..\contrib\build\ffmpeg\Build\win32\x64\include;$(ProjectDir)..\contrib\build\sndfile\src;%(AdditionalIncludeDirectories) STATIC_GETOPT;_USE_MATH_DEFINES;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;NOMINMAX;HAVE_CONFIG_H;WIN32_LEAN_AND_MEAN;_MBCS;OPENDHT_PROXY_CLIENT;OPENDHT_PROXY_SERVER;OPENDHT_PUSH_NOTIFICATIONS;%(PreprocessorDefinitions) 4996;4503;4180;4244;4267; true @@ -687,7 +687,7 @@ - ws2_32.lib;iphlpapi.lib;advapi32.lib;avcodec.lib;avdevice.lib;avfilter.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;libgnutls.lib;lib_json.lib;libopendht.lib;Argon2Ref.lib;secp256k1.lib;libyaml-cppmd.lib;portaudio.lib;libupnp.lib;pjsip-core-x86_64-x64-vc15-Release.lib;pjsip-simple-x86_64-x64-vc15-Release.lib;pjsua2-lib-x86_64-x64-vc15-Release.lib;pjsua-lib-x86_64-x64-vc15-Release.lib;pjsip-ua-x86_64-x64-vc15-Release.lib;pjmedia-x86_64-x64-vc15-Release.lib;pjlib-util-x86_64-x64-vc15-Release.lib;pjlib-x86_64-x64-vc15-Release.lib;pjnath-x86_64-x64-vc15-Release.lib;restbed.lib;libeay32.lib;ssleay32.lib;%(AdditionalDependencies) + ws2_32.lib;advapi32.lib;avcodec.lib;avdevice.lib;avfilter.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;libgnutls.lib;lib_json.lib;libopendht.lib;Argon2Ref.lib;secp256k1.lib;libyaml-cppmd.lib;portaudio.lib;libupnp.lib;pjsip-core-x86_64-x64-vc15-Release.lib;pjsip-simple-x86_64-x64-vc15-Release.lib;pjsua2-lib-x86_64-x64-vc15-Release.lib;pjsua-lib-x86_64-x64-vc15-Release.lib;pjsip-ua-x86_64-x64-vc15-Release.lib;pjmedia-x86_64-x64-vc15-Release.lib;pjlib-util-x86_64-x64-vc15-Release.lib;pjlib-x86_64-x64-vc15-Release.lib;pjnath-x86_64-x64-vc15-Release.lib;restbed.lib;libeay32.lib;ssleay32.lib;%(AdditionalDependencies) $(ProjectDir)..\contrib\build\ffmpeg\Build\win32\x64\bin;$(ProjectDir)..\contrib\msvc\lib\x64;$(ProjectDir)..\contrib\build\boost\stage\lib;$(ProjectDir)..\contrib\build\pjproject\pjsip\lib;$(ProjectDir)..\contrib\build\pjproject\pjmedia\lib;$(ProjectDir)..\contrib\build\pjproject\pjlib-util\lib;$(ProjectDir)..\contrib\build\pjproject\third_party\lib;$(ProjectDir)..\contrib\build\pjproject\pjlib\lib;$(ProjectDir)..\contrib\build\pjproject\pjnath\lib;$(ProjectDir)..\contrib\build\restbed\build\Release;$(ProjectDir)..\contrib\build\restbed\dependency\openssl\out32dll;$(ProjectDir)..\contrib\build\argon2\vs2015\Argon2Ref\vs2015\build;$(ProjectDir)..\contrib\build\yaml-cpp\msvc\Release;$(ProjectDir)..\contrib\build\sndfile\msvc\x64\Release /ignore:4006 /ignore:4221 %(AdditionalOptions) true @@ -828,9 +828,12 @@ + + + + - @@ -987,9 +990,14 @@ + + + + + + - diff --git a/MSVC/ring-daemon.vcxproj.filters b/MSVC/ring-daemon.vcxproj.filters index 4c833a923..b61f30c0f 100644 --- a/MSVC/ring-daemon.vcxproj.filters +++ b/MSVC/ring-daemon.vcxproj.filters @@ -62,6 +62,12 @@ {96b01e7f-7e05-480c-85a3-fa35ef5f8b87} + + {13d62143-0c05-410a-bc32-8748a87deaa9} + + + {a75d6869-9eb3-4cdd-8006-08b5ee5e6050} + @@ -175,15 +181,6 @@ Source Files\jamidht - - Source Files\upnp - - - Source Files\upnp - - - Source Files\upnp - Source Files\sip @@ -328,9 +325,6 @@ Source Files - - Source Files - Source Files @@ -409,6 +403,24 @@ Source Files\media\video + + Source Files\upnp + + + Source Files\upnp + + + Source Files\upnp\protocol + + + Source Files\upnp\protocol + + + Source Files\upnp\protocol\pupnp + + + Source Files\upnp\protocol\pupnp + @@ -507,9 +519,6 @@ Source Files - - Source Files - Source Files @@ -606,9 +615,6 @@ Source Files\jamidht - - Source Files\jamidht - Source Files\jamidht @@ -792,15 +798,6 @@ Source Files\sip - - Source Files\upnp - - - Source Files\upnp - - - Source Files\upnp - Source Files\media @@ -843,10 +840,37 @@ Source Files\media\video + + Source Files\upnp + + + Source Files\upnp + + + Source Files\jamidht + + + Source Files\upnp\protocol + + + Source Files\upnp\protocol + + + Source Files\upnp\protocol + + + Source Files\upnp\protocol + + + Source Files\upnp\protocol\pupnp + + + Source Files\upnp\protocol\pupnp + Source Files\jamidht\eth\libdevcore - \ No newline at end of file + diff --git a/configure.ac b/configure.ac index 0fefb1e0d..536927e52 100644 --- a/configure.ac +++ b/configure.ac @@ -566,6 +566,7 @@ AS_IF([test "x$with_upnp" = "xyes"], [AC_MSG_WARN([Missing libupnp development files]) AC_DEFINE([HAVE_LIBUPNP], 0, [Define if you have libupnp])]) ]) +AM_CONDITIONAL([BUILD_PUPNP], test "x$with_upnp" = "xyes") # LIBNATPMP dnl check for libnatpmp @@ -580,6 +581,7 @@ AS_IF([test "x$with_natpmp" != xno], ],[]) AC_DEFINE_UNQUOTED([HAVE_LIBNATPMP], `if test "x$with_natpmp" != xno; then echo 1; else echo 0; fi`, [Define if you have libnatpmp]) +AM_CONDITIONAL([BUILD_NATPMP], test "x$with_natpmp" = "xyes") AC_DEFINE_UNQUOTED([HAVE_SHM], `if test -z "${HAVE_LINUX_TRUE}" && test -z "${HAVE_ANDROID_FALSE}" ; then echo 1; else echo 0; fi`, [Define if you have shared memory support]) @@ -632,6 +634,9 @@ AC_CONFIG_FILES([Makefile \ src/media/video/winvideo/Makefile \ src/security/Makefile \ src/upnp/Makefile \ + src/upnp/protocol/Makefile \ + src/upnp/protocol/natpmp/Makefile \ + src/upnp/protocol/pupnp/Makefile \ ringtones/Makefile \ test/Makefile\ test/sip/Makefile diff --git a/contrib/build_all.bat b/contrib/build_all.bat index 8e1d306f1..e70ebb3bf 100644 --- a/contrib/build_all.bat +++ b/contrib/build_all.bat @@ -68,9 +68,8 @@ msgpack=build\msgpack-c\vs2017\msgpackc-static.vcxproj, ^ opendht=build\opendht\MSVC\opendht.vcxproj, ^ pjproject=pjproject, ^ pthreads=build\pthreads\MSVC\pthreads.vcxproj, ^ -xml=build\libupnp\build\vs2017\ixml.vcxproj, ^ -threadutil=build\libupnp\build\vs2017\threadutil.vcxproj, ^ -upnp=build\libupnp\build\vs2017\libupnp.vcxproj, ^ +xml=build\pupnp\build\vs2017\ixml.vcxproj, ^ +upnp=build\pupnp\build\vs2017\libupnp.vcxproj, ^ secp256k1=build\secp256k1\MSVC\secp256k1.vcxproj, ^ portaudio=build\portaudio\msvc\portaudio.vcxproj, ^ yaml-cpp=build\yaml-cpp\msvc\yaml-cpp.vcxproj, ^ @@ -95,9 +94,8 @@ msgpack=build\msgpack-c\vs2017\msgpackc-static.vcxproj, ^ opendht=build\opendht\MSVC\opendht.vcxproj, ^ pjproject=pjproject, ^ pthreads=build\pthreads\MSVC\pthreads.vcxproj, ^ -xml=build\libupnp\build\vs2017\ixml.vcxproj, ^ -threadutil=build\libupnp\build\vs2017\threadutil.vcxproj, ^ -upnp=build\libupnp\build\vs2017\libupnp.vcxproj, ^ +xml=build\pupnp\build\vs2017\ixml.vcxproj, ^ +upnp=build\pupnp\build\vs2017\libupnp.vcxproj, ^ secp256k1=build\secp256k1\MSVC\secp256k1.vcxproj, ^ portaudio=build\portaudio\msvc\portaudio.vcxproj, ^ yaml-cpp=build\yaml-cpp\msvc\yaml-cpp.vcxproj diff --git a/contrib/src/upnp/SHA512SUMS b/contrib/src/upnp/SHA512SUMS index caa04b0b5..9f1d758ec 100644 --- a/contrib/src/upnp/SHA512SUMS +++ b/contrib/src/upnp/SHA512SUMS @@ -1 +1 @@ -0c44356cd466c72baa9a15a45bc601d3d88a5c1efea79495e07581d93edfeaf3d6e47a301a51e3fa54a776945e0d6b65a67998c480e918a535eeb80d18b05fa9 pupnp-release-1.6.25.tar.gz +6d96dc0dcf187a425f3b60f4e750102331bd0a5bd452007e345eeacb63e9287ac213574c8071294809283ff8d8795433706ed2a14bae3d451605bf7a75e5a5bb pupnp-release-1.8.4.tar.gz diff --git a/contrib/src/upnp/fetch_and_patch.bat b/contrib/src/upnp/fetch_and_patch.bat index 90ae973a6..219f6e195 100644 --- a/contrib/src/upnp/fetch_and_patch.bat +++ b/contrib/src/upnp/fetch_and_patch.bat @@ -1,6 +1,6 @@ set BUILD=%SRC%..\build -set UPNP_VERSION=1.6.25 +set UPNP_VERSION=1.8.4 set UPNP_URL=https://github.com/mrjimenez/pupnp/archive/release-%UPNP_VERSION%.tar.gz mkdir %BUILD% @@ -13,11 +13,10 @@ if %USE_CACHE%==1 ( 7z -y x release-%UPNP_VERSION%.tar.gz && 7z -y x release-%UPNP_VERSION%.tar -o%BUILD% del release-%UPNP_VERSION%.tar && del release-%UPNP_VERSION%.tar.gz && del %BUILD%\pax_global_header -rename %BUILD%\pupnp-release-%UPNP_VERSION% libupnp +rename %BUILD%\pupnp-release-%UPNP_VERSION% pupnp -cd %BUILD%\libupnp +cd %BUILD%\pupnp %APPLY_CMD% %SRC%\upnp\libupnp-windows.patch -%APPLY_CMD% %SRC%\upnp\libupnp-vs2017.patch -cd %SRC% \ No newline at end of file +cd %SRC% diff --git a/contrib/src/upnp/libupnp-ipv6.patch b/contrib/src/upnp/libupnp-ipv6.patch index 7b89bc268..81df7a64f 100644 --- a/contrib/src/upnp/libupnp-ipv6.patch +++ b/contrib/src/upnp/libupnp-ipv6.patch @@ -15,7 +15,7 @@ index af310ca..1ae422f 100644 @@ -68,6 +68,13 @@ /*! . */ #define APPLICATION_LISTENING_PORT 49152 - + +/* IPV6_V6ONLY is missing from MinGW, hack taken from + * http://svn.apache.org/repos/asf/apr/apr/trunk/network_io/win32/sockopt.c + */ @@ -33,7 +33,7 @@ index 231c2c5..6a9c27f 100644 @@ -69,6 +69,13 @@ #endif /* UPNP_ENABLE_IPV6 */ #endif /* INCLUDE_CLIENT_APIS */ - + +/* IPV6_V6ONLY is missing from MinGW, hack taken from + * http://svn.apache.org/repos/asf/apr/apr/trunk/network_io/win32/sockopt.c + */ @@ -42,8 +42,8 @@ index 231c2c5..6a9c27f 100644 +#endif + void RequestHandler(); - + enum Listener { --- +-- 1.7.9.7 diff --git a/contrib/src/upnp/libupnp-vs2017.patch b/contrib/src/upnp/libupnp-vs2017.patch deleted file mode 100644 index f78b812b6..000000000 --- a/contrib/src/upnp/libupnp-vs2017.patch +++ /dev/null @@ -1,904 +0,0 @@ ---- /dev/null -+++ b/build/vs2017/ixml.vcxproj -@@ -0,0 +1,203 @@ -+ -+ -+ -+ -+ Debug -+ Win32 -+ -+ -+ Debug -+ x64 -+ -+ -+ Release -+ Win32 -+ -+ -+ Release -+ x64 -+ -+ -+ -+ {9C2C266D-35A3-465F-A297-0E21D54E5C89} -+ ixml -+ 10.0.16299.0 -+ -+ -+ -+ StaticLibrary -+ v141 -+ NotSet -+ true -+ -+ -+ StaticLibrary -+ v141 -+ NotSet -+ -+ -+ StaticLibrary -+ v141 -+ NotSet -+ true -+ -+ -+ StaticLibrary -+ v141 -+ NotSet -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ <_ProjectFileVersion>14.0.24730.2 -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(SolutionDir)\x86\tmp\$(ProjectName)\ -+ true -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(SolutionDir)\$(Platform)\tmp\$(ProjectName)\ -+ true -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(SolutionDir)\x86\tmp\$(ProjectName)\ -+ true -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(ProjectDir)..\..\MSVC\$(Platform)\tmp\$(ProjectName)\ -+ true -+ -+ -+ -+ Disabled -+ ..\..\ixml\inc;..\..\ixml\src\inc;..\inc;..\..\upnp\inc;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;DEBUG;WIN32;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ EnableFastChecks -+ MultiThreadedDebug -+ -+ $(IntDir) -+ $(OutDir)\lib\x86\$(ProjectName).pdb -+ Level3 -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ $(OutDir);$(OutDir)..\lib\x86;$(OutDir)..\bin\x86;%(AdditionalLibraryDirectories) -+ $(OutDir)\lib\x86\$(TargetName)$(TargetExt) -+ -+ -+ -+ -+ X64 -+ -+ -+ Disabled -+ ..\..\ixml\inc;..\..\ixml\src\inc;..\inc;..\..\upnp\inc;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;DEBUG;WIN32;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ EnableFastChecks -+ MultiThreadedDebug -+ -+ $(IntDir) -+ $(OutDir)\lib\x64\$(ProjectName).pdb -+ Level3 -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ $(OutDir);$(OutDir)..\lib\x64;$(OutDir)..\bin\x64;%(AdditionalLibraryDirectories) -+ $(OutDir)\lib\x64\$(TargetName)$(TargetExt) -+ -+ -+ -+ -+ MaxSpeed -+ Default -+ true -+ ..\..\ixml\inc;..\..\ixml\src\inc;..\inc;..\..\upnp\inc;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;WIN32;NDEBUG;RELEASE;_WINDOWS;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ MultiThreaded -+ true -+ -+ $(IntDir) -+ $(OutDir)\lib\x86\$(ProjectName).pdb -+ Level3 -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ $(OutDir);$(OutDir)..\lib\x86;$(OutDir)..\bin\x86;%(AdditionalLibraryDirectories) -+ $(OutDir)\lib\x86\$(TargetName)$(TargetExt) -+ -+ -+ -+ -+ X64 -+ -+ -+ MaxSpeed -+ Default -+ true -+ ..\..\ixml\inc;..\..\ixml\src\inc;..\inc;..\..\upnp\inc;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;WIN32;NDEBUG;RELEASE;_WINDOWS;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ MultiThreaded -+ true -+ -+ $(IntDir) -+ $(OutDir)\lib\x64\$(ProjectName).pdb -+ Level3 -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ $(OutDir);$(OutDir)..\lib\x64;$(OutDir)..\bin\x64;%(AdditionalLibraryDirectories) -+ $(OutDir)\lib\x64\$(TargetName)$(TargetExt) -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/build/vs2017/libupnp.sln -@@ -0,0 +1,51 @@ -+ -+Microsoft Visual Studio Solution File, Format Version 12.00 -+# Visual Studio 15 -+VisualStudioVersion = 15.0.27703.2026 -+MinimumVisualStudioVersion = 10.0.40219.1 -+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libupnp", "libupnp.vcxproj", "{6227F51A-1498-4C4A-B213-F6FDED605125}" -+EndProject -+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ixml", "ixml.vcxproj", "{9C2C266D-35A3-465F-A297-0E21D54E5C89}" -+EndProject -+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "threadutil", "threadutil.vcxproj", "{1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D}" -+EndProject -+Global -+ GlobalSection(SolutionConfigurationPlatforms) = preSolution -+ Debug|Win32 = Debug|Win32 -+ Debug|x64 = Debug|x64 -+ Release|Win32 = Release|Win32 -+ Release|x64 = Release|x64 -+ EndGlobalSection -+ GlobalSection(ProjectConfigurationPlatforms) = postSolution -+ {6227F51A-1498-4C4A-B213-F6FDED605125}.Debug|Win32.ActiveCfg = Debug|Win32 -+ {6227F51A-1498-4C4A-B213-F6FDED605125}.Debug|Win32.Build.0 = Debug|Win32 -+ {6227F51A-1498-4C4A-B213-F6FDED605125}.Debug|x64.ActiveCfg = Debug|x64 -+ {6227F51A-1498-4C4A-B213-F6FDED605125}.Debug|x64.Build.0 = Debug|x64 -+ {6227F51A-1498-4C4A-B213-F6FDED605125}.Release|Win32.ActiveCfg = Release|Win32 -+ {6227F51A-1498-4C4A-B213-F6FDED605125}.Release|Win32.Build.0 = Release|Win32 -+ {6227F51A-1498-4C4A-B213-F6FDED605125}.Release|x64.ActiveCfg = Release|x64 -+ {6227F51A-1498-4C4A-B213-F6FDED605125}.Release|x64.Build.0 = Release|x64 -+ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Debug|Win32.ActiveCfg = Debug|Win32 -+ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Debug|Win32.Build.0 = Debug|Win32 -+ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Debug|x64.ActiveCfg = Debug|x64 -+ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Debug|x64.Build.0 = Debug|x64 -+ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Release|Win32.ActiveCfg = Release|Win32 -+ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Release|Win32.Build.0 = Release|Win32 -+ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Release|x64.ActiveCfg = Release|x64 -+ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Release|x64.Build.0 = Release|x64 -+ {1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D}.Debug|Win32.ActiveCfg = Debug|Win32 -+ {1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D}.Debug|Win32.Build.0 = Debug|Win32 -+ {1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D}.Debug|x64.ActiveCfg = Debug|x64 -+ {1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D}.Debug|x64.Build.0 = Debug|x64 -+ {1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D}.Release|Win32.ActiveCfg = Release|Win32 -+ {1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D}.Release|Win32.Build.0 = Release|Win32 -+ {1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D}.Release|x64.ActiveCfg = Release|x64 -+ {1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D}.Release|x64.Build.0 = Release|x64 -+ EndGlobalSection -+ GlobalSection(SolutionProperties) = preSolution -+ HideSolutionNode = FALSE -+ EndGlobalSection -+ GlobalSection(ExtensibilityGlobals) = postSolution -+ SolutionGuid = {C0989A12-7B9E-4AF3-8C0F-930A122FC7F4} -+ EndGlobalSection -+EndGlobal ---- /dev/null -+++ b/build/vs2017/libupnp.vcxproj -@@ -0,0 +1,440 @@ -+ -+ -+ -+ -+ Debug -+ Win32 -+ -+ -+ Debug -+ x64 -+ -+ -+ Release -+ Win32 -+ -+ -+ Release -+ x64 -+ -+ -+ -+ {6227F51A-1498-4C4A-B213-F6FDED605125} -+ libupnp -+ 10.0.16299.0 -+ -+ -+ -+ StaticLibrary -+ v141 -+ false -+ NotSet -+ false -+ -+ -+ StaticLibrary -+ v141 -+ false -+ NotSet -+ -+ -+ StaticLibrary -+ v141 -+ false -+ NotSet -+ false -+ -+ -+ StaticLibrary -+ v141 -+ false -+ NotSet -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ <_ProjectFileVersion>14.0.24730.2 -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(SolutionDir)\x86\tmp\$(ProjectName)\ -+ libupnpd -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(SolutionDir)\$(Platform)\tmp\$(ProjectName)\ -+ libupnpd -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(SolutionDir)\x86\tmp\$(ProjectName)\ -+ libupnp -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(ProjectDir)..\..\MSVC\$(Platform)\tmp\$(ProjectName)\ -+ libupnp -+ -+ -+ -+ _DEBUG;%(PreprocessorDefinitions) -+ true -+ true -+ Win32 -+ .\Debug/libupnp.tlb -+ -+ -+ -+ Disabled -+ ..\inc;..\msvc;..\..\upnp\inc;..\..\upnp\src\inc;..\..\ixml\inc;..\..\ixml\src\inc;..\..\threadutil\inc;..\..\pthreads;..\..\pthreads\include;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;WIN32;DEBUG;_WINDOWS;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ EnableFastChecks -+ MultiThreadedDebug -+ -+ $(IntDir) -+ $(IntDir) -+ $(OutDir)\lib\x86\$(ProjectName).pdb -+ -+ Level3 -+ true -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ _DEBUG;%(PreprocessorDefinitions) -+ 0x0407 -+ -+ -+ $(OutDir)\lib\x86;$(OutDir)\bin\x86;%(AdditionalLibraryDirectories) -+ false -+ $(OutDir)\lib\x86\$(TargetName)$(TargetExt) -+ -+ -+ true -+ $(OutDir)libupnp.bsc -+ -+ -+ true -+ -+ -+ mkdir "$(OutDir)"\include -+ -+mkdir "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\upnp\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\build\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\ixml\src\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\ixml\inc\*.h "$(OutDir)"\include\upnp -+ -+ -+ -+ -+ -+ _DEBUG;%(PreprocessorDefinitions) -+ true -+ true -+ X64 -+ .\Debug/libupnp.tlb -+ -+ -+ -+ Disabled -+ ..\inc;..\msvc;..\..\upnp\inc;..\..\upnp\src\inc;..\..\ixml\inc;..\..\ixml\src\inc;..\..\threadutil\inc;..\..\pthreads;..\..\pthreads\include;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;WIN32;DEBUG;_WINDOWS;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ EnableFastChecks -+ MultiThreadedDebug -+ -+ $(IntDir) -+ $(IntDir) -+ $(OutDir)\lib\x64\$(ProjectName).pdb -+ -+ Level3 -+ true -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ _DEBUG;%(PreprocessorDefinitions) -+ 0x0407 -+ -+ -+ $(OutDir)\lib\x64;$(OutDir)\bin\x64;%(AdditionalLibraryDirectories) -+ $(OutDir)\lib\x64\$(TargetName)$(TargetExt) -+ -+ -+ true -+ $(OutDir)libupnp.bsc -+ -+ -+ mkdir "$(OutDir)"\include -+ -+mkdir "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\upnp\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\build\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\ixml\src\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\ixml\inc\*.h "$(OutDir)"\include\upnp -+ -+ -+ -+ -+ -+ -+ -+ -+ NDEBUG;%(PreprocessorDefinitions) -+ true -+ true -+ Win32 -+ .\Release/libupnp.tlb -+ -+ -+ -+ MaxSpeed -+ Default -+ true -+ false -+ ..\inc;..\msvc;..\..\upnp\inc;..\..\upnp\src\inc;..\..\ixml\inc;..\..\ixml\src\inc;..\..\threadutil\inc;..\..\pthreads;..\..\pthreads\include;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\pthreads;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;WIN32;NDEBUG;RELEASE;_WINDOWS;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ MultiThreaded -+ true -+ -+ $(IntDir) -+ $(IntDir) -+ $(OutDir)\lib\x86\$(ProjectName).pdb -+ -+ Level3 -+ true -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ NDEBUG;%(PreprocessorDefinitions) -+ 0x0407 -+ -+ -+ $(OutDir)\lib\x86;$(OutDir)\bin\x86;%(AdditionalLibraryDirectories) -+ $(OutDir)\lib\x86\$(TargetName)$(TargetExt) -+ -+ -+ true -+ .\Release/libupnp.bsc -+ -+ -+ mkdir "$(OutDir)"\include -+ -+mkdir "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\upnp\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\build\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\ixml\src\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\ixml\inc\*.h "$(OutDir)"\include\upnp -+ -+ -+ -+ -+ -+ NDEBUG;%(PreprocessorDefinitions) -+ true -+ true -+ X64 -+ .\Release/libupnp.tlb -+ -+ -+ -+ MinSpace -+ Default -+ true -+ false -+ ..\inc;..\msvc;..\..\upnp\inc;..\..\upnp\src\inc;..\..\ixml\inc;..\..\ixml\src\inc;..\..\threadutil\inc;..\..\pthreads;..\..\pthreads\include;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\pthreads;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;WIN32;NDEBUG;RELEASE;_WINDOWS;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ MultiThreaded -+ true -+ -+ $(IntDir) -+ $(IntDir) -+ $(OutDir)\lib\x64\$(ProjectName).pdb -+ -+ Level3 -+ true -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ NDEBUG;%(PreprocessorDefinitions) -+ 0x0407 -+ -+ -+ $(OutDir)\lib\x64;$(OutDir)\bin\x64;%(AdditionalLibraryDirectories) -+ -+ -+ /IGNORE:4006 %(AdditionalOptions) -+ $(OutDir)\lib\x64\$(TargetName)$(TargetExt) -+ -+ -+ true -+ .\Release/libupnp.bsc -+ -+ -+ mkdir "$(OutDir)"\include -+ -+mkdir "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\upnp\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\build\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\ixml\src\inc\*.h "$(OutDir)"\include\upnp -+ -+xcopy /S /Y $(ProjectDir)..\..\ixml\inc\*.h "$(OutDir)"\include\upnp -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ {9c2c266d-35a3-465f-a297-0e21d54e5c89} -+ false -+ -+ -+ {1d3eef7a-d248-48c0-b6b5-eca229fe4b3d} -+ false -+ -+ -+ -+ -+ -+ -\ No newline at end of file ---- /dev/null -+++ b/build/vs2017/threadutil.vcxproj -@@ -0,0 +1,192 @@ -+ -+ -+ -+ -+ Debug -+ Win32 -+ -+ -+ Debug -+ x64 -+ -+ -+ Release -+ Win32 -+ -+ -+ Release -+ x64 -+ -+ -+ -+ {1D3EEF7A-D248-48C0-B6B5-ECA229FE4B3D} -+ threadutil -+ 10.0.16299.0 -+ -+ -+ -+ StaticLibrary -+ v141 -+ NotSet -+ true -+ -+ -+ StaticLibrary -+ v141 -+ NotSet -+ -+ -+ StaticLibrary -+ v141 -+ NotSet -+ true -+ -+ -+ StaticLibrary -+ v141 -+ NotSet -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ <_ProjectFileVersion>14.0.24730.2 -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(SolutionDir)\x86\tmp\$(ProjectName)\ -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(SolutionDir)\$(Platform)\tmp\$(ProjectName)\ -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(SolutionDir)\x86\tmp\$(ProjectName)\ -+ -+ -+ $(ProjectDir)..\..\..\..\msvc\ -+ $(ProjectDir)..\..\MSVC\$(Platform)\tmp\$(ProjectName)\ -+ -+ -+ -+ Disabled -+ ..\..\threadutil\inc;..\..\upnp\inc;..\..\..\..\msvc\include;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;DEBUG;WIN32;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ EnableFastChecks -+ MultiThreadedDebug -+ $(IntDir) -+ $(OutDir)\lib\x86\$(ProjectName).pdb -+ Level3 -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ $(OutDir)\lib\x86;$(OutDir)\bin\x86;$(OutDir);%(AdditionalLibraryDirectories) -+ $(OutDir)\lib\x86\$(TargetName)$(TargetExt) -+ -+ -+ -+ -+ X64 -+ -+ -+ Disabled -+ ..\..\threadutil\inc;..\..\upnp\inc;..\..\..\..\msvc\include;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;DEBUG;WIN32;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ EnableFastChecks -+ MultiThreadedDebug -+ $(IntDir) -+ $(OutDir)\lib\x64\$(ProjectName).pdb -+ Level3 -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ $(OutDir)\lib\x64;$(OutDir)\bin\x64;$(OutDir);%(AdditionalLibraryDirectories) -+ $(OutDir)\lib\x64\$(TargetName)$(TargetExt) -+ -+ -+ -+ -+ MaxSpeed -+ Default -+ true -+ ..\..\threadutil\inc;..\..\upnp\inc;..\..\..\..\msvc\include;$(ProjectDir)..\..\..\pthreads;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\pthreads;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;WIN32;NDEBUG;RELEASE;_WINDOWS;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ MultiThreaded -+ true -+ $(IntDir) -+ $(OutDir)\lib\x86\$(ProjectName).pdb -+ Level3 -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ $(OutDir)\lib\x86;$(OutDir)\bin\x86;$(OutDir);$(ProjectDir)..\..\..\bin\x64;%(AdditionalLibraryDirectories) -+ $(OutDir)\lib\x86\$(TargetName)$(TargetExt) -+ -+ -+ -+ -+ X64 -+ -+ -+ MaxSpeed -+ Default -+ true -+ ..\..\threadutil\inc;..\..\upnp\inc;..\..\..\..\msvc\include;$(ProjectDir)..\..\..\include;$(ProjectDir)..\..\..\pthreads;%(AdditionalIncludeDirectories) -+ _TIMESPEC_DEFINED;WIN32;NDEBUG;RELEASE;_WINDOWS;PTW32_STATIC_LIB;UPNP_STATIC_LIB;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) -+ true -+ MultiThreaded -+ true -+ $(IntDir) -+ $(OutDir)\lib\x64\$(ProjectName).pdb -+ Level3 -+ ProgramDatabase -+ CompileAsC -+ 4244;4267;4311;4477;4996; -+ -+ -+ $(OutDir)\lib\x64;$(OutDir)\bin\x64;$(OutDir);$(ProjectDir)..\..\..\lib\x64;%(AdditionalLibraryDirectories) -+ pthreads.lib -+ /IGNORE:4006 %(AdditionalOptions) -+ $(OutDir)\lib\x64\$(TargetName)$(TargetExt) -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -+ -\ No newline at end of file --- -2.10.2.windows.1 - diff --git a/contrib/src/upnp/libupnp-win32.patch b/contrib/src/upnp/libupnp-win32.patch deleted file mode 100644 index 2b6c63953..000000000 --- a/contrib/src/upnp/libupnp-win32.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 1e14f51854a54e0aea7186b7408f2f74621ab4df Mon Sep 17 00:00:00 2001 -From: fner -Date: Thu, 24 May 2018 11:20:33 -0400 -Subject: [PATCH] b - ---- - configure.ac | 1 + - libupnp.pc.in | 2 +- - upnp/inc/UpnpInet.h | 5 ----- - upnp/src/inc/upnputil.h | 2 +- - 4 files changed, 3 insertions(+), 7 deletions(-) - -diff --git a/configure.ac b/configure.ac -index bcf2395..335a516 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -559,6 +559,7 @@ fi - RT_BOOL_ARG_ENABLE([samples], [yes], [compilation of upnp/sample/ code]) - - -+AC_DEFINE([_WIN32_WINNT], 0x0501, [Define to '0x0500' for Windows 2000 APIs.]) - # - # doc installation - # autoconf >= 2.60 already defines ${docdir}, but we will not use its -diff --git a/libupnp.pc.in b/libupnp.pc.in -index c649c24..f46f774 100644 ---- a/libupnp.pc.in -+++ b/libupnp.pc.in -@@ -6,6 +6,6 @@ includedir=@includedir@ - Name: libupnp - Description: Linux SDK for UPnP Devices - Version: @VERSION@ --Libs: @PTHREAD_CFLAGS@ @PTHREAD_LIBS@ -L${libdir} -lupnp -lthreadutil -lixml -+Libs: @PTHREAD_CFLAGS@ -L${libdir} -lupnp -lthreadutil -lixml -liphlpapi @PTHREAD_LIBS@ - Cflags: -I${includedir}/upnp - -diff --git a/upnp/inc/UpnpInet.h b/upnp/inc/UpnpInet.h -index ad00a7d..14f87f7 100644 ---- a/upnp/inc/UpnpInet.h -+++ b/upnp/inc/UpnpInet.h -@@ -15,11 +15,6 @@ - - #ifdef WIN32 - #include -- #ifndef UPNP_USE_MSVCPP -- /* Removed: not required (and cause compilation issues) */ -- #include -- #include -- #endif - #include - #include - #include -diff --git a/upnp/src/inc/upnputil.h b/upnp/src/inc/upnputil.h -index 76292f8..ed922e1 100644 ---- a/upnp/src/inc/upnputil.h -+++ b/upnp/src/inc/upnputil.h -@@ -130,7 +130,7 @@ void linecopylen( - #define strncasecmp strnicmp - #define sleep(a) Sleep((a)*1000) - #define usleep(a) Sleep((a)/1000) -- #define strerror_r(a,b,c) (strerror_s((b),(c),(a))) -+ #define strerror_r(a,b,c) strncpy( b, strerror(a), c) - #else - #define max(a, b) (((a)>(b))? (a):(b)) - #define min(a, b) (((a)<(b))? (a):(b)) --- -2.14.3 - diff --git a/contrib/src/upnp/libupnp-win64.patch b/contrib/src/upnp/libupnp-win64.patch deleted file mode 100644 index 8064e6d21..000000000 --- a/contrib/src/upnp/libupnp-win64.patch +++ /dev/null @@ -1,41 +0,0 @@ ---- libupnp/threadutil/inc/ThreadPool.h 2011-01-20 07:46:57.000000000 +0100 -+++ libupnp.new/threadutil/inc/ThreadPool.h 2011-09-23 01:36:12.000000000 +0200 -@@ -45,6 +45,7 @@ - #include - - #ifdef WIN32 -+ #ifndef _TIMEZONE_DEFINED - #include - struct timezone - { -@@ -52,6 +53,7 @@ - int tz_dsttime; /* type of dst correction */ - }; - int gettimeofday(struct timeval *tv, struct timezone *tz); -+ #endif - #else /* WIN32 */ - #include - #include /* for gettimeofday() */ ---- libupnp-1.6.16/upnp/inc/upnp.h.orig 2012-03-22 00:15:38.000000000 +0100 -+++ libupnp-1.6.16/upnp/inc/upnp.h 2012-03-28 18:58:55.043642000 +0200 -@@ -61,6 +61,20 @@ - /* Other systems ??? */ - #endif - -+# if defined( __MINGW32__ ) -+# if !defined( _OFF_T_ ) -+ typedef long long _off_t; -+ typedef _off_t off_t; -+# define _OFF_T_ -+# else -+# ifdef off_t -+# undef off_t -+# endif -+# define off_t long long -+# endif -+# endif -+ -+ - #define LINE_SIZE (size_t)180 - #define NAME_SIZE (size_t)256 - #define MNFT_NAME_SIZE 64 diff --git a/contrib/src/upnp/libupnp-windows.patch b/contrib/src/upnp/libupnp-windows.patch index 5484fe98d..3c0de75dc 100644 --- a/contrib/src/upnp/libupnp-windows.patch +++ b/contrib/src/upnp/libupnp-windows.patch @@ -1,148 +1,2785 @@ +From 338e06a609d4a44458e671f5c93e392fb194688b Mon Sep 17 00:00:00 2001 +From: Eden Abitbol +Date: Wed, 12 Jun 2019 16:19:25 -0400 +Subject: [PATCH] Windows patch. + +--- + .gitignore | 6 +- + build/vs2017/.gitignore | 4 + + build/vs2017/ixml.vcxproj | 233 +++++++++++++ + build/vs2017/ixml.vcxproj.filters | 65 ++++ + build/vs2017/libupnp.sln | 60 ++++ + build/vs2017/libupnp.vcxproj | 503 +++++++++++++++++++++++++++++ + build/vs2017/libupnp.vcxproj.filters | 407 +++++++++++++++++++++++ + upnp/inc/FreeList.h | 131 ++++++++ + upnp/inc/LinkedList.h | 289 +++++++++++++++++ + upnp/inc/ThreadPool.h | 538 +++++++++++++++++++++++++++++++ + upnp/inc/TimerThread.h | 161 +++++++++ + upnp/inc/UpnpGlobal.h | 3 + + upnp/inc/upnp.h | 13 + + upnp/inc/upnpconfig.h | 132 ++++++++ + upnp/src/api/upnpapi.c | 2 +- + upnp/src/api/upnptools.c | 4 +- + upnp/src/genlib/miniserver/miniserver.c | 5 + + upnp/src/genlib/net/http/httpreadwrite.c | 4 +- + upnp/src/genlib/net/http/webserver.c | 4 +- + upnp/src/genlib/net/uri/uri.c | 4 +- + upnp/src/inc/autoconfig.h | 229 +++++++++++++ + upnp/src/inc/inet_pton.h | 50 --- + upnp/src/inc/upnputil.h | 2 +- + upnp/src/inet_pton.c | 323 ------------------- + upnp/src/soap/soap_device.c | 4 +- + upnp/src/ssdp/ssdp_ctrlpt.c | 4 +- + upnp/src/ssdp/ssdp_device.c | 4 +- + upnp/src/ssdp/ssdp_server.c | 9 +- + upnp/src/urlconfig/urlconfig.c | 4 +- + upnp/src/uuid/sysdep.c | 13 +- + 30 files changed, 2818 insertions(+), 392 deletions(-) + create mode 100644 build/vs2017/.gitignore + create mode 100644 build/vs2017/ixml.vcxproj + create mode 100644 build/vs2017/ixml.vcxproj.filters + create mode 100644 build/vs2017/libupnp.sln + create mode 100644 build/vs2017/libupnp.vcxproj + create mode 100644 build/vs2017/libupnp.vcxproj.filters + create mode 100644 upnp/inc/FreeList.h + create mode 100644 upnp/inc/LinkedList.h + create mode 100644 upnp/inc/ThreadPool.h + create mode 100644 upnp/inc/TimerThread.h + create mode 100644 upnp/inc/upnpconfig.h + create mode 100644 upnp/src/inc/autoconfig.h + delete mode 100644 upnp/src/inc/inet_pton.h + delete mode 100644 upnp/src/inet_pton.c + +--- a/.gitignore ++++ b/.gitignore +@@ -55,8 +55,6 @@ modules.builtin + include/config + include/linux/version.h + include/generated +-build/inc/autoconfig.h +-build/inc/upnpconfig.h + + # stgit generated dirs + patches-* +@@ -91,7 +89,6 @@ pupnp.includes + Makefile + Makefile.in + aclocal.m4 +-autoconfig.h + autoconfig.h.in + autom4te.cache/ + build-aux/ +@@ -107,7 +104,7 @@ m4/ltversion.m4 + m4/lt~obsolete.m4 + stamp-h1 + upnp/inc/stamp-h2 +-upnp/inc/upnpconfig.h ++ + upnp/sample/tv_combo + upnp/sample/tv_combo-1.8 + upnp/sample/tv_ctrlpt +@@ -135,7 +132,6 @@ upnp/test_url.trs + /build/vc10/out.vc9.Win32/Debug + /build/vc10/out.vc10.Win32 + /build/vc10/out.vc10.x64 +-/pthreads + /build/vc10/ipch + /build/vc10/IUpnpErrFile.txt + /build/vc10/IUpnpInfoFile.txt --- /dev/null -+++ b/build/inc/upnpconfig.h -@@ -0,0 +1,136 @@ -+/* upnp/inc/upnpconfig.h. Generated from upnpconfig.h.in by configure. */ -+/* -*- C -*- */ ++++ b/build/vs2017/.gitignore +@@ -0,0 +1,4 @@ ++ ++*.suo ++*.user ++*.sdf +\ No newline at end of file +--- /dev/null ++++ b/build/vs2017/ixml.vcxproj +@@ -0,0 +1,233 @@ ++ ++ ++ ++ ++ Debug ++ Win32 ++ ++ ++ Debug ++ x64 ++ ++ ++ Release ++ Win32 ++ ++ ++ Release ++ x64 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ {9C2C266D-35A3-465F-A297-0E21D54E5C89} ++ ixml ++ 10.0.16299.0 ++ ++ ++ ++ StaticLibrary ++ NotSet ++ true ++ v141 ++ ++ ++ StaticLibrary ++ NotSet ++ v141 ++ ++ ++ StaticLibrary ++ NotSet ++ true ++ v141 ++ ++ ++ StaticLibrary ++ NotSet ++ v141 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ <_ProjectFileVersion>10.0.40219.1 ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\ ++ $(SolutionDir)Build\$(Platform)\$(Configuration)\tmp\ ++ true ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\ ++ $(SolutionDir)Build\$(Platform)\$(Configuration)\tmp\ ++ true ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\ ++ $(SolutionDir)Build\$(Platform)\$(Configuration)\tmp\ ++ true ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\ ++ $(SolutionDir)Build\$(Platform)\$(Configuration)\tmp\ ++ true ++ AllRules.ruleset ++ ++ ++ AllRules.ruleset ++ ++ ++ AllRules.ruleset ++ ++ ++ AllRules.ruleset ++ ++ ++ ++ ++ ++ Disabled ++ ..\..\ixml\inc;..\..\ixml\src\inc;..\inc;..\..\upnp\inc;..\..\.;%(AdditionalIncludeDirectories) ++ DEBUG;WIN32;_USRDLL;LIBUPNP_EXPORTS;UPNP_USE_MSVCPP;IXML_HAVE_SCRIPTSUPPORT;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) ++ true ++ EnableFastChecks ++ MultiThreadedDebugDLL ++ ++ ++ $(IntDir) ++ $(OutDir)$(ProjectName).pdb ++ Level3 ++ ProgramDatabase ++ CompileAsC ++ ++ ++ $(OutDir)..\lib;$(OutDir)..\bin;%(AdditionalLibraryDirectories) ++ ++ ++ $(IntDir)$(MSBuildProjectName).log ++ ++ ++ mkdir $(SolutionDir)..\..\..\..\msvc\include ++mkdir $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\ixml\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\ixml\src\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++ ++ ++ ++ ++ X64 ++ ++ ++ Disabled ++ ..\..\ixml\inc;..\..\ixml\src\inc;..\inc;..\..\upnp\inc;..\..\.;%(AdditionalIncludeDirectories) ++ DEBUG;WIN32;_USRDLL;LIBUPNP_EXPORTS;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) ++ true ++ EnableFastChecks ++ MultiThreadedDebugDLL ++ ++ ++ $(IntDir) ++ $(OutDir)$(ProjectName).pdb ++ Level3 ++ ProgramDatabase ++ CompileAsC ++ ++ ++ $(OutDir)..\lib;$(OutDir)..\bin;%(AdditionalLibraryDirectories) ++ ++ ++ mkdir $(SolutionDir)..\..\..\..\msvc\include ++mkdir $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\ixml\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\ixml\src\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++ ++ ++ ++ ++ MaxSpeed ++ Default ++ true ++ ..\..\ixml\inc;..\..\ixml\src\inc;..\inc;..\..\upnp\inc;..\..\.;%(AdditionalIncludeDirectories) ++ WIN32;RELEASE;NDEBUG;_WINDOWS;_USRDLL;LIBUPNP_EXPORTS;UPNP_USE_MSVCPP;IXML_HAVE_SCRIPTSUPPORT;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) ++ true ++ MultiThreadedDLL ++ true ++ ++ ++ $(IntDir) ++ $(OutDir)$(ProjectName).pdb ++ Level3 ++ ProgramDatabase ++ CompileAsC ++ ++ ++ $(OutDir)..\lib;$(OutDir)..\bin;%(AdditionalLibraryDirectories) ++ ++ ++ mkdir $(SolutionDir)..\..\..\..\msvc\include ++mkdir $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\ixml\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\ixml\src\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++ ++ ++ ++ ++ X64 ++ ++ ++ MaxSpeed ++ Default ++ true ++ ..\..\ixml\inc;..\..\ixml\src\inc;..\inc;..\..\upnp\inc;..\..\upnp\src\inc;..\..\.;%(AdditionalIncludeDirectories) ++ WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBUPNP_EXPORTS;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) ++ true ++ MultiThreadedDLL ++ true ++ ++ ++ $(IntDir) ++ $(OutDir)$(ProjectName).pdb ++ Level3 ++ ProgramDatabase ++ CompileAsC ++ false ++ ++ ++ $(OutDir)..\lib;$(OutDir)..\bin;%(AdditionalLibraryDirectories) ++ ++ ++ mkdir $(SolutionDir)..\..\..\..\msvc\include ++mkdir $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\ixml\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\ixml\src\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++ ++ ++ ++ ++ ++ +\ No newline at end of file +--- /dev/null ++++ b/build/vs2017/ixml.vcxproj.filters +@@ -0,0 +1,65 @@ ++ ++ ++ ++ ++ {93995380-89BD-4b04-88EB-625FBE52EBFB} ++ h;hpp;hxx;hm;inl;inc;xsd ++ ++ ++ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} ++ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx ++ ++ ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ ++ ++ sources ++ ++ ++ sources ++ ++ ++ sources ++ ++ ++ sources ++ ++ ++ sources ++ ++ ++ sources ++ ++ ++ sources ++ ++ ++ sources ++ ++ ++ sources ++ ++ ++ sources ++ ++ ++ +\ No newline at end of file +--- /dev/null ++++ b/build/vs2017/libupnp.sln +@@ -0,0 +1,60 @@ ++ ++Microsoft Visual Studio Solution File, Format Version 12.00 ++# Visual Studio 15 ++VisualStudioVersion = 15.0.28307.645 ++MinimumVisualStudioVersion = 10.0.40219.1 ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libupnp", "libupnp.vcxproj", "{6227F51A-1498-4C4A-B213-F6FDED605125}" ++EndProject ++Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ixml", "ixml.vcxproj", "{9C2C266D-35A3-465F-A297-0E21D54E5C89}" ++EndProject ++Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{55AF07A8-18AA-45B8-A231-5082F1C6FC08}" ++ ProjectSection(SolutionItems) = preProject ++ ..\..\AUTHORS = ..\..\AUTHORS ++ ..\..\bootstrap = ..\..\bootstrap ++ ..\..\ChangeLog = ..\..\ChangeLog ++ ..\..\configure.ac = ..\..\configure.ac ++ ..\..\COPYING = ..\..\COPYING ++ ..\..\Doxyfile = ..\..\Doxyfile ++ ..\..\INSTALL = ..\..\INSTALL ++ ..\..\libupnp.pc.in = ..\..\libupnp.pc.in ++ ..\..\libupnp.spec = ..\..\libupnp.spec ++ ..\..\LICENSE = ..\..\LICENSE ++ ..\..\Makefile.am = ..\..\Makefile.am ++ ..\..\NEWS = ..\..\NEWS ++ ..\..\README = ..\..\README ++ ..\..\THANKS = ..\..\THANKS ++ ..\..\TODO = ..\..\TODO ++ EndProjectSection ++EndProject ++Global ++ GlobalSection(SolutionConfigurationPlatforms) = preSolution ++ Debug|Win32 = Debug|Win32 ++ Debug|x64 = Debug|x64 ++ Release|Win32 = Release|Win32 ++ Release|x64 = Release|x64 ++ EndGlobalSection ++ GlobalSection(ProjectConfigurationPlatforms) = postSolution ++ {6227F51A-1498-4C4A-B213-F6FDED605125}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {6227F51A-1498-4C4A-B213-F6FDED605125}.Debug|Win32.Build.0 = Debug|Win32 ++ {6227F51A-1498-4C4A-B213-F6FDED605125}.Debug|x64.ActiveCfg = Debug|x64 ++ {6227F51A-1498-4C4A-B213-F6FDED605125}.Debug|x64.Build.0 = Debug|x64 ++ {6227F51A-1498-4C4A-B213-F6FDED605125}.Release|Win32.ActiveCfg = Release|Win32 ++ {6227F51A-1498-4C4A-B213-F6FDED605125}.Release|Win32.Build.0 = Release|Win32 ++ {6227F51A-1498-4C4A-B213-F6FDED605125}.Release|x64.ActiveCfg = Release|x64 ++ {6227F51A-1498-4C4A-B213-F6FDED605125}.Release|x64.Build.0 = Release|x64 ++ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Debug|Win32.ActiveCfg = Debug|Win32 ++ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Debug|Win32.Build.0 = Debug|Win32 ++ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Debug|x64.ActiveCfg = Debug|x64 ++ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Debug|x64.Build.0 = Debug|x64 ++ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Release|Win32.ActiveCfg = Release|Win32 ++ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Release|Win32.Build.0 = Release|Win32 ++ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Release|x64.ActiveCfg = Release|x64 ++ {9C2C266D-35A3-465F-A297-0E21D54E5C89}.Release|x64.Build.0 = Release|x64 ++ EndGlobalSection ++ GlobalSection(SolutionProperties) = preSolution ++ HideSolutionNode = FALSE ++ EndGlobalSection ++ GlobalSection(ExtensibilityGlobals) = postSolution ++ SolutionGuid = {EC77EE93-6BCC-4784-9370-EFEC22AE22CD} ++ EndGlobalSection ++EndGlobal +--- /dev/null ++++ b/build/vs2017/libupnp.vcxproj +@@ -0,0 +1,503 @@ ++ ++ ++ ++ ++ Debug ++ Win32 ++ ++ ++ Debug ++ x64 ++ ++ ++ Release ++ Win32 ++ ++ ++ Release ++ x64 ++ ++ ++ ++ {6227F51A-1498-4C4A-B213-F6FDED605125} ++ libupnp ++ 10.0.16299.0 ++ ++ ++ ++ StaticLibrary ++ false ++ NotSet ++ v141 ++ ++ ++ StaticLibrary ++ false ++ NotSet ++ true ++ v141 ++ ++ ++ StaticLibrary ++ false ++ NotSet ++ v141 ++ ++ ++ StaticLibrary ++ Static ++ NotSet ++ true ++ v141 ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ <_ProjectFileVersion>10.0.40219.1 ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\ ++ $(SolutionDir)Build\$(Platform)\$(Configuration)\tmp\ ++ false ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\ ++ $(SolutionDir)Build\$(Platform)\$(Configuration)\tmp\ ++ false ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\ ++ $(SolutionDir)Build\$(Platform)\$(Configuration)\tmp\ ++ false ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\ ++ $(SolutionDir)Build\$(Platform)\$(Configuration)\tmp\ ++ false ++ AllRules.ruleset ++ ++ ++ AllRules.ruleset ++ ++ ++ AllRules.ruleset ++ ++ ++ AllRules.ruleset ++ ++ ++ ++ ++ ++ NDEBUG;%(PreprocessorDefinitions) ++ true ++ true ++ Win32 ++ .\Release/libupnp.tlb ++ ++ ++ ++ ++ MaxSpeed ++ Default ++ true ++ true ++ ..\..\upnp\inc;..\..\upnp\src\inc;..\..\..\..\msvc\include;..\..\..\..\msvc\include\upnp;..\..\upnp\src\threadutil;..\..\ixml\inc;..\..\ixml\src\inc;..\..\..\..\msvc\include;..\..\..\..\msvc\lib\win32 ++ IXML_HAVE_SCRIPTSUPPORT;WIN32;NDEBUG;RELEASE;_WINDOWS;_USRDLL;LIBUPNP_EXPORTS;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions);_WINSOCK_DEPRECATED_NO_WARNINGS;UPNP_STATIC_LIB ++ true ++ MultiThreadedDLL ++ true ++ ++ ++ $(IntDir) ++ $(IntDir) ++ $(OutDir)$(ProjectName).pdb ++ ++ ++ Level3 ++ true ++ ProgramDatabase ++ CompileAsC ++ ++ ++ NDEBUG;%(PreprocessorDefinitions) ++ 0x0407 ++ ++ ++ pthreadvc2.lib;ws2_32.lib;iphlpapi.lib;ixml.lib;%(AdditionalDependencies) ++ $(OutDir)$(ProjectName).dll ++ true ++ ..\..\pthreads\;..\..\pthreads\lib;$(OutDir)..\lib\ixml;%(AdditionalLibraryDirectories) ++ true ++ $(OutDir)$(ProjectName).pdb ++ Windows ++ true ++ true ++ UseLinkTimeCodeGeneration ++ false ++ ++ ++ $(TargetDir)$(TargetName).lib ++ MachineX86 ++ ++ ++ true ++ .\Release/libupnp.bsc ++ ++ ++ mkdir $(SolutionDir)..\..\..\..\msvc\include ++mkdir $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\src\inc\membuffer.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\src\inc\miniserver.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++ Add pthreadVC2.dll to output ++ ++ ++ ixml.lib;pthreads.lib;%(AdditionalDependencies) ++ ++ ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\;%(AdditionalLibraryDirectories) ++ ++ ++ ++ ++ NDEBUG;%(PreprocessorDefinitions) ++ true ++ true ++ X64 ++ .\Release/libupnp.tlb ++ ++ ++ ++ ++ MaxSpeed ++ Default ++ true ++ false ++ ..\..\upnp\inc;..\..\upnp\src\inc;..\..\ixml\inc;..\..\ixml\src\inc;$(SolutionDir)..\..\..\..\msvc\include\; ++ WIN32;NDEBUG;_WINDOWS;PTW32_STATIC_LIB;RELEASE;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions);_WINSOCK_DEPRECATED_NO_WARNINGS;UPNP_STATIC_LIB ++ true ++ MultiThreadedDLL ++ true ++ ++ ++ $(IntDir) ++ $(IntDir) ++ $(OutDir)$(ProjectName).pdb ++ ++ ++ Level3 ++ true ++ ProgramDatabase ++ CompileAsC ++ false ++ ++ ++ NDEBUG;%(PreprocessorDefinitions) ++ 0x0407 ++ ++ ++ pthreadvc2.lib;ws2_32.lib;iphlpapi.lib;ixml.lib;%(AdditionalDependencies) ++ $(OutDir)$(ProjectName).dll ++ true ++ ..\..\pthreads\;..\..\pthreads\lib;$(OutDir)..\lib\ixml;%(AdditionalLibraryDirectories) ++ true ++ $(OutDir)$(ProjectName).pdb ++ Windows ++ true ++ true ++ UseLinkTimeCodeGeneration ++ false ++ ++ ++ $(TargetDir)$(TargetName).lib ++ MachineX64 ++ ++ ++ true ++ .\Release/libupnp.bsc ++ ++ ++ ws2_32.lib;ixml.lib;pthreads.lib;iphlpapi.lib;%(AdditionalDependencies) ++ ++ ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\;%(AdditionalLibraryDirectories) ++ ++ ++ mkdir $(SolutionDir)..\..\..\..\msvc\include ++mkdir $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++ ++ ++ true ++ ++ ++ ++ ++ _DEBUG;%(PreprocessorDefinitions) ++ true ++ true ++ Win32 ++ .\Debug/libupnp.tlb ++ ++ ++ ++ ++ Disabled ++ ..\..\upnp\inc;..\..\upnp\src\inc;..\..\..\..\msvc\include;..\..\..\..\msvc\include\upnp;..\..\upnp\src\threadutil;..\..\ixml\inc;..\..\ixml\src\inc;..\..\..\..\msvc\include;..\..\..\..\msvc\lib\win32 ++ IXML_HAVE_SCRIPTSUPPORT;DEBUG;WIN32;_WINDOWS;_USRDLL;LIBUPNP_EXPORTS;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions);_WINSOCK_DEPRECATED_NO_WARNINGS;UPNP_STATIC_LIB ++ true ++ EnableFastChecks ++ MultiThreadedDebugDLL ++ ++ ++ $(IntDir) ++ $(IntDir) ++ ++ ++ Level3 ++ true ++ ProgramDatabase ++ CompileAsC ++ $(OutDir)$(ProjectName).pdb ++ ++ ++ _DEBUG;%(PreprocessorDefinitions) ++ 0x0407 ++ ++ ++ pthreadvc2.lib;ws2_32.lib;iphlpapi.lib;ixml.lib;%(AdditionalDependencies) ++ $(OutDir)$(ProjectName).dll ++ true ++ ..\..\pthreads\include;..\..\pthreads\lib\x86;$(SolutionDir)Build\$(Platform)\$(Configuration)\lib;%(AdditionalLibraryDirectories) ++ true ++ Windows ++ false ++ ++ ++ $(TargetDir)$(TargetName).lib ++ MachineX86 ++ ++ ++ true ++ $(OutDir)libupnp.bsc ++ ++ ++ $(IntDir)$(MSBuildProjectName).log ++ ++ ++ ++ ++ ++ mkdir $(SolutionDir)..\..\..\..\msvc\include ++mkdir $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\src\inc\membuffer.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\src\inc\miniserver.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++ ++ ++ ixml.lib;pthreads.lib;%(AdditionalDependencies) ++ ++ ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\;%(AdditionalLibraryDirectories) ++ ++ ++ ++ ++ _DEBUG;%(PreprocessorDefinitions) ++ true ++ true ++ X64 ++ .\Debug/libupnp.tlb ++ ++ ++ ++ ++ Disabled ++ ..\..\upnp\inc;..\..\upnp\src\inc;..\..\..\..\msvc\include;..\..\..\..\msvc\include\upnp;..\..\upnp\src\threadutil;..\..\ixml\inc;..\..\ixml\src\inc;..\..\..\..\msvc\include;..\..\..\..\msvc\lib\x64 ++ DEBUG;WIN32;_USRDLL;LIBUPNP_EXPORTS;UPNP_USE_MSVCPP;_CRT_NONSTDC_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL;_SCL_SECURE_NO_WARNINGS;_SCL_SECURE_NO_DEPRECATE;_AFX_SECURE_NO_WARNINGS;_AFX_SECURE_NO_DEPRECATE;_SECURE_ATL;_ATL_NO_COM_SUPPORT;_ATL_SECURE_NO_WARNINGS;_ATL_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions);_WINSOCK_DEPRECATED_NO_WARNINGS;UPNP_STATIC_LIB ++ true ++ EnableFastChecks ++ MultiThreadedDebugDLL ++ ++ ++ $(IntDir) ++ $(IntDir) ++ $(OutDir)$(ProjectName).pdb ++ ++ ++ Level3 ++ true ++ ProgramDatabase ++ CompileAsC ++ ++ ++ _DEBUG;%(PreprocessorDefinitions) ++ 0x0407 ++ ++ ++ pthreadvc2.lib;ws2_32.lib;iphlpapi.lib;ixml.lib;%(AdditionalDependencies) ++ $(OutDir)$(ProjectName).dll ++ true ++ ..\..\pthreads\;..\..\pthreads\lib;$(OutDir)..\lib\ixml;%(AdditionalLibraryDirectories) ++ true ++ $(OutDir)$(ProjectName).pdb ++ Windows ++ false ++ ++ ++ $(TargetDir)$(TargetName).lib ++ MachineX64 ++ ++ ++ true ++ $(OutDir)libupnp.bsc ++ ++ ++ ixml.lib;pthreads.lib;%(AdditionalDependencies) ++ ++ ++ $(SolutionDir)..\..\..\..\msvc\lib\$(Platform)\;%(AdditionalLibraryDirectories) ++ ++ ++ mkdir $(SolutionDir)..\..\..\..\msvc\include ++mkdir $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\inc\*.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\src\inc\membuffer.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++xcopy /S /Y $(ProjectDir)..\..\upnp\src\inc\miniserver.h $(SolutionDir)..\..\..\..\msvc\include\upnp ++ ++ ++ ++ ++ {9c2c266d-35a3-465f-a297-0e21d54e5c89} ++ false ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +\ No newline at end of file +--- /dev/null ++++ b/build/vs2017/libupnp.vcxproj.filters +@@ -0,0 +1,407 @@ ++ ++ ++ ++ ++ {47d40159-145c-4ff3-98f5-9b2c96c80092} ++ cpp;c;cxx;rc;def;r;odl;idl;hpj;bat ++ ++ ++ {2a8d348a-a429-4b41-9934-050df3866f50} ++ h;hpp;hxx;hm;inl ++ ++ ++ {d7e1f6fa-d5eb-46a7-be21-a1de81e1c83b} ++ ++ ++ {046adcbc-ea36-4b2c-947d-bc49c65dd159} ++ ++ ++ {63eeed5a-fb55-4c74-87cc-f01b3e65396c} ++ ++ ++ {ac2412f9-1484-40e5-94e2-601cf585f0ca} ++ ++ ++ {6918bb2d-ec22-4237-9d43-07f3a469b921} ++ ++ ++ {13db6453-fe8d-4869-b839-7fceeca36c68} ++ ++ ++ {ff538420-d04b-4693-b7e5-1231dc3578c3} ++ ++ ++ {a518a094-dc55-4904-b6f6-54432f881279} ++ ++ ++ {ae11371f-7271-415f-b938-bded10b1d69b} ++ ++ ++ {4268e9a2-9786-4559-ab8e-a116194e2431} ++ ++ ++ {a1f7e40e-0309-468e-b7d5-b06cdb2db669} ++ ++ ++ {9db59f74-7e5a-4d8a-bea6-dd7ebc6a1bf3} ++ ++ ++ {da4ae3ad-7d2f-4e3e-962f-5543000541f2} ++ ++ ++ {62a4fe61-d4d7-4205-99a2-f4a9f5b00914} ++ ++ ++ {eb789bde-b652-42d1-a691-2ee7b5f39f05} ++ ++ ++ {43451589-0f69-481c-a63c-0f89f30e61c0} ++ ++ ++ ++ ++ sources\ssdp ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ inc ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ inc ++ ++ ++ headers ++ ++ ++ headers ++ ++ ++ ++ ++ sources ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\api ++ ++ ++ sources\gena ++ ++ ++ sources\gena ++ ++ ++ sources\gena ++ ++ ++ sources\genlib\client_table ++ ++ ++ sources\genlib\client_table ++ ++ ++ sources\genlib\miniserver ++ ++ ++ sources\genlib\net ++ ++ ++ sources\genlib\net\http ++ ++ ++ sources\genlib\net\http ++ ++ ++ sources\genlib\net\http ++ ++ ++ sources\genlib\net\http ++ ++ ++ sources\genlib\net\http ++ ++ ++ sources\genlib\net\uri ++ ++ ++ sources\soap ++ ++ ++ sources\soap ++ ++ ++ sources\soap ++ ++ ++ sources\ssdp ++ ++ ++ sources\ssdp ++ ++ ++ sources\ssdp ++ ++ ++ sources\ssdp ++ ++ ++ sources\threadutil ++ ++ ++ sources\threadutil ++ ++ ++ sources\threadutil ++ ++ ++ sources\threadutil ++ ++ ++ sources\urlconfig ++ ++ ++ sources\uuid ++ ++ ++ sources\uuid ++ ++ ++ sources\uuid ++ ++ ++ sources\genlib\service_table ++ ++ ++ sources\genlib\util ++ ++ ++ sources\genlib\util ++ ++ ++ sources\genlib\util ++ ++ ++ sources\genlib\util ++ ++ ++ +\ No newline at end of file +--- /dev/null ++++ b/upnp/inc/FreeList.h +@@ -0,0 +1,131 @@ ++/******************************************************************************* ++ * ++ * Copyright (c) 2000-2003 Intel Corporation ++ * 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. ++ * * Neither name of Intel Corporation nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ ******************************************************************************/ ++ ++#ifndef FREE_LIST_H ++#define FREE_LIST_H ++ ++/*! ++ * \file ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "ithread.h" ++ ++#include ++ ++/*! ++ * Free list node. points to next free item. ++ * Memory for node is borrowed from allocated items. ++ * \internal ++ */ ++typedef struct FREELISTNODE ++{ ++ struct FREELISTNODE *next; ++} FreeListNode; ++ ++/*! ++ * Stores head and size of free list, as well as mutex for protection. ++ * \internal ++ */ ++typedef struct FREELIST ++{ ++ FreeListNode *head; ++ size_t element_size; ++ int maxFreeListLength; ++ int freeListLength; ++} FreeList; ++ ++/*! ++ * \brief Initializes Free List. ++ * ++ * Must be called first and only once for FreeList. ++ * ++ * \return: ++ * \li \c 0 on success. ++ * \li \c EINVAL on failure. ++ */ ++int FreeListInit( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ FreeList *free_list, ++ /*! Size of elements to store in free list. */ ++ size_t elementSize, ++ /*! Max size that the free list can grow to before returning ++ * memory to O.S. */ ++ int maxFreeListLength); ++ ++/*! ++ * \brief Allocates chunk of set size. ++ * ++ * If a free item is available in the list, returnes the stored item, ++ * otherwise calls the O.S. to allocate memory. ++ * ++ * \return Non NULL on success. NULL on failure. ++ */ ++void *FreeListAlloc( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ FreeList *free_list); ++ ++/*! ++ * \brief Returns an item to the Free List. ++ * ++ * If the free list is smaller than the max size then adds the item to the ++ * free list, otherwise returns the item to the O.S. ++ * ++ * \return: ++ * \li \c 0 on success. ++ * \li \c EINVAL on failure. ++ */ ++int FreeListFree( ++ /*! Must be valid, non null, pointer to a free list. */ ++ FreeList *free_list, ++ /*! Must be a pointer allocated by FreeListAlloc. */ ++ void *element); ++ ++/*! ++ * \brief Releases the resources stored with the free list. ++ * ++ * \return: ++ * \li \c 0 on success. ++ * \li \c EINVAL on failure. ++ */ ++int FreeListDestroy( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ FreeList *free_list); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* FREE_LIST_H */ ++ +--- /dev/null ++++ b/upnp/inc/LinkedList.h +@@ -0,0 +1,289 @@ ++/******************************************************************************* ++ * ++ * Copyright (c) 2000-2003 Intel Corporation ++ * 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. ++ * * Neither name of Intel Corporation nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ ******************************************************************************/ ++ ++#ifndef LINKED_LIST_H ++#define LINKED_LIST_H ++ ++/*! ++ * \file ++ */ ++ ++#include "FreeList.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define EOUTOFMEM (-7 & 1<<29) ++ ++#define FREELISTSIZE 100 ++#define LIST_SUCCESS 1 ++#define LIST_FAIL 0 ++ ++/*! Function for freeing list items. */ ++typedef void (*free_function)(void *arg); ++ ++/*! Function for comparing list items. Returns 1 if itemA==itemB */ ++typedef int (*cmp_routine)(void *itemA,void *itemB); ++ ++/*! Linked list node. Stores generic item and pointers to next and prev. ++ * \internal ++ */ ++typedef struct LISTNODE ++{ ++ struct LISTNODE *prev; ++ struct LISTNODE *next; ++ void *item; ++} ListNode; ++ ++/*! ++ * Linked list (no protection). ++ * ++ * Because this is for internal use, parameters are NOT checked for validity. ++ * The first item of the list is stored at node: head->next ++ * The last item of the list is stored at node: tail->prev ++ * If head->next=tail, then list is empty. ++ * To iterate through the list: ++ * ++ * LinkedList g; ++ * ListNode *temp = NULL; ++ * for (temp = ListHead(g);temp!=NULL;temp = ListNext(g,temp)) { ++ * } ++ * ++ * \internal ++ */ ++typedef struct LINKEDLIST ++{ ++ /*! head, first item is stored at: head->next */ ++ ListNode head; ++ /*! tail, last item is stored at: tail->prev */ ++ ListNode tail; ++ /*! size of list */ ++ long size; ++ /*! free list to use */ ++ FreeList freeNodeList; ++ /*! free function to use */ ++ free_function free_func; ++ /*! compare function to use */ ++ cmp_routine cmp_func; ++} LinkedList; ++ ++/*! ++ * \brief Initializes LinkedList. Must be called first and only once for List. ++ * ++ * \return ++ * \li \c 0 on success. ++ * \li \c EOUTOFMEM on failure. ++ */ ++int ListInit( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! Function used to compare items. (May be NULL). */ ++ cmp_routine cmp_func, ++ /*! Function used to free items. (May be NULL). */ ++ free_function free_func); ++ ++/*! ++ * \brief Adds a node to the head of the list. Node gets immediately after ++ * list head. ++ * ++ * Precondition: ++ * The list has been initialized. ++ * ++ * \return The pointer to the ListNode on success, NULL on failure. ++ */ ++ListNode *ListAddHead( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! Item to be added. */ ++ void *item); ++ ++/*! ++ * \brief Adds a node to the tail of the list. Node gets added immediately ++ * before list.tail. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The pointer to the ListNode on success, NULL on failure. ++ */ ++ListNode *ListAddTail( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! Item to be added. */ ++ void *item); ++ ++/*! ++ * \brief Adds a node after the specified node. Node gets added immediately ++ * after bnode. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The pointer to the ListNode on success, NULL on failure. ++ */ ++ListNode *ListAddAfter( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! Item to be added. */ ++ void *item, ++ /*! Node to add after. */ ++ ListNode *bnode); ++ ++/*! ++ * \brief Adds a node before the specified node. Node gets added immediately ++ * before anode. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The pointer to the ListNode on success, NULL on failure. ++ */ ++ListNode *ListAddBefore( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! Item to be added. */ ++ void *item, ++ /*! Node to add in front of. */ ++ ListNode *anode); ++ ++/*! ++ * \brief Removes a node from the list. The memory for the node is freed. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The pointer to the item stored in the node or NULL if the item ++ * is freed. ++ */ ++void *ListDelNode( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! Node to delete. */ ++ ListNode *dnode, ++ /*! if !0 then item is freed using free function. If 0 (or free ++ * function is NULL) then item is not freed. */ ++ int freeItem); ++ ++/*! ++ * \brief Removes all memory associated with list nodes. Does not free ++ * LinkedList *list. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return 0 on success, EINVAL on failure. ++ */ ++int ListDestroy( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! if !0 then item is freed using free function. If 0 (or free ++ * function is NULL) then item is not freed. */ ++ int freeItem); ++ ++/*! ++ * \brief Returns the head of the list. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The head of the list. NULL if list is empty. ++ */ ++ListNode *ListHead( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list); ++ ++/*! ++ * \brief Returns the tail of the list. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The tail of the list. NULL if list is empty. ++ */ ++ListNode *ListTail( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list); ++ ++/*! ++ * \brief Returns the next item in the list. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The next item in the list. NULL if there are no more items in list. ++ */ ++ListNode *ListNext( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! Node from the list. */ ++ ListNode *node); ++ ++/*! ++ * \brief Returns the previous item in the list. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The previous item in the list. NULL if there are no more items in list. ++ */ ++ListNode *ListPrev( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! Node from the list. */ ++ ListNode *node); ++ ++/*! ++ * \brief Finds the specified item in the list. ++ * ++ * Uses the compare function specified in ListInit. If compare function ++ * is NULL then compares items as pointers. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The node containing the item. NULL if no node contains the item. ++ */ ++ListNode* ListFind( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList *list, ++ /*! The node to start from, NULL if to start from beginning. */ ++ ListNode *start, ++ /*! The item to search for. */ ++ void *item); ++ ++/*! ++ * \brief Returns the size of the list. ++ * ++ * Precondition: The list has been initialized. ++ * ++ * \return The number of items in the list. ++ */ ++long ListSize( ++ /*! Must be valid, non null, pointer to a linked list. */ ++ LinkedList* list); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* LINKED_LIST_H */ ++ +--- /dev/null ++++ b/upnp/inc/ThreadPool.h +@@ -0,0 +1,538 @@ ++/******************************************************************************* ++ * ++ * Copyright (c) 2000-2003 Intel Corporation ++ * All rights reserved. ++ * Copyright (c) 2012 France Telecom 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. ++ * * Neither name of Intel Corporation nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ ******************************************************************************/ ++ ++#ifndef THREADPOOL_H ++#define THREADPOOL_H ++ ++/*! ++ * \file ++ */ ++ ++#include "FreeList.h" ++#include "ithread.h" ++#include "LinkedList.h" ++#include "UpnpInet.h" ++#include "UpnpGlobal.h" /* for UPNP_INLINE, EXPORT_SPEC */ ++ ++#include ++ ++#ifdef _WIN32 ++#ifndef _TIMEZONE_DEFINED ++ #include ++ struct timezone ++ { ++ int tz_minuteswest; /* minutes W of Greenwich */ ++ int tz_dsttime; /* type of dst correction */ ++ }; ++ int gettimeofday(struct timeval *tv, struct timezone *tz); ++#endif ++#else /* _WIN32 */ ++ #include ++ #include /* for gettimeofday() */ ++ #if defined(__OSX__) || defined(__APPLE__) || defined(__NetBSD__) ++ #include /* for setpriority() */ ++ #endif ++#endif ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/*! Size of job free list */ ++#define JOBFREELISTSIZE 100 ++ ++#define INFINITE_THREADS -1 ++ ++#define EMAXTHREADS (-8 & 1<<29) ++ ++/*! Invalid Policy */ ++#define INVALID_POLICY (-9 & 1<<29) ++ ++/*! Invalid JOB Id */ ++#define INVALID_JOB_ID (-2 & 1<<29) ++ ++typedef enum duration { ++ SHORT_TERM, ++ PERSISTENT ++} Duration; ++ ++typedef enum priority { ++ LOW_PRIORITY, ++ MED_PRIORITY, ++ HIGH_PRIORITY ++} ThreadPriority; ++ ++/*! default priority used by TPJobInit */ ++#define DEFAULT_PRIORITY MED_PRIORITY ++ ++/*! default minimum used by TPAttrInit */ ++#define DEFAULT_MIN_THREADS 1 ++ ++/*! default max used by TPAttrInit */ ++#define DEFAULT_MAX_THREADS 10 ++ ++/*! default stack size used by TPAttrInit */ ++#define DEFAULT_STACK_SIZE 0u ++ ++/*! default jobs per thread used by TPAttrInit */ ++#define DEFAULT_JOBS_PER_THREAD 10 ++ ++/*! default starvation time used by TPAttrInit */ ++#define DEFAULT_STARVATION_TIME 500 ++ ++/*! default idle time used by TPAttrInit */ ++#define DEFAULT_IDLE_TIME 10 * 1000 ++ ++/*! default free routine used TPJobInit */ ++#define DEFAULT_FREE_ROUTINE NULL ++ ++/*! default max jobs used TPAttrInit */ ++#define DEFAULT_MAX_JOBS_TOTAL 100 ++ ++/*! ++ * \brief Statistics. ++ * ++ * Always include stats because code change is minimal. ++ */ ++#define STATS 1 ++ ++#ifdef _DEBUG ++ #define DEBUG 1 ++#endif ++ ++typedef int PolicyType; ++ ++#define DEFAULT_POLICY SCHED_OTHER ++ ++/*! Function for freeing a thread argument. */ ++typedef void (*free_routine)(void *arg); ++ ++ ++/*! Attributes for thread pool. Used to set and change parameters of thread ++ * pool. */ ++typedef struct THREADPOOLATTR ++{ ++ /*! ThreadPool will always maintain at least this many threads. */ ++ int minThreads; ++ /*! ThreadPool will never have more than this number of threads. */ ++ int maxThreads; ++ /*! This is the minimum stack size allocated for each thread. */ ++ size_t stackSize; ++ /*! This is the maximum time a thread will ++ * remain idle before dying (in milliseconds). */ ++ int maxIdleTime; ++ /*! Jobs per thread to maintain. */ ++ int jobsPerThread; ++ /*! Maximum number of jobs that can be queued totally. */ ++ int maxJobsTotal; ++ /*! the time a low priority or med priority job waits before getting ++ * bumped up a priority (in milliseconds). */ ++ int starvationTime; ++ /*! scheduling policy to use. */ ++ PolicyType schedPolicy; ++} ThreadPoolAttr; ++ ++/*! Internal ThreadPool Job. */ ++typedef struct THREADPOOLJOB ++{ ++ start_routine func; ++ void *arg; ++ free_routine free_func; ++ struct timeval requestTime; ++ ThreadPriority priority; ++ int jobId; ++} ThreadPoolJob; ++ ++/*! Structure to hold statistics. */ ++typedef struct TPOOLSTATS ++{ ++ double totalTimeHQ; ++ int totalJobsHQ; ++ double avgWaitHQ; ++ double totalTimeMQ; ++ int totalJobsMQ; ++ double avgWaitMQ; ++ double totalTimeLQ; ++ int totalJobsLQ; ++ double avgWaitLQ; ++ double totalWorkTime; ++ double totalIdleTime; ++ int workerThreads; ++ int idleThreads; ++ int persistentThreads; ++ int totalThreads; ++ int maxThreads; ++ int currentJobsHQ; ++ int currentJobsLQ; ++ int currentJobsMQ; ++} ThreadPoolStats; ++ ++/*! ++ * \brief A thread pool similar to the thread pool in the UPnP SDK. ++ * ++ * Allows jobs to be scheduled for running by threads in a ++ * thread pool. The thread pool is initialized with a ++ * minimum and maximum thread number as well as a max idle time ++ * and a jobs per thread ratio. If a worker thread waits the whole ++ * max idle time without receiving a job and the thread pool ++ * currently has more threads running than the minimum ++ * then the worker thread will exit. If when ++ * scheduling a job the current job to thread ratio ++ * becomes greater than the set ratio and the thread pool currently has ++ * less than the maximum threads then a new thread will ++ * be created. ++ */ ++typedef struct THREADPOOL ++{ ++ /*! Mutex to protect job qs. */ ++ ithread_mutex_t mutex; ++ /*! Condition variable to signal Q. */ ++ ithread_cond_t condition; ++ /*! Condition variable for start and stop. */ ++ ithread_cond_t start_and_shutdown; ++ /*! ids for jobs */ ++ int lastJobId; ++ /*! whether or not we are shutting down */ ++ int shutdown; ++ /*! total number of threads */ ++ int totalThreads; ++ /*! flag that's set when waiting for a new worker thread to start */ ++ int pendingWorkerThreadStart; ++ /*! number of threads that are currently executing jobs */ ++ int busyThreads; ++ /*! number of persistent threads */ ++ int persistentThreads; ++ /*! free list of jobs */ ++ FreeList jobFreeList; ++ /*! low priority job Q */ ++ LinkedList lowJobQ; ++ /*! med priority job Q */ ++ LinkedList medJobQ; ++ /*! high priority job Q */ ++ LinkedList highJobQ; ++ /*! persistent job */ ++ ThreadPoolJob *persistentJob; ++ /*! thread pool attributes */ ++ ThreadPoolAttr attr; ++ /*! statistics */ ++ ThreadPoolStats stats; ++} ThreadPool; ++ ++/*! ++ * \brief Initializes and starts ThreadPool. Must be called first and ++ * only once for ThreadPool. ++ * ++ * \return ++ * \li \c 0 on success. ++ * \li \c EAGAIN if not enough system resources to create minimum threads. ++ * \li \c INVALID_POLICY if schedPolicy can't be set. ++ * \li \c EMAXTHREADS if minimum threads is greater than maximum threads. ++ */ ++int ThreadPoolInit( ++ /*! Must be valid, non null, pointer to ThreadPool. */ ++ ThreadPool *tp, ++ /*! Can be null. if not null then attr contains the following fields: ++ * \li \c minWorkerThreads - minimum number of worker threads thread ++ * pool will never have less than this number of threads. ++ * \li \c maxWorkerThreads - maximum number of worker threads thread ++ * pool will never have more than this number of threads. ++ * \li \c maxIdleTime - maximum time that a worker thread will spend ++ * idle. If a worker is idle longer than this time and there are more ++ * than the min number of workers running, then the worker thread ++ * exits. ++ * \li \c jobsPerThread - ratio of jobs to thread to try and maintain ++ * if a job is scheduled and the number of jobs per thread is greater ++ * than this number,and if less than the maximum number of workers are ++ * running then a new thread is started to help out with efficiency. ++ * \li \c schedPolicy - scheduling policy to try and set (OS dependent). ++ */ ++ ThreadPoolAttr *attr); ++ ++/*! ++ * \brief Adds a persistent job to the thread pool. ++ * ++ * Job will be run as soon as possible. Call will block until job is scheduled. ++ * ++ * \return ++ * \li \c 0 on success. ++ * \li \c EOUTOFMEM not enough memory to add job. ++ * \li \c EMAXTHREADS not enough threads to add persistent job. ++ */ ++int ThreadPoolAddPersistent( ++ /*! Valid thread pool pointer. */ ++ ThreadPool*tp, ++ /*! Valid thread pool job. */ ++ ThreadPoolJob *job, ++ /*! . */ ++ int *jobId); ++ ++/*! ++ * \brief Gets the current set of attributes associated with the thread pool. ++ * ++ * \return ++ * \li \c 0 on success, nonzero on failure. ++ */ ++int ThreadPoolGetAttr( ++ /*! valid thread pool pointer. */ ++ ThreadPool *tp, ++ /*! non null pointer to store attributes. */ ++ ThreadPoolAttr *out); ++ ++/*! ++ * \brief Sets the attributes for the thread pool. ++ * Only affects future calculations. ++ * ++ * \return ++ * \li \c 0 on success, nonzero on failure. ++ * \li \c INVALID_POLICY if policy can not be set. ++ */ ++int ThreadPoolSetAttr( ++ /*! valid thread pool pointer. */ ++ ThreadPool *tp, ++ /*! pointer to attributes, null sets attributes to default. */ ++ ThreadPoolAttr *attr); ++ ++/*! ++ * \brief Adds a job to the thread pool. Job will be run as soon as possible. ++ * ++ * \return ++ * \li \c 0 on success, nonzero on failure. ++ * \li \c EOUTOFMEM if not enough memory to add job. ++ */ ++int ThreadPoolAdd( ++ /*! valid thread pool pointer. */ ++ ThreadPool*tp, ++ /*! . */ ++ ThreadPoolJob *job, ++ /*! id of job. */ ++ int *jobId); ++ ++/*! ++ * \brief Removes a job from the thread pool. Can only remove jobs which ++ * are not currently running. ++ * ++ * \return ++ * \li \c 0 on success, nonzero on failure. ++ * \li \c INVALID_JOB_ID if job not found. ++ */ ++int ThreadPoolRemove( ++ /*! valid thread pool pointer. */ ++ ThreadPool *tp, ++ /*! id of job. */ ++ int jobId, ++ /*! space for removed job. */ ++ ThreadPoolJob *out); ++ ++/*! ++ * \brief Shuts the thread pool down. Waits for all threads to finish. ++ * May block indefinitely if jobs do not exit. ++ * ++ * \return 0 on success, nonzero on failure ++ */ ++int ThreadPoolShutdown( ++ /*! must be valid tp. */ ++ ThreadPool *tp); ++ ++/*! ++ * \brief Initializes thread pool job. Sets the priority to default defined ++ * in ThreadPool.h. Sets the free_routine to default defined in ThreadPool.h. ++ * ++ * \return Always returns 0. ++ */ ++int TPJobInit( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolJob *job, ++ /*! function to run, must be valid. */ ++ start_routine func, ++ /*! argument to pass to function. */ ++ void *arg); ++ ++/*! ++ * \brief Sets the max threads for the thread pool attributes. ++ * ++ * \return Always returns 0. ++ */ ++int TPJobSetPriority( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolJob *job, ++ /*! value to set. */ ++ ThreadPriority priority); ++ ++/*! ++ * \brief Sets the max threads for the thread pool attributes. ++ * ++ * \return Always returns 0. ++ */ ++int TPJobSetFreeFunction( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolJob *job, ++ /*! value to set. */ ++ free_routine func); ++ ++/*! ++ * \brief Initializes thread pool attributes. Sets values to defaults defined ++ * in ThreadPool.h. ++ * ++ * \return Always returns 0. ++ */ ++int TPAttrInit( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolAttr *attr); ++ ++/*! ++ * \brief Sets the max threads for the thread pool attributes. ++ * ++ * \return Always returns 0. ++ */ ++int TPAttrSetMaxThreads( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolAttr *attr, ++ /*! value to set. */ ++ int maxThreads); ++ ++/*! ++ * \brief Sets the min threads for the thread pool attributes. ++ * ++ * \return Always returns 0. ++ */ ++int TPAttrSetMinThreads( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolAttr *attr, ++ /*! value to set. */ ++ int minThreads); ++ ++/*! ++ * \brief Sets the stack size for the thread pool attributes. ++ * ++ * \return Always returns 0. ++ */ ++int TPAttrSetStackSize( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolAttr *attr, ++ /*! value to set. */ ++ size_t stackSize); ++ ++/*! ++ * \brief Sets the idle time for the thread pool attributes. ++ * ++ * \return Always returns 0. ++ */ ++int TPAttrSetIdleTime( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolAttr *attr, ++ /*! . */ ++ int idleTime); ++ ++/*! ++ * \brief Sets the jobs per thread ratio ++ * ++ * \return Always returns 0. ++ */ ++int TPAttrSetJobsPerThread( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolAttr *attr, ++ /*! number of jobs per thread to maintain. */ ++ int jobsPerThread); ++ ++/*! ++ * \brief Sets the starvation time for the thread pool attributes. ++ * ++ * \return Always returns 0. ++ */ ++int TPAttrSetStarvationTime( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolAttr *attr, ++ /*! milliseconds. */ ++ int starvationTime); ++ ++/*! ++ * \brief Sets the scheduling policy for the thread pool attributes. ++ * ++ * \return Always returns 0. ++ */ ++int TPAttrSetSchedPolicy( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolAttr *attr, ++ /*! must be a valid policy type. */ ++ PolicyType schedPolicy); ++ ++/*! ++ * \brief Sets the maximum number jobs that can be qeued totally. ++ * ++ * \return Always returns 0. ++ */ ++int TPAttrSetMaxJobsTotal( ++ /*! must be valid thread pool attributes. */ ++ ThreadPoolAttr *attr, ++ /*! maximum number of jobs. */ ++ int maxJobsTotal); ++ ++/*! ++ * \brief Returns various statistics about the thread pool. ++ * ++ * Only valid if STATS has been defined. ++ * ++ * \return Always returns 0. ++ */ ++#ifdef STATS ++ EXPORT_SPEC int ThreadPoolGetStats( ++ /*! Valid initialized threadpool. */ ++ ThreadPool *tp, ++ /*! Valid stats, out parameter. */ ++ ThreadPoolStats *stats); ++#else ++ static UPNP_INLINE int ThreadPoolGetStats( ++ /*! Valid initialized threadpool. */ ++ ThreadPool *tp, ++ /*! Valid stats, out parameter. */ ++ ThreadPoolStats *stats) {} ++#endif ++ ++/*! ++ * \brief ++ */ ++#ifdef STATS ++ EXPORT_SPEC void ThreadPoolPrintStats( ++ /*! . */ ++ ThreadPoolStats *stats); ++#else ++ static UPNP_INLINE void ThreadPoolPrintStats( ++ /*! . */ ++ ThreadPoolStats *stats) {} ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* THREADPOOL_H */ ++ +--- /dev/null ++++ b/upnp/inc/TimerThread.h +@@ -0,0 +1,161 @@ ++/******************************************************************************* ++ * ++ * Copyright (c) 2000-2003 Intel Corporation ++ * 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. ++ * * Neither name of Intel Corporation nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ ******************************************************************************/ ++ ++#ifndef TIMERTHREAD_H ++#define TIMERTHREAD_H ++ ++/*! ++ * \file ++ */ ++ ++#include "FreeList.h" ++#include "ithread.h" ++#include "LinkedList.h" ++#include "ThreadPool.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define INVALID_EVENT_ID (-10 & 1<<29) ++ ++/*! Timeout Types. */ ++typedef enum timeoutType { ++ /*! seconds from Jan 1, 1970. */ ++ ABS_SEC, ++ /*! seconds from current time. */ ++ REL_SEC ++} TimeoutType; ++ ++/*! ++ * A timer thread similar to the one in the Upnp SDK that allows ++ * the scheduling of a job to run at a specified time in the future. ++ * ++ * Because the timer thread uses the thread pool there is no ++ * gurantee of timing, only approximate timing. ++ * ++ * Uses ThreadPool, Mutex, Condition, Thread. ++ */ ++typedef struct TIMERTHREAD ++{ ++ ithread_mutex_t mutex; ++ ithread_cond_t condition; ++ int lastEventId; ++ LinkedList eventQ; ++ int shutdown; ++ FreeList freeEvents; ++ ThreadPool *tp; ++} TimerThread; ++ ++/*! ++ * Struct to contain information for a timer event. ++ * ++ * Internal to the TimerThread. ++ */ ++typedef struct TIMEREVENT ++{ ++ ThreadPoolJob job; ++ /*! [in] Absolute time for event in seconds since Jan 1, 1970. */ ++ time_t eventTime; ++ /*! [in] Long term or short term job. */ ++ Duration persistent; ++ int id; ++} TimerEvent; ++ ++/*! ++ * \brief Initializes and starts timer thread. ++ * ++ * \return 0 on success, nonzero on failure. Returns error from ++ * ThreadPoolAddPersistent on failure. ++ */ ++int TimerThreadInit( ++ /*! [in] Valid timer thread pointer. */ ++ TimerThread *timer, ++ /*! [in] Valid thread pool to use. Must be started. Must be valid for ++ * lifetime of timer. Timer must be shutdown BEFORE thread pool. */ ++ ThreadPool *tp); ++ ++/*! ++ * \brief Schedules an event to run at a specified time. ++ * ++ * \return 0 on success, nonzero on failure, EOUTOFMEM if not enough memory ++ * to schedule job. ++ */ ++int TimerThreadSchedule( ++ /*! [in] Valid timer thread pointer. */ ++ TimerThread* timer, ++ /*! [in] time of event. Either in absolute seconds, or relative ++ * seconds in the future. */ ++ time_t time, ++ /*! [in] either ABS_SEC, or REL_SEC. If REL_SEC, then the event ++ * will be scheduled at the current time + REL_SEC. */ ++ TimeoutType type, ++ /*! [in] Valid Thread pool job with following fields. */ ++ ThreadPoolJob *job, ++ /*! [in] . */ ++ Duration duration, ++ /*! [in] Id of timer event. (out, can be null). */ ++ int *id); ++ ++/*! ++ * \brief Removes an event from the timer Q. ++ * ++ * Events can only be removed before they have been placed in the thread pool. ++ * ++ * \return 0 on success, INVALID_EVENT_ID on failure. ++ */ ++int TimerThreadRemove( ++ /*! [in] Valid timer thread pointer. */ ++ TimerThread *timer, ++ /*! [in] Id of event to remove. */ ++ int id, ++ /*! [in] Space for thread pool job. */ ++ ThreadPoolJob *out); ++ ++/*! ++ * \brief Shutdown the timer thread. ++ * ++ * Events scheduled in the future will NOT be run. ++ * ++ * Timer thread should be shutdown BEFORE it's associated thread pool. ++ * ++ * \return 0 if succesfull, nonzero otherwise. Always returns 0. ++ */ ++int TimerThreadShutdown( ++ /*! [in] Valid timer thread pointer. */ ++ TimerThread *timer); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* TIMER_THREAD_H */ ++ +--- a/upnp/inc/UpnpGlobal.h ++++ b/upnp/inc/UpnpGlobal.h +@@ -1,5 +1,6 @@ + #ifndef UPNPGLOBAL_H + #define UPNPGLOBAL_H ++#include + + /*! + * \file +@@ -42,7 +43,9 @@ + /* define some things the M$ VC++ doesn't know */ + #define UPNP_INLINE _inline + typedef __int64 int64_t; ++#if !defined(PRId64) + #define PRId64 "I64d" ++#endif + #define PRIzd "ld" + #define PRIzu "lu" + #define PRIzx "lx" +--- a/upnp/inc/upnp.h ++++ b/upnp/inc/upnp.h +@@ -61,6 +61,19 @@ + /* Other systems ??? */ + #endif + ++# if defined(__MINGW32__) ++# if !defined(_OFF_T_) ++typedef long long _off_t; ++typedef _off_t off_t; ++# define _OFF_T_ ++# else ++# ifdef off_t ++# undef off_t ++# endif ++# define off_t long long ++# endif ++# endif ++ + #ifdef UPNP_ENABLE_OPEN_SSL + #include + #endif +--- /dev/null ++++ b/upnp/inc/upnpconfig.h +@@ -0,0 +1,132 @@ +/******************************************************************************* + * + * Copyright (c) 2006 Rémi Turboult -+ * All rights reserved. ++ * All rights reserved. + * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions are met: ++ * 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. -+ * * Neither name of Intel Corporation nor the names of its contributors -+ * may be used to endorse or promote products derived from this software ++ * * 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. ++ * * Neither name of Intel Corporation nor the names of its contributors ++ * may be used to endorse or promote products derived from this software + * without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL 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 ++ * ++ * 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 INTEL 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 ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ******************************************************************************/ + +#ifndef UPNP_CONFIG_H -+#define UPNP_CONFIG_H ++#define UPNP_CONFIG_H + + +/*************************************************************************** -+ * Library version -+ ***************************************************************************/ ++ * Library version ++ ***************************************************************************/ + +/** The library version (string) e.g. "1.3.0" */ -+#define UPNP_VERSION_STRING "1.6.19" ++#define UPNP_VERSION_STRING "1.8.5" + +/** Major version of the library */ +#define UPNP_VERSION_MAJOR 1 + +/** Minor version of the library */ -+#define UPNP_VERSION_MINOR 6 ++#define UPNP_VERSION_MINOR 8 + +/** Patch version of the library */ -+#define UPNP_VERSION_PATCH 19 ++#define UPNP_VERSION_PATCH 5 + +/** The library version (numeric) e.g. 10300 means version 1.3.0 */ +#define UPNP_VERSION \ + ((UPNP_VERSION_MAJOR * 100 + UPNP_VERSION_MINOR) * 100 + UPNP_VERSION_PATCH) + -+ -+ +/*************************************************************************** + * Large file support + ***************************************************************************/ + -+/** File Offset size */ -+#define _FILE_OFFSET_BITS 64 -+ -+/** Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ -+/* #undef _LARGEFILE_SOURCE */ -+ -+/** Large files support */ -+#define _LARGE_FILE_SOURCE /**/ ++/* #undef UPNP_LARGEFILE_SENSITIVE */ + +/*************************************************************************** + * Library optional features + ***************************************************************************/ + +/* -+ * The following defines can be tested in order to know which ++ * The following defines can be tested in order to know which + * optional features have been included in the installed library. + */ + + -+/** Defined to 1 if the library has been compiled with DEBUG enabled ++/** Defined to 1 if the library has been compiled with DEBUG enabled + * (i.e. configure --enable-debug) : file is available */ +/* #undef UPNP_HAVE_DEBUG */ + -+ -+/** Defined to 1 if the library has been compiled with client API enabled ++/** Defined to 1 if the library has been compiled with client API enabled + * (i.e. configure --enable-client) */ +#define UPNP_HAVE_CLIENT 1 + -+ -+/** Defined to 1 if the library has been compiled with device API enabled ++/** Defined to 1 if the library has been compiled with device API enabled + * (i.e. configure --enable-device) */ +#define UPNP_HAVE_DEVICE 1 + -+ +/** Defined to 1 if the library has been compiled with integrated web server + * (i.e. configure --enable-webserver --enable-device) */ +#define UPNP_HAVE_WEBSERVER 1 + -+ +/** Defined to 1 if the library has been compiled with the SSDP part enabled + * (i.e. configure --enable-ssdp) */ +#define UPNP_HAVE_SSDP 1 + -+ +/** Defined to 1 if the library has been compiled with optional SSDP headers + * support (i.e. configure --enable-optssdp) */ +#define UPNP_HAVE_OPTSSDP 1 + -+ +/** Defined to 1 if the library has been compiled with the SOAP part enabled + * (i.e. configure --enable-soap) */ +#define UPNP_HAVE_SOAP 1 + -+ +/** Defined to 1 if the library has been compiled with the GENA part enabled + * (i.e. configure --enable-gena) */ +#define UPNP_HAVE_GENA 1 + -+ +/** Defined to 1 if the library has been compiled with helper API + * (i.e. configure --enable-tools) : file is available */ +#define UPNP_HAVE_TOOLS 1 + +/** Defined to 1 if the library has been compiled with ipv6 support + * (i.e. configure --enable-ipv6) */ -+/* #undef UPNP_ENABLE_IPV6 */ ++/*#undef UPNP_ENABLE_IPV6 */ + +/** Defined to 1 if the library has been compiled with unspecified SERVER + * header (i.e. configure --enable-unspecified_server) */ +/* #undef UPNP_ENABLE_UNSPECIFIED_SERVER */ + -+#endif /* UPNP_CONFIG_H */ ++/** Defined to 1 if the library has been compiled with OpenSSL support ++ * (i.e. configure --enable-open_ssl) */ ++/* #undef UPNP_ENABLE_OPEN_SSL */ + ++/** Defined to 1 if the library has been compiled to use blocking TCP socket calls ++ * (i.e. configure --enable_blocking_tcp_connections) */ ++#define UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS 0 ++ ++/** Defined to 1 if the library has been compiled to support filesystem writes on POST ++ * (i.e. configure --enable-postwrite) */ ++/* #undef UPNP_ENABLE_POST_WRITE */ ++ ++/** Defined to 1 if the library has been compiled bind the miniserver socket with SO_REUSEADDR ++ * (i.e. configure --enable_reuseaddr) */ ++/* #undef UPNP_MINISERVER_REUSEADDR */ ++ ++#endif /* UPNP_CONFIG_H */ +--- a/upnp/src/api/upnpapi.c ++++ b/upnp/src/api/upnpapi.c +@@ -383,13 +383,13 @@ static int UpnpInitPreamble(void) + return retVal; + } + ++#ifdef INTERNAL_WEB_SERVER + #ifdef INCLUDE_DEVICE_APIS + #if EXCLUDE_SOAP == 0 + SetSoapCallback(soap_device_callback); + #endif + #endif /* INCLUDE_DEVICE_APIS */ + +-#ifdef INTERNAL_WEB_SERVER + #if EXCLUDE_GENA == 0 + SetGenaCallback(genaCallback); + #endif +--- a/upnp/src/api/upnptools.c ++++ b/upnp/src/api/upnptools.c +@@ -57,7 +57,9 @@ + #define HEADER_LENGTH 2000 + + #ifdef _WIN32 +- #define snprintf _snprintf ++ #if _MSC_VER < 1900 ++ #define snprintf _snprintf ++ #endif + #endif + + /*! +--- a/upnp/src/genlib/miniserver/miniserver.c ++++ b/upnp/src/genlib/miniserver/miniserver.c +@@ -68,6 +68,11 @@ + /*! . */ + #define APPLICATION_LISTENING_PORT 49152 + ++#ifndef IPV6_V6ONLY ++#define IPV6_V6ONLY 27 ++#endif ++ ++ + struct mserv_request_t { + /*! Connection handle. */ + SOCKET connfd; +--- a/upnp/src/genlib/net/http/httpreadwrite.c ++++ b/upnp/src/genlib/net/http/httpreadwrite.c +@@ -59,7 +59,9 @@ + #ifdef _WIN32 + #include + #define fseeko fseek +- #define snprintf _snprintf ++ #if _MSC_VER < 1900 ++ #define snprintf _snprintf ++ #endif + #else + #include + #include +--- a/upnp/src/genlib/net/http/webserver.c ++++ b/upnp/src/genlib/net/http/webserver.c +@@ -65,7 +65,9 @@ + #include + + #ifdef _WIN32 +- #define snprintf _snprintf ++ #if _MSC_VER < 1900 ++ #define snprintf _snprintf ++ #endif + #endif + + /*! +--- a/upnp/src/genlib/net/uri/uri.c ++++ b/upnp/src/genlib/net/uri/uri.c +@@ -45,7 +45,9 @@ + #endif + #endif + #ifdef _WIN32 +- #define snprintf _snprintf ++ #if _MSC_VER < 1900 ++ #define snprintf _snprintf ++ #endif + #endif + #include + --- /dev/null -+++ b/build/inc/autoconfig.h -@@ -0,0 +1,202 @@ ++++ b/upnp/src/inc/autoconfig.h +@@ -0,0 +1,229 @@ +/* autoconfig.h. Generated from autoconfig.h.in by configure. */ +/* autoconfig.h.in. Generated from configure.ac by autoheader. */ + ++/* Define if building universal (internal helper macro) */ ++/* #undef AC_APPLE_UNIVERSAL_BUILD */ ++ +/* Define to 1 to compile debug code */ +/* #undef DEBUG */ + @@ -224,19 +2861,19 @@ +/* Define to 1 if you have the header file. */ +/* #undef HAVE_WS2TCPIP_H */ + -+/* Define to the sub-directory in which libtool stores uninstalled libraries. ++/* see upnpconfig.h */ ++#define IXML_HAVE_SCRIPTSUPPORT 1 ++ ++/* whether the system defaults to 32bit off_t but can do 64bit when requested + */ ++/* #undef LARGEFILE_SENSITIVE */ ++ ++/* Define to the sub-directory where libtool stores uninstalled libraries. */ +#define LT_OBJDIR ".libs/" + +/* Define to 1 to prevent compilation of assert() */ +#define NDEBUG 1 + -+/* Define to 1 to prevent some debug code */ -+#define NO_DEBUG 1 -+ -+/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -+/* #undef NO_MINUS_C_MINUS_O */ -+ +/* Name of package */ +#define PACKAGE "libupnp" + @@ -247,7 +2884,7 @@ +#define PACKAGE_NAME "libupnp" + +/* Define to the full name and version of this package. */ -+#define PACKAGE_STRING "libupnp 1.6.19" ++#define PACKAGE_STRING "libupnp 1.8.5" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libupnp" @@ -256,7 +2893,7 @@ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ -+#define PACKAGE_VERSION "1.6.19" ++#define PACKAGE_VERSION "1.8.5" + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ @@ -266,13 +2903,16 @@ +#define STDC_HEADERS 1 + +/* see upnpconfig.h */ -+#define UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS 1 ++/*#undef UPNP_ENABLE_BLOCKING_TCP_CONNECTIONS */ + +/* see upnpconfig.h */ -+/* #undef UPNP_ENABLE_IPV6 */ ++/*#undef UPNP_ENABLE_IPV6 */ + +/* see upnpconfig.h */ -+#define UPNP_ENABLE_NOTIFICATION_REORDERING 1 ++/* #undef UPNP_ENABLE_OPEN_SSL */ ++ ++/* see upnpconfig.h */ ++/* #undef UPNP_ENABLE_POST_WRITE */ + +/* see upnpconfig.h */ +/* #undef UPNP_ENABLE_UNSPECIFIED_SERVER */ @@ -304,6 +2944,13 @@ +/* see upnpconfig.h */ +#define UPNP_HAVE_WEBSERVER 1 + ++/* whether the system defaults to 32bit off_t but can do 64bit when requested ++ */ ++/* #undef UPNP_LARGEFILE_SENSITIVE */ ++ ++/* see upnpconfig.h */ ++/* #undef UPNP_MINISERVER_REUSEADDR */ ++ +/* Do not use pthread_rwlock_t */ +#define UPNP_USE_RWLOCK 1 + @@ -311,218 +2958,104 @@ +#define UPNP_VERSION_MAJOR 1 + +/* see upnpconfig.h */ -+#define UPNP_VERSION_MINOR 6 ++#define UPNP_VERSION_MINOR 8 + +/* see upnpconfig.h */ -+#define UPNP_VERSION_PATCH 19 ++#define UPNP_VERSION_PATCH 5 + +/* see upnpconfig.h */ -+#define UPNP_VERSION_STRING "1.6.19" ++#define UPNP_VERSION_STRING "1.8.5" + +/* Version number of package */ -+#define VERSION "1.6.19" ++#define VERSION "1.8.5" + -+/* File Offset size */ -+#define _FILE_OFFSET_BITS 64 ++/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most ++ significant byte first (like Motorola and SPARC, unlike Intel). */ ++#if defined AC_APPLE_UNIVERSAL_BUILD ++# if defined __BIG_ENDIAN__ ++# define WORDS_BIGENDIAN 1 ++# endif ++#else ++# ifndef WORDS_BIGENDIAN ++/* # undef WORDS_BIGENDIAN */ ++# endif ++#endif ++ ++/* Enable large inode numbers on Mac OS X 10.5. */ ++#ifndef _DARWIN_USE_64_BIT_INODE ++# define _DARWIN_USE_64_BIT_INODE 1 ++#endif ++ ++/* Number of bits in a file offset, on hosts where this is settable. */ ++/* #undef _FILE_OFFSET_BITS */ + +/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */ -+#define _LARGEFILE_SOURCE 1 ++/* #undef _LARGEFILE_SOURCE */ + -+/* Large files support */ -+#define _LARGE_FILE_SOURCE /**/ ++/* Define for large files, on AIX-style hosts. */ ++/* #undef _LARGE_FILES */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + -+/* Define to `long int' if does not define. */ -+/* #undef off_t */ -+ +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ + +/* Type for storing the length of struct sockaddr */ +/* #undef socklen_t */ ---- a/threadutil/inc/ThreadPool.h -+++ b/threadutil/inc/ThreadPool.h -@@ -46,6 +46,7 @@ - #include - - #ifdef WIN32 -+#ifndef _TIMEZONE_DEFINED - #include - struct timezone - { -@@ -53,6 +54,7 @@ - int tz_dsttime; /* type of dst correction */ - }; - int gettimeofday(struct timeval *tv, struct timezone *tz); -+#endif - #else /* WIN32 */ - #include - #include /* for gettimeofday() */ ---- a/upnp/src/api/upnpapi.c -+++ b/upnp/src/api/upnpapi.c -@@ -357,8 +357,8 @@ static int UpnpInitPreamble(void) - - #ifdef UPNP_HAVE_OPTSSDP - /* Create the NLS uuid. */ -- uuid_create(&nls_uuid); -- uuid_unpack(&nls_uuid, gUpnpSdkNLSuuid); -+ uuid_upnp_create(&nls_uuid); -+ uuid_upnp_unpack(&nls_uuid, gUpnpSdkNLSuuid); - #endif /* UPNP_HAVE_OPTSSDP */ - - /* Initializes the handle list. */ -@@ -374,13 +374,13 @@ static int UpnpInitPreamble(void) - return retVal; - } - -+#ifdef INTERNAL_WEB_SERVER - #ifdef INCLUDE_DEVICE_APIS - #if EXCLUDE_SOAP == 0 - SetSoapCallback(soap_device_callback); - #endif - #endif /* INCLUDE_DEVICE_APIS */ - --#ifdef INTERNAL_WEB_SERVER - #if EXCLUDE_GENA == 0 - SetGenaCallback(genaCallback); - #endif ---- a/upnp/src/api/upnptools.c -+++ b/upnp/src/api/upnptools.c -@@ -57,7 +57,9 @@ - #define HEADER_LENGTH 2000 - - #ifdef WIN32 -- #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif - #endif - - /*! ---- a/upnp/src/gena/gena_ctrlpt.c -+++ b/upnp/src/gena/gena_ctrlpt.c -@@ -53,7 +53,9 @@ - #include "upnpapi.h" - - #ifdef WIN32 -- #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif - #endif - - extern ithread_mutex_t GlobalClientSubscribeMutex; -@@ -561,8 +563,8 @@ int genaSubscribe( - } - - /* generate client SID */ -- uuid_create(&uid ); -- uuid_unpack(&uid, temp_sid); -+ uuid_upnp_create(&uid ); -+ uuid_upnp_unpack(&uid, temp_sid); - rc = snprintf(temp_sid2, sizeof(temp_sid2), "uuid:%s", temp_sid); - if (rc < 0 || (unsigned int) rc >= sizeof(temp_sid2)) { - return_code = UPNP_E_OUTOF_MEMORY; ---- a/upnp/src/gena/gena_device.c -+++ b/upnp/src/gena/gena_device.c -@@ -53,7 +53,9 @@ - #include "uuid.h" - - #ifdef WIN32 -- #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif - #endif - - #define STALE_JOBID (INVALID_JOB_ID -1) -@@ -1271,8 +1273,8 @@ void gena_process_subscription_request( - } - - /* generate SID */ -- uuid_create(&uid); -- uuid_unpack(&uid, temp_sid); -+ uuid_upnp_create(&uid); -+ uuid_upnp_unpack(&uid, temp_sid); - rc = snprintf(sub->sid, sizeof(sub->sid), "uuid:%s", temp_sid); - - /* respond OK */ ---- a/upnp/src/genlib/miniserver/miniserver.c -+++ b/upnp/src/genlib/miniserver/miniserver.c -@@ -68,6 +68,13 @@ - /*! . */ - #define APPLICATION_LISTENING_PORT 49152 - -+/* IPV6_V6ONLY is missing from MinGW, hack taken from -+ * http://svn.apache.org/repos/asf/apr/apr/trunk/network_io/win32/sockopt.c -+ */ -+#ifndef IPV6_V6ONLY -+#define IPV6_V6ONLY 27 -+#endif -+ - struct mserv_request_t { - /*! Connection handle. */ - SOCKET connfd; ---- a/upnp/src/genlib/net/http/httpreadwrite.c -+++ b/upnp/src/genlib/net/http/httpreadwrite.c -@@ -59,7 +59,9 @@ - #ifdef WIN32 - #include - #define fseeko fseek -- #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif - #else - #include - #include -@@ -2165,6 +2167,7 @@ void get_sdk_info(OUT char *info, IN size_t infoSize) - OSVERSIONINFO versioninfo; - versioninfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - -+#if (WINAPI_FAMILY_APP != WINAPI_FAMILY_PC_APP) - if (GetVersionEx(&versioninfo) != 0) - snprintf(info, infoSize, - "%d.%d.%d %d/%s, UPnP/1.0, Portable SDK for UPnP devices/" -@@ -2174,6 +2177,10 @@ void get_sdk_info(OUT char *info, IN size_t infoSize) - else - *info = '\0'; - #else -+ snprintf(info, infoSize, "%d.%d.%d %d/%s, UPnP/1.0, Portable SDK for UPnP devices/" PACKAGE_VERSION "\r\n", -+ 0, 0, 0, 0, 0); -+ #endif -+ #else - int ret_code; - struct utsname sys_info; - ---- a/upnp/src/genlib/net/http/webserver.c -+++ b/upnp/src/genlib/net/http/webserver.c -@@ -63,7 +63,9 @@ - #include - - #ifdef WIN32 -- #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif - #endif - - /*! ---- a/upnp/src/genlib/net/uri/uri.c -+++ b/upnp/src/genlib/net/uri/uri.c -@@ -45,7 +45,9 @@ - #endif - #endif - #ifdef WIN32 -- #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif - #endif - #include - +--- a/upnp/src/inc/inet_pton.h ++++ /dev/null +@@ -1,50 +0,0 @@ +-#ifndef INET_PTON +-#define INET_PTON +- +-#ifdef _WIN32 +- +-#ifdef IPV6_ +-#define INET_IPV6 +-#endif +- +-#include "unixutil.h" +- +-#include +-#include +-#include +- +-/*! +- * \file +- * +- * \author: Paul Vixie, 1996. +- * +- * \brief Network support routines missing in WIN32. +- * +- * \warning Don't even consider trying to compile this on a system where +- * sizeof(int) < 4. sizeof(int) 4 is fine; all the world's not a VAX. +- * +- */ +- +-/*! +- * \brief convert a network format address to presentation format. +- * +- * \return +- * pointer to presentation format address (`dst'), or NULL (see errno). +- */ +-extern const char *inet_ntop(int af, const void *src, char *dst, +- socklen_t size); +- +-/*! +- * \brief convert from presentation format (which usually means ASCII printable) +- * to network format (which is usually some kind of binary format). +- * +- * \return +- * \li 1 if the address was valid for the specified address family +- * \li 0 if the address wasn't valid (`dst' is untouched in this case) +- * \li -1 if some other error occurred (`dst' is untouched in this case, too) +- */ +-extern int inet_pton(int af, const char *src, void *dst); +- +-#endif /* _WIN32 */ +- +-#endif /* INET_PTON */ --- a/upnp/src/inc/upnputil.h +++ b/upnp/src/inc/upnputil.h @@ -130,7 +130,7 @@ void linecopylen( @@ -530,71 +3063,360 @@ #define sleep(a) Sleep((a)*1000) #define usleep(a) Sleep((a)/1000) - #define strerror_r(a,b,c) (strerror_s((b),(c),(a))) -+ #define strerror_r(a,b,c) strncpy( b, strerror(a), c) ++ #define strerror_r(a,b,c) strncpy( b, strerror(a), c) #else #define max(a, b) (((a)>(b))? (a):(b)) #define min(a, b) (((a)<(b))? (a):(b)) ---- a/upnp/src/inc/uuid.h -+++ b/upnp/src/inc/uuid.h -@@ -41,14 +41,14 @@ typedef struct _uuid_upnp { - /*! - * \brief Generate a UUID. - */ --int uuid_create( -+int uuid_upnp_create( - /*! . */ - uuid_upnp * id); - - /*! - * \brief Out will be xxxx-xx-xx-xx-xxxxxx format. - */ --void uuid_unpack( -+void uuid_upnp_unpack( - /*! . */ - uuid_upnp * u, - /*! . */ -@@ -57,7 +57,7 @@ void uuid_unpack( - /*! - * \brief Create a UUID using a "name" from a "name space" - */ --void uuid_create_from_name( -+void uuid_upnp_create_from_name( - /*! Resulting UUID. */ - uuid_upnp * uid, - /*! UUID to serve as context, so identical names from different name -@@ -78,7 +78,7 @@ void uuid_create_from_name( - * - * \note Lexical ordering is not temporal ordering! - */ --int uuid_compare( -+int uuid_upnp_compare( - /*! . */ - uuid_upnp * u1, - /*! . */ +--- a/upnp/src/inet_pton.c ++++ /dev/null +@@ -1,323 +0,0 @@ +-/* +- * Copyright (c) 1996-1999 by Internet Software Consortium. +- * +- * Permission to use, copy, modify, and distribute this software for any +- * purpose with or without fee is hereby granted, provided that the above +- * copyright notice and this permission notice appear in all copies. +- * +- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS +- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE +- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +- * SOFTWARE. +- */ +- +-/*! +- * \file +- */ +- +-/* This file is _WIN32 only */ +-#ifdef _WIN32 +- +-#include "inet_pton.h" +- +-/*! +- * \brief format an IPv4 address +- * +- * \return `dst' (as a const) +- * +- * \note +- * \li (1) uses no statics +- * \li (2) takes a u_char* not an in_addr as input +- */ +-static const char *inet_ntop4(const u_char *src, char *dst, socklen_t size) +-{ +- char tmp[sizeof ("255.255.255.255") + 1] = "\0"; +- int octet; +- int i; +- +- i = 0; +- for (octet = 0; octet <= 3; octet++) { +- +- if (src[octet]>255) { +- //__set_errno (ENOSPC); +- return (NULL); +- } +- tmp[i++] = '0' + src[octet] / 100; +- if (tmp[i - 1] == '0') { +- tmp[i - 1] = '0' + (src[octet] / 10 % 10); +- if (tmp[i - 1] == '0') i--; +- } else { +- tmp[i++] = '0' + (src[octet] / 10 % 10); +- } +- tmp[i++] = '0' + src[octet] % 10; +- tmp[i++] = '.'; +- } +- tmp[i - 1] = '\0'; +- +- if ((socklen_t)strlen(tmp)>size) { +- //__set_errno (ENOSPC); +- return (NULL); +- } +- +- return strcpy(dst, tmp); +-} +- +-#ifdef INET_IPV6 +-/*! +- * \brief convert IPv6 binary address into presentation (printable) format +- */ +-static const char *inet_ntop6(const u_char *src, char *dst, socklen_t size) +-{ +- /* +- * Note that int32_t and int16_t need only be "at least" large enough +- * to contain a value of the specified size. On some systems, like +- * Crays, there is no such thing as an integer variable with 16 bits. +- * Keep this in mind if you think this function should have been coded +- * to use pointer overlays. All the world's not a VAX. +- */ +- char tmp[sizeof ("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")], *tp; +- struct { int base, len; } best, cur; +- u_int words[8]; +- int i; +- +- /* +- * Preprocess: +- * Copy the input (bytewise) array into a wordwise array. +- * Find the longest run of 0x00's in src[] for :: shorthanding. +- */ +- memset(words, '\0', sizeof words); +- for (i = 0; i < 16; i += 2) +- words[i / 2] = (src[i] << 8) | src[i + 1]; +- best.base = -1; +- cur.base = -1; +- for (i = 0; i < 8; i++) { +- if (words[i] == 0) { +- if (cur.base == -1) +- cur.base = i, cur.len = 1; +- else +- cur.len++; +- } else { +- if (cur.base != -1) { +- if (best.base == -1 || cur.len best.len) +- best = cur; +- cur.base = -1; +- } +- } +- } +- if (cur.base != -1) { +- if (best.base == -1 || cur.len best.len) +- best = cur; +- } +- if (best.base != -1 && best.len < 2) +- best.base = -1; +- +- /* +- * Format the result. +- */ +- tp = tmp; +- for (i = 0; i < 8; i++) { +- /* Are we inside the best run of 0x00's? */ +- if (best.base != -1 && i >= best.base && +- i < (best.base + best.len)) { +- if (i == best.base) +- *tp++ = ':'; +- continue; +- } +- /* Are we following an initial run of 0x00s or any real hex? */ +- if (i != 0) +- *tp++ = ':'; +- /* Is this address an encapsulated IPv4? */ +- if (i == 6 && best.base == 0 && +- (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { +- if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) +- return (NULL); +- tp += strlen(tp); +- break; +- } +- tp += SPRINTF((tp, "%x", words[i])); +- } +- /* Was it a trailing run of 0x00's? */ +- if (best.base != -1 && (best.base + best.len) == 8) +- *tp++ = ':'; +- *tp++ = '\0'; +- +- /* Check for overflow, copy, and we're done. */ +- if ((socklen_t)(tp - tmp) size) { +- //__set_errno (ENOSPC); +- return (NULL); +- } +- return strcpy(dst, tmp); +-} +-#endif /* INET_IPV6 */ +- +-/*! +- * \brief like inet_aton() but without all the hexadecimal and shorthand. +- * +- * \return 1 if `src' is a valid dotted quad, else 0. +- * +- * \note does not touch `dst' unless it's returning 1. +- */ +-static int inet_pton4(const char *src,u_char *dst) +-{ +- int saw_digit, octets, ch; +- u_char tmp[4], *tp; +- +- saw_digit = 0; +- octets = 0; +- *(tp = tmp) = 0; +- while ((ch = *src++) != '\0') { +- if (ch >= '0' && ch <= '9') { +- u_int new = *tp * 10 + (ch - '0'); +- if (new>255) +- return (0); +- *tp = new; +- if (! saw_digit) { +- if (++octets>4) +- return (0); +- saw_digit = 1; +- } +- } else if (ch == '.' && saw_digit) { +- if (octets == 4) +- return (0); +- *++tp = 0; +- saw_digit = 0; +- } else +- return (0); +- } +- if (octets < 4) +- return (0); +- memcpy(dst, tmp, 4); +- return 1; +-} +- +-#ifdef INET_IPV6 +-/*! +- * \brief convert presentation level address to network order binary form. +- * +- * \return 1 if `src' is a valid [RFC1884 2.2] address, else 0. +- * +- * \note +- * \li (1) does not touch `dst' unless it's returning 1. +- * \li (2) :: in a full address is silently ignored. +- */ +-static int inet_pton6(const char *src, u_char *dst) +-{ +- static const char xdigits[] = "0123456789abcdef"; +- u_char tmp[16], *tp, *endp, *colonp; +- const char *curtok; +- int ch, saw_xdigit; +- u_int val; +- +- tp = memset(tmp, '\0', 16); +- endp = tp + 16; +- colonp = NULL; +- /* Leading :: requires some special handling. */ +- if (*src == ':') +- if (*++src != ':') +- return (0); +- curtok = src; +- saw_xdigit = 0; +- val = 0; +- while ((ch = tolower (*src++)) != '\0') { +- const char *pch; +- +- pch = strchr(xdigits, ch); +- if (pch != NULL) { +- val <<= 4; +- val |= (pch - xdigits); +- if (val 0xffff) +- return (0); +- saw_xdigit = 1; +- continue; +- } +- if (ch == ':') { +- curtok = src; +- if (!saw_xdigit) { +- if (colonp) +- return (0); +- colonp = tp; +- continue; +- } else if (*src == '\0') { +- return (0); +- } +- if (tp + 2 endp) +- return (0); +- *tp++ = (u_char) (val >8) & 0xff; +- *tp++ = (u_char) val & 0xff; +- saw_xdigit = 0; +- val = 0; +- continue; +- } +- if (ch == '.' && ((tp + 4) <= endp) && +- inet_pton4(curtok, tp) 0) { +- tp += 4; +- saw_xdigit = 0; +- break; /* '\0' was seen by inet_pton4(). */ +- } +- return (0); +- } +- if (saw_xdigit) { +- if (tp + 2 endp) +- return (0); +- *tp++ = (u_char) (val >8) & 0xff; +- *tp++ = (u_char) val & 0xff; +- } +- if (colonp != NULL) { +- /* Since some memmove()'s erroneously fail to handle +- * overlapping regions, we'll do the shift by hand. */ +- const int n = tp - colonp; +- int i; +- +- if (tp == endp) +- return (0); +- for (i = 1; i <= n; i++) { +- endp[- i] = colonp[n - i]; +- colonp[n - i] = 0; +- } +- tp = endp; +- } +- if (tp != endp) +- return (0); +- memcpy(dst, tmp, 16); +- return (1); +-} +-#endif /* INET_IPV6 */ +- +- +-const char *inet_ntop(int af, const void *src, char *dst,socklen_t size) +-{ +- switch (af) { +- case AF_INET: +- return inet_ntop4(src, dst, size); +-#ifdef INET_IPV6 +- case AF_INET6: +- return inet_ntop6(src, dst, size); +-#endif +- default: +- /*__set_errno(EAFNOSUPPORT);*/ +- return NULL; +- } +- /* NOTREACHED */ +-} +- +-int inet_pton(int af, const char *src, void *dst) +-{ +- switch (af) { +- case AF_INET: +- return inet_pton4(src, dst); +-#ifdef INET_IPV6 +- case AF_INET6: +- return inet_pton6(src, dst); +-#endif +- default: +- /*__set_errno(EAFNOSUPPORT);*/ +- return -1; +- } +- /* NOTREACHED */ +-} +- +-#endif /* _WIN32 */ --- a/upnp/src/soap/soap_device.c +++ b/upnp/src/soap/soap_device.c -@@ -52,7 +52,9 @@ +@@ -53,7 +53,9 @@ #include - #ifdef WIN32 + #ifdef _WIN32 - #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif ++ #if _MSC_VER < 1900 ++ #define snprintf _snprintf ++ #endif #endif /*! timeout duration in secs for transmission/reception */ --- a/upnp/src/ssdp/ssdp_ctrlpt.c +++ b/upnp/src/ssdp/ssdp_ctrlpt.c -@@ -59,7 +59,9 @@ +@@ -58,7 +58,9 @@ - #ifdef WIN32 + #ifdef _WIN32 #include -#define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif - #endif /* WIN32 */ ++ #if _MSC_VER < 1900 ++ #define snprintf _snprintf ++ #endif + #endif /* _WIN32 */ /*! --- a/upnp/src/ssdp/ssdp_device.c @@ -602,37 +3424,35 @@ @@ -57,7 +57,9 @@ #include - #ifdef WIN32 + #ifdef _WIN32 - #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif ++ #if _MSC_VER < 1900 ++ #define snprintf _snprintf ++ #endif #endif #define MSGTYPE_SHUTDOWN 0 --- a/upnp/src/ssdp/ssdp_server.c +++ b/upnp/src/ssdp/ssdp_server.c @@ -41,7 +41,9 @@ - #ifndef WIN32 + #ifndef _WIN32 #include #else - #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif - #endif /* WIN32 */ ++ #if _MSC_VER < 1900 ++ #define snprintf _snprintf ++ #endif + #endif /* _WIN32 */ #include "config.h" -@@ -69,6 +71,13 @@ +@@ -69,6 +71,11 @@ #endif /* UPNP_ENABLE_IPV6 */ #endif /* INCLUDE_CLIENT_APIS */ -+/* IPV6_V6ONLY is missing from MinGW, hack taken from -+ * http://svn.apache.org/repos/asf/apr/apr/trunk/network_io/win32/sockopt.c -+ */ +#ifndef IPV6_V6ONLY +#define IPV6_V6ONLY 27 +#endif ++ + void RequestHandler(); @@ -642,37 +3462,36 @@ @@ -50,7 +50,9 @@ #include - #ifdef WIN32 + #ifdef _WIN32 - #define snprintf _snprintf -+ #if _MSC_VER < 1900 -+ #define snprintf _snprintf -+ #endif ++ #if _MSC_VER < 1900 ++ #define snprintf _snprintf ++ #endif #else #include #endif --- a/upnp/src/uuid/sysdep.c +++ b/upnp/src/uuid/sysdep.c -@@ -30,6 +30,8 @@ +@@ -29,6 +29,7 @@ + #include #include ++#include -+#include -+ /*! * \brief System dependent call to get IEEE node ID. - * -@@ -93,7 +95,9 @@ void get_random_info(unsigned char seed[16]) +@@ -93,7 +94,9 @@ void get_random_info(unsigned char seed[16]) /* Initialize memory area so that valgrind does not complain */ memset(&r, 0, sizeof r); /* memory usage stats */ - GlobalMemoryStatus( &r.m ); +#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) -+ GlobalMemoryStatus(&r.m); ++ GlobalMemoryStatus(&r.m); +#endif /* random system stats */ GetSystemInfo( &r.s ); /* 100ns resolution (nominally) time of day */ -@@ -101,9 +105,13 @@ void get_random_info(unsigned char seed[16]) +@@ -101,9 +104,13 @@ void get_random_info(unsigned char seed[16]) /* high resolution performance counter */ QueryPerformanceCounter( &r.pc ); /* milliseconds since last boot */ @@ -681,51 +3500,13 @@ r.l = MAX_COMPUTERNAME_LENGTH + 1; - GetComputerName( r.hostname, &r.l ); +#if !defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP) -+ GetComputerName(r.hostname, &r.l); ++ GetComputerName(r.hostname, &r.l); +#else -+ GetHostNameW(r.hostname, &r.l); ++ GetHostNameW(r.hostname, &r.l); +#endif /* MD5 it */ MD5Init(&c); MD5Update(&c, (unsigned char *)(&r), sizeof r); ---- a/upnp/src/uuid/uuid.c -+++ b/upnp/src/uuid/uuid.c -@@ -50,7 +50,7 @@ static uint16_t true_random(void); - /*! - * \brief Generator of a UUID. - */ --int uuid_create(uuid_upnp * uid) -+int uuid_upnp_create(uuid_upnp * uid) - { - uuid_time_t timestamp; - uuid_time_t last_time; -@@ -82,7 +82,7 @@ int uuid_create(uuid_upnp * uid) - return 1; - }; - --void uuid_unpack(uuid_upnp * u, char *out) -+void uuid_upnp_unpack(uuid_upnp * u, char *out) - { - sprintf(out, - "%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", -@@ -221,7 +221,7 @@ static uint16_t true_random(void) - /*! - * \brief Create a UUID using a "name" from a "name space". - */ --void uuid_create_from_name( -+void uuid_upnp_create_from_name( - /*! resulting UUID. */ - uuid_upnp *uid, - /*! UUID to serve as context, so identical names from different name -@@ -280,7 +280,7 @@ void format_uuid_v3(uuid_upnp *uid, unsigned char hash[16]) - * - * Note: Lexical ordering is not temporal ordering! - */ --int uuid_compare(uuid_upnp *u1, uuid_upnp *u2) -+int uuid_upnp_compare(uuid_upnp *u1, uuid_upnp *u2) - { - int i; - --- +-- 2.10.2.windows.1 diff --git a/contrib/src/upnp/miniserver.patch b/contrib/src/upnp/miniserver.patch index 45ff90c67..a2114bef2 100644 --- a/contrib/src/upnp/miniserver.patch +++ b/contrib/src/upnp/miniserver.patch @@ -1,17 +1,31 @@ ---- upnp/upnp/src/api/upnpapi.c.orig 2013-04-08 00:23:46.000000000 +0200 -+++ upnp/upnp/src/api/upnpapi.c 2013-04-08 00:25:49.000000000 +0200 -@@ -358,13 +358,13 @@ +From 39f8e5f5379607166cf6e54d270590e1fd13c12f Mon Sep 17 00:00:00 2001 +From: Eden Abitbol +Date: Mon, 3 Jun 2019 15:55:40 -0400 +Subject: [PATCH] web server patch + +--- + upnp/src/api/upnpapi.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/upnp/src/api/upnpapi.c b/upnp/src/api/upnpapi.c +index 9ddc7fb..d286092 100644 +--- a/upnp/src/api/upnpapi.c ++++ b/upnp/src/api/upnpapi.c +@@ -383,13 +383,13 @@ static int UpnpInitPreamble(void) return retVal; } - + +#ifdef INTERNAL_WEB_SERVER #ifdef INCLUDE_DEVICE_APIS #if EXCLUDE_SOAP == 0 SetSoapCallback(soap_device_callback); #endif #endif /* INCLUDE_DEVICE_APIS */ - + -#ifdef INTERNAL_WEB_SERVER #if EXCLUDE_GENA == 0 SetGenaCallback(genaCallback); #endif +-- +2.17.1 + diff --git a/contrib/src/upnp/rules.mak b/contrib/src/upnp/rules.mak index 749a8611f..d058de119 100644 --- a/contrib/src/upnp/rules.mak +++ b/contrib/src/upnp/rules.mak @@ -1,9 +1,9 @@ # UPNP -UPNP_VERSION := 1.6.25 +UPNP_VERSION := 1.8.4 UPNP_URL := https://github.com/mrjimenez/pupnp/archive/release-$(UPNP_VERSION).tar.gz PKGS += upnp -ifeq ($(call need_pkg,"libupnp >= 1.6.25"),) +ifeq ($(call need_pkg,"libupnp >= 1.8.2"),) PKGS_FOUND += upnp endif @@ -14,27 +14,21 @@ $(TARBALLS)/pupnp-release-$(UPNP_VERSION).tar.gz: upnp: pupnp-release-$(UPNP_VERSION).tar.gz .sum-upnp $(UNPACK) -ifdef HAVE_WIN32 - $(APPLY) $(SRC)/upnp/libupnp-win32.patch - $(APPLY) $(SRC)/upnp/libupnp-win64.patch - $(APPLY) $(SRC)/upnp/threadpool.patch -endif -ifdef HAVE_WIN64 - $(APPLY) $(SRC)/upnp/win_inet_pton.patch +ifeq ($(OS),Windows_NT) + $(APPLY) $(SRC)/upnp/libupnp-windows.patch endif $(APPLY) $(SRC)/upnp/libupnp-ipv6.patch $(APPLY) $(SRC)/upnp/miniserver.patch - $(APPLY) $(SRC)/upnp/uuid_upnp.patch $(UPDATE_AUTOCONFIG) && cd $(UNPACK_DIR) && mv config.guess config.sub $(MOVE) .upnp: upnp ifdef HAVE_WIN32 $(RECONF) - cd $< && $(HOSTVARS) CFLAGS="-DUPNP_STATIC_LIB" ./configure --disable-samples --without-documentation --disable-blocking_tcp_connections $(HOSTCONF) + cd $< && $(HOSTVARS) CFLAGS="-DUPNP_STATIC_LIB" ./configure --disable-largefile --disable-samples --without-documentation --disable-blocking_tcp_connections $(HOSTCONF) else $(RECONF) - cd $< && $(HOSTVARS) CFLAGS="$(CFLAGS) -DUPNP_STATIC_LIB" ./configure --disable-samples --without-documentation --disable-blocking_tcp_connections $(HOSTCONF) + cd $< && $(HOSTVARS) CFLAGS="$(CFLAGS) -DUPNP_STATIC_LIB" ./configure --disable-largefile --disable-samples --without-documentation --disable-blocking_tcp_connections $(HOSTCONF) endif cd $< && $(MAKE) install touch $@ diff --git a/contrib/src/upnp/threadpool.patch b/contrib/src/upnp/threadpool.patch index b0a5a6335..db8b89568 100644 --- a/contrib/src/upnp/threadpool.patch +++ b/contrib/src/upnp/threadpool.patch @@ -1,29 +1,34 @@ ---- a/threadutil/src/ThreadPool.c 2012-04-03 09:01:15.000000000 -0400 -+++ b/threadutil/src/ThreadPool.c 2015-05-07 14:37:13.168494069 -0400 -@@ -264,7 +264,7 @@ - setpriority(PRIO_PROCESS, 0, 0); - retVal = 0; - #elif defined(WIN32) -- retVal = sched_setscheduler(0, in); -+ retVal = 0; - #elif defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING > 0 - struct sched_param current; - int sched_result; -@@ -416,7 +416,7 @@ +From 1da6fe09a3387d31c111e5d26c98653e39bf1d46 Mon Sep 17 00:00:00 2001 +From: Eden Abitbol +Date: Mon, 3 Jun 2019 17:48:04 -0400 +Subject: [PATCH] threadpool patch - gettimeofday(&t, NULL); - #if defined(WIN32) +--- + upnp/src/threadutil/ThreadPool.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/upnp/src/threadutil/ThreadPool.c b/upnp/src/threadutil/ThreadPool.c +index 2441d38..6de4d3d 100644 +--- a/upnp/src/threadutil/ThreadPool.c ++++ b/upnp/src/threadutil/ThreadPool.c +@@ -264,7 +264,7 @@ static int SetPolicyType( + setpriority(PRIO_PROCESS, 0, 0); + retVal = 0; + #elif defined(_WIN32) +- retVal = sched_setscheduler(0, in); ++ retVal = 0 + #elif defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING > 0 + struct sched_param current; + int sched_result; +@@ -416,7 +416,7 @@ static void SetSeed(void) + + gettimeofday(&t, NULL); + #if defined(_WIN32) - srand((unsigned int)t.tv_usec + (unsigned int)ithread_get_current_thread_id().p); + srand((unsigned int)t.tv_usec + (unsigned int)ithread_get_current_thread_id()); #elif defined(BSD) || defined(__OSX__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) - srand((unsigned int)t.tv_usec + (unsigned int)ithread_get_current_thread_id()); + srand((unsigned int)t.tv_usec + (unsigned int)ithread_get_current_thread_id()); #elif defined(__linux__) || defined(__sun) || defined(__CYGWIN__) || defined(__GLIBC__) -@@ -654,7 +654,7 @@ - rc = ithread_detach(temp); - /* ithread_detach will return EINVAL if thread has been - successfully detached by ithread_create */ -- if (rc == EINVAL) -+ if (rc == EINVAL || rc == ESRCH) - rc = 0; - tp->pendingWorkerThreadStart = 1; - /* wait until the new worker thread starts */ +-- +2.17.1 + diff --git a/contrib/src/upnp/uuid_upnp.patch b/contrib/src/upnp/uuid_upnp.patch deleted file mode 100644 index 339b9668a..000000000 --- a/contrib/src/upnp/uuid_upnp.patch +++ /dev/null @@ -1,124 +0,0 @@ -diff --git a/upnp/src/api/upnpapi.c b/upnp/src/api/upnpapi.c -index 8f36822..322c724 100644 ---- a/upnp/src/api/upnpapi.c -+++ b/upnp/src/api/upnpapi.c -@@ -341,8 +341,8 @@ static int UpnpInitPreamble(void) - - #ifdef UPNP_HAVE_OPTSSDP - /* Create the NLS uuid. */ -- uuid_create(&nls_uuid); -- uuid_unpack(&nls_uuid, gUpnpSdkNLSuuid); -+ uuid_upnp_create(&nls_uuid); -+ uuid_upnp_unpack(&nls_uuid, gUpnpSdkNLSuuid); - #endif /* UPNP_HAVE_OPTSSDP */ - - /* Initializes the handle list. */ -diff --git a/upnp/src/gena/gena_ctrlpt.c b/upnp/src/gena/gena_ctrlpt.c -index bca4981..0c428fc 100644 ---- a/upnp/src/gena/gena_ctrlpt.c -+++ b/upnp/src/gena/gena_ctrlpt.c -@@ -561,8 +561,8 @@ int genaSubscribe( - } - - /* generate client SID */ -- uuid_create(&uid ); -- uuid_unpack(&uid, temp_sid); -+ uuid_upnp_create(&uid ); -+ uuid_upnp_unpack(&uid, temp_sid); - rc = snprintf(temp_sid2, sizeof(temp_sid2), "uuid:%s", temp_sid); - if (rc < 0 || (unsigned int) rc >= sizeof(temp_sid2)) { - return_code = UPNP_E_OUTOF_MEMORY; -diff --git a/upnp/src/gena/gena_device.c b/upnp/src/gena/gena_device.c -index 39edc0b..b1ec3c4 100644 ---- a/upnp/src/gena/gena_device.c -+++ b/upnp/src/gena/gena_device.c -@@ -1353,8 +1353,8 @@ void gena_process_subscription_request( - } - - /* generate SID */ -- uuid_create(&uid); -- uuid_unpack(&uid, temp_sid); -+ uuid_upnp_create(&uid); -+ uuid_upnp_unpack(&uid, temp_sid); - rc = snprintf(sub->sid, sizeof(sub->sid), "uuid:%s", temp_sid); - - /* respond OK */ -diff --git a/upnp/src/inc/uuid.h b/upnp/src/inc/uuid.h -index 93f5681..17fd463 100644 ---- a/upnp/src/inc/uuid.h -+++ b/upnp/src/inc/uuid.h -@@ -41,14 +41,14 @@ typedef struct _uuid_upnp { - /*! - * \brief Generate a UUID. - */ --int uuid_create( -+int uuid_upnp_create( - /*! . */ - uuid_upnp * id); - - /*! - * \brief Out will be xxxx-xx-xx-xx-xxxxxx format. - */ --void uuid_unpack( -+void uuid_upnp_unpack( - /*! . */ - uuid_upnp * u, - /*! . */ -@@ -57,7 +57,7 @@ void uuid_unpack( - /*! - * \brief Create a UUID using a "name" from a "name space" - */ --void uuid_create_from_name( -+void uuid_upnp_create_from_name( - /*! Resulting UUID. */ - uuid_upnp * uid, - /*! UUID to serve as context, so identical names from different name -@@ -78,7 +78,7 @@ void uuid_create_from_name( - * - * \note Lexical ordering is not temporal ordering! - */ --int uuid_compare( -+int uuid_upnp_compare( - /*! . */ - uuid_upnp * u1, - /*! . */ -diff --git a/upnp/src/uuid/uuid.c b/upnp/src/uuid/uuid.c -index 211b61f..ecd5904 100644 ---- a/upnp/src/uuid/uuid.c -+++ b/upnp/src/uuid/uuid.c -@@ -50,7 +50,7 @@ static uint16_t true_random(void); - /*! - * \brief Generator of a UUID. - */ --int uuid_create(uuid_upnp * uid) -+int uuid_upnp_create(uuid_upnp * uid) - { - uuid_time_t timestamp; - uuid_time_t last_time; -@@ -82,7 +82,7 @@ int uuid_create(uuid_upnp * uid) - return 1; - }; - --void uuid_unpack(uuid_upnp * u, char *out) -+void uuid_upnp_unpack(uuid_upnp * u, char *out) - { - sprintf(out, - "%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x", -@@ -221,7 +221,7 @@ static uint16_t true_random(void) - /*! - * \brief Create a UUID using a "name" from a "name space". - */ --void uuid_create_from_name( -+void uuid_upnp_create_from_name( - /*! resulting UUID. */ - uuid_upnp *uid, - /*! UUID to serve as context, so identical names from different name -@@ -280,7 +280,7 @@ void format_uuid_v3(uuid_upnp *uid, unsigned char hash[16]) - * - * Note: Lexical ordering is not temporal ordering! - */ --int uuid_compare(uuid_upnp *u1, uuid_upnp *u2) -+int uuid_upnp_compare(uuid_upnp *u1, uuid_upnp *u2) - { - int i; - diff --git a/src/account.cpp b/src/account.cpp index 4bb0bbdd5..4111ad595 100644 --- a/src/account.cpp +++ b/src/account.cpp @@ -471,11 +471,11 @@ Account::getUPnPIpAddress() const * ie: if it is able to make port mappings */ bool -Account::getUPnPActive(std::chrono::seconds timeout) const +Account::getUPnPActive() const { std::lock_guard lk(upnp_mtx); if (upnp_) - return upnp_->hasValidIGD(timeout); + return upnp_->hasValidIGD(); return false; } diff --git a/src/account.h b/src/account.h index 097e7071f..575da296b 100644 --- a/src/account.h +++ b/src/account.h @@ -295,7 +295,7 @@ class Account : public Serializable, public std::enable_shared_from_thisaddAnyMapping(port, portType, true, &port_used)) { + if (upnp_->addMapping(port, portType, true, &port_used)) { publicIP.setPort(port_used); addReflectiveCandidate(comp_id, candidate.addr, publicIP, candidate.transport); } else - JAMI_WARN("[ice:%p] UPnP: Could not create a port mapping for the ICE candide", this); + JAMI_WARN("[ice:%p] Could not create a port mapping for the ICE candide", this); } } } else { - JAMI_WARN("[ice:%p] UPnP: Could not determine public IP for ICE candidates", this); + JAMI_WARN("[ice:%p] Could not determine public IP for ICE candidates", this); } } } diff --git a/src/jamidht/jamiaccount.cpp b/src/jamidht/jamiaccount.cpp index fe1b8a469..d7e4146ee 100644 --- a/src/jamidht/jamiaccount.cpp +++ b/src/jamidht/jamiaccount.cpp @@ -2024,10 +2024,10 @@ JamiAccount::mapPortUPnP() uint16_t port_used; std::lock_guard lock(upnp_mtx); upnp_->removeMappings(); - added = upnp_->addAnyMapping(dhtPort_, jami::upnp::PortType::UDP, false, &port_used); + added = upnp_->addMapping(dhtPort_, jami::upnp::PortType::UDP, false, &port_used); if (added) { if (port_used != dhtPort_) - JAMI_DBG("UPnP could not map port %u for DHT, using %u instead", dhtPort_, port_used); + JAMI_WARN("[Account %s] Could not map port %u for DHT, using %u instead.", getAccountID().c_str(), dhtPort_, port_used); dhtPortUsed_ = port_used; } } diff --git a/src/sip/sipaccount.cpp b/src/sip/sipaccount.cpp index 4ab8a078a..aed61a2b4 100644 --- a/src/sip/sipaccount.cpp +++ b/src/sip/sipaccount.cpp @@ -705,10 +705,10 @@ bool SIPAccount::mapPortUPnP() * a different port, if succesfull, then we have to use that port for SIP */ uint16_t port_used; - bool added = upnp_->addAnyMapping(publishedPort_, localPort_, jami::upnp::PortType::UDP, false, false, &port_used); + bool added = upnp_->addMapping(publishedPort_, jami::upnp::PortType::UDP, false, &port_used, localPort_); if (added) { if (port_used != publishedPort_) - JAMI_DBG("UPnP could not map published port %u for SIP, using %u instead", publishedPort_, port_used); + JAMI_WARN("[Account %s] Could not map published port %u for SIP, using %u instead.", getAccountID().c_str(), publishedPort_, port_used); publishedPortUsed_ = port_used; } } @@ -740,7 +740,7 @@ void SIPAccount::doRegister() if (auto acc = w.lock()) { sip_utils::register_thread(); if (not acc->mapPortUPnP()) - JAMI_WARN("UPnP: Could not successfully map SIP port with UPnP, continuing with account registration anyways."); + JAMI_WARN("UPnP: Could not successfully map SIP port with UPnP, continuing with account registration anyways"); acc->doRegister1_(); } }}.detach(); diff --git a/src/sip/sipcall.cpp b/src/sip/sipcall.cpp index 773195e01..bc6b73ce8 100644 --- a/src/sip/sipcall.cpp +++ b/src/sip/sipcall.cpp @@ -1186,17 +1186,17 @@ SIPCall::openPortsUPnP() */ JAMI_DBG("[call:%s] opening ports via UPNP for SDP session", getCallId().c_str()); uint16_t audio_port_used; - if (upnp_->addAnyMapping(sdp_->getLocalAudioPort(), upnp::PortType::UDP, true, &audio_port_used)) { + if (upnp_->addMapping(sdp_->getLocalAudioPort(), upnp::PortType::UDP, true, &audio_port_used)) { uint16_t control_port_used; - if (upnp_->addAnyMapping(sdp_->getLocalAudioControlPort(), upnp::PortType::UDP, true, &control_port_used)) { + if (upnp_->addMapping(sdp_->getLocalAudioControlPort(), upnp::PortType::UDP, true, &control_port_used)) { sdp_->setLocalPublishedAudioPorts(audio_port_used, control_port_used); } } #ifdef ENABLE_VIDEO uint16_t video_port_used; - if (upnp_->addAnyMapping(sdp_->getLocalVideoPort(), upnp::PortType::UDP, true, &video_port_used)) { + if (upnp_->addMapping(sdp_->getLocalVideoPort(), upnp::PortType::UDP, true, &video_port_used)) { uint16_t control_port_used; - if (upnp_->addAnyMapping(sdp_->getLocalVideoControlPort(), upnp::PortType::UDP, true, &control_port_used)) { + if (upnp_->addMapping(sdp_->getLocalVideoControlPort(), upnp::PortType::UDP, true, &control_port_used)) { sdp_->setLocalPublishedVideoPorts(video_port_used, control_port_used); } } diff --git a/src/upnp/Makefile.am b/src/upnp/Makefile.am index 60a07466c..01975e779 100644 --- a/src/upnp/Makefile.am +++ b/src/upnp/Makefile.am @@ -2,13 +2,16 @@ include $(top_srcdir)/globals.mk noinst_LTLIBRARIES = libupnpcontrol.la +SUBDIRS = protocol + libupnpcontrol_la_CXXFLAGS = \ - @CXXFLAGS@ + @CXXFLAGS@ libupnpcontrol_la_SOURCES = \ upnp_control.cpp \ upnp_control.h \ upnp_context.cpp \ - upnp_context.h \ - upnp_igd.cpp \ - upnp_igd.h + upnp_context.h + +libupnpcontrol_la_LIBADD = \ + ./protocol/libupnpprotocol.la \ No newline at end of file diff --git a/src/upnp/protocol/Makefile.am b/src/upnp/protocol/Makefile.am new file mode 100644 index 000000000..5e5c56839 --- /dev/null +++ b/src/upnp/protocol/Makefile.am @@ -0,0 +1,33 @@ +include $(top_srcdir)/globals.mk + +noinst_LTLIBRARIES = libupnpprotocol.la + +SUBDIRS = + +if BUILD_PUPNP +SUBDIRS += pupnp +endif +if BUILD_NATPMP +SUBDIRS += natpmp +endif + +libupnpprotocol_la_CXXFLAGS = \ + @CXXFLAGS@ + +libupnpprotocol_la_SOURCES = \ + igd.h \ + igd.cpp \ + mapping.h \ + mapping.cpp \ + global_mapping.h \ + upnp_protocol.h + +libupnpprotocol_la_LIBADD = + +if BUILD_PUPNP +libupnpprotocol_la_LIBADD += ./pupnp/libpupnp.la +endif + +if BUILD_NATPMP +libupnpprotocol_la_LIBADD += ./natpmp/libnat_pmp.la +endif \ No newline at end of file diff --git a/src/upnp/protocol/global_mapping.h b/src/upnp/protocol/global_mapping.h new file mode 100644 index 000000000..ead084417 --- /dev/null +++ b/src/upnp/protocol/global_mapping.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Stepan Salenikovich + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "mapping.h" + +#include "noncopyable.h" + +namespace jami { namespace upnp { + +/* + * GlobalMapping is like a mapping, but it tracks the number of global users, + * ie: the number of upnp:Controller which are using this mapping + * this is usually only relevant for accounts (not calls) as multiple SIP accounts + * can use the same SIP port and we don't want to delete a mapping from the router + * if other accounts are using it + */ +class GlobalMapping : public Mapping +{ +public: + GlobalMapping(const Mapping& mapping, unsigned users = 1) : + Mapping(mapping.getPortExternal(), + mapping.getPortInternal(), + mapping.getType(), + mapping.getDescription()), + users(users) {} + +public: + unsigned users; // Number of users of this mapping. Multiple accounts can use the same SIP ports. +}; + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/protocol/igd.cpp b/src/upnp/protocol/igd.cpp new file mode 100644 index 000000000..7e5f1701a --- /dev/null +++ b/src/upnp/protocol/igd.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Stepan Salenikovich + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "igd.h" + +namespace jami { namespace upnp { + +IGD::IGD(IpAddr&& localIp, IpAddr&& publicIp) +{ + localIp_ = std::move(localIp); + publicIp_ = std::move(publicIp); +} + +bool +IGD::operator==(IGD& other) const +{ + return (localIp_ == other.localIp_ and publicIp_ == other.publicIp_); +} + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/protocol/igd.h b/src/upnp/protocol/igd.h new file mode 100644 index 000000000..856c7311b --- /dev/null +++ b/src/upnp/protocol/igd.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#pragma once + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "global_mapping.h" + +#include "noncopyable.h" +#include "ip_utils.h" +#include "string_utils.h" + +namespace jami { namespace upnp { + +// Subclasses to make it easier to differentiate and cast maps of port mappings. +class PortMapLocal : public std::map {}; +class PortMapGlobal : public std::map {}; + +class IGD +{ +public: + IGD(IpAddr&& localIp = {}, IpAddr&& publicIp = {}); + IGD(IGD&&) = default; + virtual ~IGD() = default; + + IGD& operator=(IGD&&) = default; + bool operator==(IGD& other) const; + +public: + IpAddr localIp_ {}; // Internal IP interface used to communication with IGD. + IpAddr publicIp_ {}; // External IP of IGD. + + PortMapGlobal udpMappings {}; // IGD UDP port mappings. + PortMapGlobal tcpMappings {}; // IGD TCP port mappings. + +private: + NON_COPYABLE(IGD); +}; + + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/upnp_igd.cpp b/src/upnp/protocol/mapping.cpp similarity index 75% rename from src/upnp/upnp_igd.cpp rename to src/upnp/protocol/mapping.cpp index bbe92bc79..b24a645ce 100644 --- a/src/upnp/upnp_igd.cpp +++ b/src/upnp/protocol/mapping.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2004-2019 Savoir-faire Linux Inc. * * Author: Stepan Salenikovich + * Author: Eden Abitbol * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,16 +19,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "upnp_igd.h" +#include "mapping.h" namespace jami { namespace upnp { -/* move constructor and operator */ -Mapping::Mapping(Mapping&& other) noexcept - : port_external_(other.port_external_) - , port_internal_(other.port_internal_) - , type_(other.type_) - , description_(std::move(other.description_)) +Mapping::Mapping(Mapping&& other) noexcept: + port_external_(other.port_external_), + port_internal_(other.port_internal_), + type_(other.type_), + description_(std::move(other.description_)) { other.port_external_ = 0; other.port_internal_ = 0; @@ -48,10 +48,6 @@ Mapping& Mapping::operator=(Mapping&& other) noexcept bool operator== (const Mapping& cMap1, const Mapping& cMap2) { - /* we don't compare the description because it doesn't change the function of the - * mapping; we don't compare the IGD because for now we assume that we always - * use the same one and that all mappings are active - */ return (cMap1.port_external_ == cMap2.port_external_ && cMap1.port_internal_ == cMap2.port_internal_ && cMap1.type_ == cMap2.type_); @@ -62,4 +58,14 @@ bool operator!= (const Mapping& cMap1, const Mapping& cMap2) return !(cMap1 == cMap2); } -}} // namespace jami::upnp +std::string Mapping::toString() const +{ + return getPortExternalStr() + ":" + getPortInternalStr() + " " + getTypeStr(); +} + +bool Mapping::isValid() const +{ + return port_external_ == 0 or port_internal_ == 0 ? false : true; +}; + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/protocol/mapping.h b/src/upnp/protocol/mapping.h new file mode 100644 index 000000000..2be31ea72 --- /dev/null +++ b/src/upnp/protocol/mapping.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "noncopyable.h" +#include "ip_utils.h" +#include "string_utils.h" + +#include +#include +#include +#include + +namespace jami { namespace upnp { + +enum class PortType { UDP, TCP }; + +class Mapping +{ +public: + constexpr static const char * UPNP_DEFAULT_MAPPING_DESCRIPTION = "RING"; + constexpr static uint16_t UPNP_PORT_MIN = 1024; + constexpr static uint16_t UPNP_PORT_MAX = 65535; + + Mapping(uint16_t port_external = 0, + uint16_t port_internal = 0, + PortType type = PortType::UDP, + const std::string& description = UPNP_DEFAULT_MAPPING_DESCRIPTION): + port_external_(port_external), + port_internal_(port_internal), + type_(type), + description_(description) {}; + Mapping(Mapping&&) noexcept; + ~Mapping() = default; + + Mapping& operator=(Mapping&&) noexcept; + friend bool operator== (const Mapping& cRedir1, const Mapping& cRedir2); + friend bool operator!= (const Mapping& cRedir1, const Mapping& cRedir2); + + uint16_t getPortExternal() const { return port_external_; } + std::string getPortExternalStr() const { return std::to_string(port_external_); } + uint16_t getPortInternal() const { return port_internal_; } + std::string getPortInternalStr() const { return std::to_string(port_internal_); } + PortType getType() const { return type_; } + std::string getTypeStr() const { return type_ == PortType::UDP ? "UDP" : "TCP"; } + std::string getDescription() const { return description_; } + + std::string toString() const; + bool isValid() const; + + inline explicit operator bool() const { return isValid(); } + +public: +#if HAVE_LIBNATPMP + std::chrono::system_clock::time_point renewal_ {std::chrono::system_clock::time_point::min()}; + bool remove {false}; +#endif + +private: + NON_COPYABLE(Mapping); + +protected: + uint16_t port_external_; + uint16_t port_internal_; + PortType type_; + std::string description_; +}; + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/protocol/natpmp/Makefile.am b/src/upnp/protocol/natpmp/Makefile.am new file mode 100644 index 000000000..4058c7696 --- /dev/null +++ b/src/upnp/protocol/natpmp/Makefile.am @@ -0,0 +1,12 @@ +include $(top_srcdir)/globals.mk + +noinst_LTLIBRARIES = libnat_pmp.la + +libnat_pmp_la_CXXFLAGS = \ + @CXXFLAGS@ + +libnat_pmp_la_SOURCES = \ + pmp_igd.h \ + pmp_igd.cpp \ + nat_pmp.h \ + nat_pmp.cpp \ No newline at end of file diff --git a/src/upnp/protocol/natpmp/nat_pmp.cpp b/src/upnp/protocol/natpmp/nat_pmp.cpp new file mode 100644 index 000000000..8a2e86099 --- /dev/null +++ b/src/upnp/protocol/natpmp/nat_pmp.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "nat_pmp.h" + +namespace jami { namespace upnp { + +NatPmp::NatPmp() + : pmpThread_([this]() { + auto pmp_igd = std::make_shared(); + natpmp_t natpmp; + + while (pmpRun_) { + if (initnatpmp(&natpmp, 0, 0) < 0) { + JAMI_ERR("NAT-PMP: Can't initialize libnatpmp"); + std::unique_lock lk(pmpMutex_); + pmpCv_.wait_for(lk, std::chrono::minutes(1)); + } else { + JAMI_DBG("NAT-PMP: Initialized"); + break; + } + } + + while (pmpRun_) { + std::unique_lock lk(pmpMutex_); + pmpCv_.wait_until(lk, pmp_igd->getRenewalTime(), [&] { + return not pmpRun_ or pmp_igd->getRenewalTime() <= clock::now(); + }); + if (not pmpRun_) break; + + auto now = clock::now(); + + if (pmp_igd->renewal_ < now) { + searchForIGD(pmp_igd, natpmp); + } + if (pmpIGD_) { + if (pmp_igd->clearAll_) { + deleteAllPortMappings(*pmp_igd, natpmp, NATPMP_PROTOCOL_UDP); + deleteAllPortMappings(*pmp_igd, natpmp, NATPMP_PROTOCOL_TCP); + pmp_igd->clearAll_ = false; + pmp_igd->toRemove_.clear(); + } else if (not pmp_igd->toRemove_.empty()) { + decltype(pmp_igd->toRemove_) removed = std::move(pmp_igd->toRemove_); + pmp_igd->toRemove_.clear(); + lk.unlock(); + for (auto& m : removed) { + addPortMapping(*pmp_igd, natpmp, m, true); + } + lk.lock(); + } + auto mapping = pmp_igd->getNextMappingToRenew(); + if (mapping and mapping->renewal_ < now) + addPortMapping(*pmp_igd, natpmp, *mapping); + } + } + closenatpmp(&natpmp); + }) +{ + +} + +NatPmp::~NatPmp() +{ + { + std::lock_guard lock(validIgdMutex); + + if (pmpIGD_) { + { + std::lock_guard lk(pmpMutex_); + pmpIGD_->clearMappings(); + } + pmpCv_.notify_all(); + } + } + + pmpRun_ = false; + pmpCv_.notify_all(); + if (pmpThread_.joinable()) { + pmpThread_.join(); + } + pmpIGD_.reset(); +} + +void +NatPmp::connectivityChanged() +{ + { + // Lock valid IGD. + std::lock_guard lock(validIgdMutex); + + // Clear internal IGD (nat pmp only supports one). + if (pmpIGD_) { + std::lock_guard lk(pmpMutex_); + pmpIGD_->renewal_ = clock::now(); + pmpIGD_.reset(); + } + + validIgdMutex.unlock(); + + // Notify. + pmpCv_.notify_all(); + } + +} + +void +NatPmp::searchForIGD() +{ + pmpRun_ = true; + + // Clear internal IGD (nat pmp only supports one). + if (pmpIGD_) { + std::lock_guard lk(pmpMutex_); + pmpIGD_->renewal_ = clock::now(); + pmpIGD_.reset(); + } + pmpCv_.notify_all(); +} + +Mapping +NatPmp::addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type, UPnPProtocol::UpnpError& upnp_error) +{ + upnp_error = UPnPProtocol::UpnpError::INVALID_ERR; + + Mapping mapping {port_external, port_internal, type}; + + /* check if this mapping already exists + * if the mapping is the same, then we just need to increment the number of users globally + * if the mapping is not the same, then we have to return fail, as the external port is used + * for something else + * if the mapping doesn't exist, then try to add it + */ + auto globalMappings = type == PortType::UDP ? &igd->udpMappings : &igd->tcpMappings; + auto iter = globalMappings->find(port_external); + if (iter != globalMappings->end()) { + /* mapping exists with same external port */ + auto mapping_ptr = &iter->second; + if (*mapping_ptr == mapping) { + /* the same mapping, so nothing needs to be done */ + upnp_error = UPnPProtocol::UpnpError::ERROR_OK; + ++(mapping_ptr->users); + JAMI_DBG("NAT-PMP: Mapping already exists, incrementing number of users: %d", + iter->second.users); + return mapping; + } else { + /* this port is already used by a different mapping */ + JAMI_WARN("NAT-PMP: Cannot add a mapping with an external port which is already used by another:\n\tcurrent: %s\n\ttrying to add: %s", + mapping_ptr->toString().c_str(), mapping.toString().c_str()); + upnp_error = UPnPProtocol::UpnpError::CONFLICT_IN_MAPPING; + return {}; + } + } + + { + /* success; add it to global list */ + globalMappings->emplace(port_external, GlobalMapping{mapping}); + + pmpCv_.notify_all(); + return mapping; + } + return {}; +} + +void +NatPmp::removeAllLocalMappings(IGD* /*igd*/) +{ + if (pmpIGD_) { + pmpIGD_->clearAll_ = true; + pmpCv_.notify_all(); + } +} + +void +NatPmp::removeMapping(const Mapping& igdMapping) +{ + std::lock_guard lock(validIgdMutex); + + if (not pmpIGD_) { + JAMI_WARN("NAT-PMP: no valid IGD available"); + return; + } + + /* first make sure the mapping exists in the global list of the igd */ + auto globalMappings = igdMapping.getType() == PortType::UDP ? + &pmpIGD_->udpMappings : &pmpIGD_->tcpMappings; + + auto iter = globalMappings->find(igdMapping.getPortExternal()); + if (iter != globalMappings->end()) { + /* make sure its the same mapping */ + GlobalMapping& global_mapping = iter->second; + if (igdMapping == global_mapping) { + /* now check the users */ + if (global_mapping.users > 1) { + /* more than one user, simply decrement the number */ + --(global_mapping.users); + JAMI_DBG("NAT-PMP: Decrementing users of mapping: %s, %d users remaining", + igdMapping.toString().c_str(), global_mapping.users); + } else { + { + std::lock_guard lk(pmpMutex_); + pmpIGD_->toRemove_.emplace_back(std::move(global_mapping)); + } + pmpCv_.notify_all(); + globalMappings->erase(iter); + } + } else { + JAMI_WARN("NAT-PMP: Cannot remove mapping which doesn't match the existing one in the IGD list"); + } + } else { + JAMI_WARN("NAT-PMP: Cannot remove mapping which is not in the list of existing mappings of the IGD"); + } +} + +void +NatPmp::searchForIGD(const std::shared_ptr& pmp_igd, natpmp_t& natpmp) +{ + if (sendpublicaddressrequest(&natpmp) < 0) { + JAMI_ERR("NAT-PMP: Can't send request"); + pmp_igd->renewal_ = clock::now() + std::chrono::minutes(1); + return; + } + + while (pmpRun_) { + natpmpresp_t response; + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + auto r = readnatpmpresponseorretry(&natpmp, &response); + if (r < 0 && r != NATPMP_TRYAGAIN) { + pmp_igd->renewal_ = clock::now() + std::chrono::minutes(5); + break; + } else if (r != NATPMP_TRYAGAIN) { + pmp_igd->localIp_ = ip_utils::getLocalAddr(AF_INET); + pmp_igd->publicIp_ = IpAddr(response.pnu.publicaddress.addr); + if (not pmpIGD_) { + JAMI_DBG("NAT-PMP: Found device with external IP %s", pmp_igd->publicIp_.toString().c_str()); + { + // Store public Ip address. + std::string publicIpStr(std::move(pmp_igd.get()->publicIp_.toString())); + + // Add the igd to the upnp context class list. + if (updateIgdListCb_(this, std::move(pmp_igd.get()), std::move(pmp_igd.get()->publicIp_), true)) { + JAMI_DBG("NAT-PMP: IGD with public IP %s was added to the list", publicIpStr.c_str()); + } else { + JAMI_DBG("NAT-PMP: IGD with public IP %s is already in the list", publicIpStr.c_str()); + } + + // Keep IGD internally. + std::lock_guard lock(validIgdMutex); + pmpIGD_ = pmp_igd; + } + } + pmp_igd->renewal_ = clock::now() + std::chrono::minutes(1); + break; + } + } +} + +void +NatPmp::addPortMapping(const PMPIGD& /*pmp_igd*/, natpmp_t& natpmp, GlobalMapping& mapping, bool remove) const +{ + if (sendnewportmappingrequest(&natpmp, + mapping.getType() == PortType::UDP ? NATPMP_PROTOCOL_UDP : NATPMP_PROTOCOL_TCP, + mapping.getPortInternal(), + mapping.getPortExternal(), remove ? 0 : 3600) < 0) { + JAMI_ERR("NAT-PMP: Can't send port mapping request"); + mapping.renewal_ = clock::now() + std::chrono::minutes(1); + return; + } + + while (pmpRun_) { + natpmpresp_t response; + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + auto r = readnatpmpresponseorretry(&natpmp, &response); + if (r < 0 && r != NATPMP_TRYAGAIN) { + JAMI_ERR("NAT-PMP: Can't %sregister port mapping", remove ? "un" : ""); + break; + } + else if (r != NATPMP_TRYAGAIN) { + mapping.renewal_ = clock::now() + + std::chrono::seconds(response.pnu.newportmapping.lifetime/2); + if (remove) { + JAMI_WARN("NAT-PMP: Closed port %d:%d %s", mapping.getPortInternal(), + mapping.getPortExternal(), + mapping.getType() == PortType::UDP ? "UDP" : "TCP"); + } else { + JAMI_WARN("NAT-PMP: Opened port %d:%d %s", mapping.getPortInternal(), + mapping.getPortExternal(), + mapping.getType() == PortType::UDP ? "UDP" : "TCP"); + } + break; + } + } +} + +void +NatPmp::deleteAllPortMappings(const PMPIGD& /*pmp_igd*/, natpmp_t& natpmp, int proto) const +{ + if (sendnewportmappingrequest(&natpmp, proto, 0, 0, 0) < 0) { + JAMI_ERR("NAT-PMP: Can't send all port mapping removal request"); + return; + } + + while (pmpRun_) { + natpmpresp_t response; + std::this_thread::sleep_for(std::chrono::milliseconds(2)); + auto r = readnatpmpresponseorretry(&natpmp, &response); + if (r < 0 && r != NATPMP_TRYAGAIN) { + JAMI_ERR("NAT-PMP: Can't remove all port mappings"); + break; + } + else if (r != NATPMP_TRYAGAIN) { + break; + } + } +} + + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/protocol/natpmp/nat_pmp.h b/src/upnp/protocol/natpmp/nat_pmp.h new file mode 100644 index 000000000..31969610c --- /dev/null +++ b/src/upnp/protocol/natpmp/nat_pmp.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../upnp_protocol.h" +#include "../global_mapping.h" +#include "../igd.h" +#include "pmp_igd.h" + +#include "logger.h" +#include "ip_utils.h" +#include "noncopyable.h" +#include "compiler_intrinsics.h" + +#include + +#include +#include + +namespace jami { +class IpAddr; +} + +namespace jami { namespace upnp { + +class NatPmp : public UPnPProtocol +{ +public: + NatPmp(); + ~NatPmp(); + + // Returns the protocol type. + Type getType() const override { return Type::NAT_PMP; } + + // Notifies a change in network. + void connectivityChanged() override; + + // Renew pmp_igd. + void searchForIGD() override; + + // Tries to add mapping. Assumes mutex is already locked. + Mapping addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type, UPnPProtocol::UpnpError& upnp_error) override; + + // Removes a mapping. + void removeMapping(const Mapping& igdMapping) override; + + // Removes all local mappings of IGD that we're added by the application. + void removeAllLocalMappings(IGD* igd) override; + +private: + // Searches for an IGD. + void searchForIGD(const std::shared_ptr& pmp_igd, natpmp_t& natpmp); + + // Adds (or deletes) a port mapping. + void addPortMapping(const PMPIGD& pmp_igd, natpmp_t& natpmp, GlobalMapping& mapping, bool remove=false) const; + + // Deletes all port mappings. + void deleteAllPortMappings(const PMPIGD& pmp_igd, natpmp_t& natpmp, int proto) const; + +private: + NON_COPYABLE(NatPmp); + + std::mutex pmpMutex_ {}; // NatPmp mutex. + std::condition_variable pmpCv_ {}; // Condition variable for thread-safe signaling. + std::atomic_bool pmpRun_ { false }; // Variable to allow the thread to run. + std::thread pmpThread_ {}; // NatPmp thread. + + std::shared_ptr pmpIGD_ {}; // IGD discovered by NatPmp. +}; + +}} // namespace jami::upnp diff --git a/src/upnp/protocol/natpmp/pmp_igd.cpp b/src/upnp/protocol/natpmp/pmp_igd.cpp new file mode 100644 index 000000000..78294a8ae --- /dev/null +++ b/src/upnp/protocol/natpmp/pmp_igd.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Stepan Salenikovich + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include "pmp_igd.h" + +namespace jami { namespace upnp { + +void +PMPIGD::clear() +{ + toRemove_.clear(); + udpMappings.clear(); + tcpMappings.clear(); +} + +void +PMPIGD::clearMappings() +{ + clear(); + clearAll_ = true; +} + +GlobalMapping* +PMPIGD::getNextMappingToRenew() const +{ + const GlobalMapping* mapping {nullptr}; + for (const auto& m : udpMappings) + { + if (!mapping or m.second.renewal_ < mapping->renewal_) + { + mapping = &m.second; + } + } + + for (const auto& m : tcpMappings) + { + if (!mapping or m.second.renewal_ < mapping->renewal_) + { + mapping = &m.second; + } + } + return (GlobalMapping*)mapping; +} + +time_point +PMPIGD::getRenewalTime() const +{ + const auto next = getNextMappingToRenew(); + auto nextTime = std::min(renewal_, next ? next->renewal_ : time_point::max()); + return toRemove_.empty() ? nextTime : std::min(nextTime, time_point::min()); +} + +bool +PMPIGD::operator==(PMPIGD& other) const +{ + return publicIp_ == other.publicIp_ and localIp_ == other.localIp_; +} + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/protocol/natpmp/pmp_igd.h b/src/upnp/protocol/natpmp/pmp_igd.h new file mode 100644 index 000000000..eab209e37 --- /dev/null +++ b/src/upnp/protocol/natpmp/pmp_igd.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Stepan Salenikovich + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../igd.h" +#include "../global_mapping.h" + +#include "noncopyable.h" +#include "ip_utils.h" +#include "string_utils.h" + +#include +#include +#include +#include +#include + +namespace jami { namespace upnp { + +using clock = std::chrono::system_clock; +using time_point = clock::time_point; + +class PMPIGD : public IGD +{ +public: + PMPIGD(IpAddr&& localIp = {}, IpAddr&& publicIp = {}): + IGD(std::move(localIp), std::move(publicIp)){} + ~PMPIGD() = default; + bool operator==(PMPIGD& other) const; + + void clear(); + void clearMappings(); + + GlobalMapping* getNextMappingToRenew() const; + + time_point getRenewalTime() const; + +public: + time_point renewal_ {time_point::min()}; + std::vector toRemove_ {}; + + // Upon creation, the thread will clear all the previously opened + // mappings (if there are any). The NatPmp class will then set the + // clearAll variable to false. + std::atomic_bool clearAll_ {true}; +}; + +}} // namespace jami::upnp diff --git a/src/upnp/protocol/pupnp/Makefile.am b/src/upnp/protocol/pupnp/Makefile.am new file mode 100644 index 000000000..9c02e0af1 --- /dev/null +++ b/src/upnp/protocol/pupnp/Makefile.am @@ -0,0 +1,12 @@ +include $(top_srcdir)/globals.mk + +noinst_LTLIBRARIES = libpupnp.la + +libpupnp_la_CXXFLAGS = \ + @CXXFLAGS@ + +libpupnp_la_SOURCES = \ + upnp_igd.h \ + upnp_igd.cpp \ + pupnp.h \ + pupnp.cpp \ No newline at end of file diff --git a/src/upnp/protocol/pupnp/pupnp.cpp b/src/upnp/protocol/pupnp/pupnp.cpp new file mode 100644 index 000000000..da14fe9dd --- /dev/null +++ b/src/upnp/protocol/pupnp/pupnp.cpp @@ -0,0 +1,924 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Stepan Salenikovich + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "pupnp.h" + +namespace jami { namespace upnp { + +// Helper functions for xml parsing. +static std::string +getElementText(IXML_Node* node) +{ + std::string ret; + if (node) { + IXML_Node *textNode = ixmlNode_getFirstChild(node); + if (textNode) { + const char* value = ixmlNode_getNodeValue(textNode); + if (value) + ret = std::string(value); + } + } + return ret; +} + +static std::string +getFirstDocItem(IXML_Document* doc, const char* item) +{ + std::string ret; + std::unique_ptr nodeList(ixmlDocument_getElementsByTagName(doc, item), ixmlNodeList_free); + if (nodeList) { + // If there are several nodes which match the tag, we only want the first one. + ret = getElementText(ixmlNodeList_item(nodeList.get(), 0)); + } + return ret; +} + +static std::string +getFirstElementItem(IXML_Element* element, const char* item) +{ + std::string ret; + std::unique_ptr nodeList(ixmlElement_getElementsByTagName(element, item), ixmlNodeList_free); + if (nodeList) { + // If there are several nodes which match the tag, we only want the first one. + ret = getElementText(ixmlNodeList_item(nodeList.get(), 0)); + } + return ret; +} + +static bool +errorOnResponse(IXML_Document* doc) +{ + if (not doc) + return true; + + std::string errorCode = getFirstDocItem(doc, "errorCode"); + if (not errorCode.empty()) { + std::string errorDescription = getFirstDocItem(doc, "errorDescription"); + JAMI_WARN("PUPnP: Response contains error: %s : %s", errorCode.c_str(), errorDescription.c_str()); + return true; + } + return false; +} + +PUPnP::PUPnP() +{ + pupnpThread_ = std::thread([this] { registerClientAsync(); }); + + int upnp_err = UPNP_E_SUCCESS; + char* ip_address = nullptr; + char* ip_address6 = nullptr; + unsigned short port = 0; + unsigned short port6 = 0; + +#if UPNP_ENABLE_IPV6 + upnp_err = UpnpInit2(0, 0); + if (upnp_err != UPNP_E_SUCCESS) { + JAMI_WARN("PUPnP: UpnpInit2 Failed to initialize"); + UpnpFinish(); // Destroy threads before reusing upnp init function. + upnp_err = UpnpInit(0, 0); // Deprecated function but fall back on it if UpnpInit2 fails. + } +#else + upnp_err = UpnpInit(0, 0); // Deprecated function but fall back on it if IPv6 not enabled. +#endif + + if (upnp_err != UPNP_E_SUCCESS) { + JAMI_ERR("PUPnP: Can't initialize libupnp: %s", UpnpGetErrorMessage(upnp_err)); + UpnpFinish(); + } else { + ip_address = UpnpGetServerIpAddress(); + port = UpnpGetServerPort(); +#if UPNP_ENABLE_IPV6 + ip_address6 = UpnpGetServerIp6Address(); + port6 = UpnpGetServerPort6(); +#endif + if (ip_address6 and port6) { + JAMI_DBG("PUPnP: Initialiazed on %s:%u | %s:%u", ip_address, port, ip_address6, port6); + } else { + JAMI_DBG("PUPnP: Initialiazed on %s:%u", ip_address, port); + } + + // Relax the parser to allow malformed XML text. + ixmlRelaxParser(1); + } +} + +PUPnP::~PUPnP() +{ + // Clear all the lists. + { + std::lock_guard lk(validIgdMutex); + for(auto const &it : validIgdList_) { + if (auto igd = dynamic_cast(it.second.get())) + actionDeletePortMappingsByDesc(*igd, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION); + } + validIgdList_.clear(); + cpDeviceList_.clear(); + } + + // Notify thread to terminate. UpnpFinish function will get called. + pupnpRun_ = false; + pupnpCv_.notify_all(); + if (pupnpThread_.joinable()) { + pupnpThread_.join(); + } +} + +void +PUPnP::connectivityChanged() +{ + // Lock internal IGD list. + std::lock_guard lk(validIgdMutex); + + // Clear internal IGD list. + validIgdList_.clear(); + cpDeviceList_.clear(); +} + +void +PUPnP::searchForIGD() +{ + // Notify registerClientAsync function running in thread to execute in non-blocking fashion. + { + std::lock_guard lk(ctrlptMutex_); + pupnpRun_ = true; + } + pupnpCv_.notify_one(); +} + +Mapping +PUPnP::addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type, UPnPProtocol::UpnpError& upnp_error) +{ + upnp_error = UPnPProtocol::UpnpError::INVALID_ERR; + + Mapping mapping {port_external, port_internal, type}; + + /* check if this mapping already exists + * if the mapping is the same, then we just need to increment the number of users globally + * if the mapping is not the same, then we have to return fail, as the external port is used + * for something else + * if the mapping doesn't exist, then try to add it + */ + auto globalMappings = type == PortType::UDP ? &igd->udpMappings : &igd->tcpMappings; + auto iter = globalMappings->find(port_external); + if (iter != globalMappings->end()) { + /* mapping exists with same external port */ + GlobalMapping* mapping_ptr = &iter->second; + if (*mapping_ptr == mapping) { + /* the same mapping, so nothing needs to be done */ + upnp_error = UPnPProtocol::UpnpError::ERROR_OK; + ++(mapping_ptr->users); + JAMI_DBG("PUPnP: Mapping already exists, incrementing number of users: %d", + iter->second.users); + return mapping; + } else { + /* this port is already used by a different mapping */ + JAMI_WARN("PUPnP: Cannot add a mapping with an external port which is already used by another:\n\tcurrent: %s\n\ttrying to add: %s", + mapping_ptr->toString().c_str(), mapping.toString().c_str()); + upnp_error = UPnPProtocol::UpnpError::CONFLICT_IN_MAPPING; + return {}; + } + } + + auto pupnp_igd = dynamic_cast(igd); + if (pupnp_igd) { + if (actionAddPortMapping(*pupnp_igd, mapping, upnp_error)) { + JAMI_WARN("PUPnP: Opened port %s", mapping.toString().c_str()); + globalMappings->emplace(port_external, GlobalMapping{mapping}); + return mapping; + } + } + return {}; +} + +void +PUPnP::removeMapping(const Mapping& igdMapping) +{ + // Lock mutex to protect IGD list. + std::lock_guard lk(validIgdMutex); + + // Iterate over all IGDs in internal list and try to remove selected mapping. + for (auto const& item : validIgdList_) { + + if (not item.second) { + continue; + } + + // Get mappings of IGD depending on type. + PortMapGlobal* globalMappings; + if (igdMapping.getType() == PortType::UDP) { + globalMappings = &item.second->udpMappings; + } else { + globalMappings = &item.second->tcpMappings; + } + + // Check if the mapping we want to remove is present in the IGD. + auto mapToRemove = globalMappings->find(igdMapping.getPortExternal()); + if (mapToRemove != globalMappings->end()) { + // Check if the mapping we want to remove is the same as the one that is present. + GlobalMapping& global_mapping = mapToRemove->second; + if (igdMapping == global_mapping) { + // Check the users. + if (global_mapping.users > 1) { + // More than one user so not unique port. Decrement number of users. + --(global_mapping.users); + JAMI_DBG("PUPnP: Decrementing users of mapping %s, %d users remaining", + igdMapping.toString().c_str(), global_mapping.users); + } else { + // No other users so unique port. We can remove it compeltely. + if (auto upnp = dynamic_cast(item.second.get())) { + actionDeletePortMapping(*upnp, + igdMapping.getPortExternalStr(), + igdMapping.getTypeStr()); + } + // Remove the mapping locally. + globalMappings->erase(mapToRemove); + } + } + } + } +} + +void +PUPnP::removeAllLocalMappings(IGD* igd) +{ + if (auto igd_del_map = dynamic_cast(igd)) { + actionDeletePortMappingsByDesc(*igd_del_map, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION); + } +} + +void +PUPnP::registerClientAsync() +{ + std::unique_lock lk(ctrlptMutex_); + + while (pupnpRun_) { + + pupnpCv_.wait(lk); + + if (not clientRegistered_) { + // Register Upnp control point. + int upnp_err = UpnpRegisterClient(ctrlPtCallback, this, &ctrlptHandle_); + if (upnp_err != UPNP_E_SUCCESS) { + JAMI_ERR("PUPnP: Can't register client: %s", UpnpGetErrorMessage(upnp_err)); + } else { + clientRegistered_ = true; + } + } + + if (not pupnpRun_) { + break; + } + + if (clientRegistered_) { + // Send out search for multiple types of devices, as some routers may possibly only reply to one. + UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_ROOT_DEVICE, this); + UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_IGD_DEVICE, this); + UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANIP_SERVICE, this); + UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANPPP_SERVICE, this); + } + } + + UpnpFinish(); +} + +int +PUPnP::ctrlPtCallback(Upnp_EventType event_type, const void* event, void* user_data) +{ + if (auto pupnp = static_cast(user_data)) { + return pupnp->handleCtrlPtUPnPEvents(event_type, event); + } + JAMI_WARN("PUPnP: Control point callback without PUPnP"); + return 0; +} + +int +PUPnP::handleCtrlPtUPnPEvents(Upnp_EventType event_type, const void* event) +{ + // Lock mutex to prevent handling other discovery search results (or advertisements) simultaneously. + std::lock_guard lk(ctrlptMutex_); + + switch(event_type) + { + case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: // Fall through. Treat advertisements like discovery search results. + case UPNP_DISCOVERY_SEARCH_RESULT: + { + const UpnpDiscovery* d_event = (const UpnpDiscovery*)event; + int upnp_err; + + // First check the error code. + if (UpnpDiscovery_get_ErrCode(d_event) != UPNP_E_SUCCESS) { + break; + } + + // Check if this device ID is already in the list. + std::lock_guard lk(validIgdMutex); + if (cpDeviceList_.count(std::string(UpnpDiscovery_get_DeviceID_cstr(d_event))) > 0) { + break; + } + cpDeviceList_.emplace(std::pair(std::string(UpnpDiscovery_get_DeviceID_cstr(d_event)), "")); + + /* + * NOTE: This thing will block until success for the system socket timeout + * unless libupnp is compile with '-disable-blocking-tcp-connections', in + * which case it will block for the libupnp specified timeout. + */ + IXML_Document* doc_container_ptr = nullptr; + std::unique_ptr doc_desc_ptr(nullptr, ixmlDocument_free); + upnp_err = UpnpDownloadXmlDoc(UpnpDiscovery_get_Location_cstr(d_event), &doc_container_ptr); + if (doc_container_ptr) { + doc_desc_ptr.reset(doc_container_ptr); + } + + if (upnp_err != UPNP_E_SUCCESS or not doc_desc_ptr) { + JAMI_WARN("PUPnP: Error downloading device XML document -> %s", UpnpGetErrorMessage(upnp_err)); + break; + } + + // Check device type. + std::string deviceType = getFirstDocItem(doc_desc_ptr.get(), "deviceType"); + if (deviceType.empty()) { + // No device type. Exit. + break; + } + + if (deviceType.compare(UPNP_IGD_DEVICE) != 0) { + // Device type not IGD. Exit. + break; + } + + std::unique_ptr igd_candidate; + igd_candidate = parseIGD(doc_desc_ptr.get(), d_event); + if (not igd_candidate) { + // No valid IGD candidate. Exit. + break; + } + + JAMI_DBG("PUPnP: Validating IGD candidate.\n\tUDN: %s\n\tBase URL: %s\n\tName: %s\n\tserviceType: %s\n\tserviceID: %s\n\tcontrolURL: %s\n\teventSubURL: %s", + igd_candidate->getUDN().c_str(), + igd_candidate->getBaseURL().c_str(), + igd_candidate->getFriendlyName().c_str(), + igd_candidate->getServiceType().c_str(), + igd_candidate->getServiceId().c_str(), + igd_candidate->getControlURL().c_str(), + igd_candidate->getEventSubURL().c_str()); + + // Check if IGD is connected. + if (not actionIsIgdConnected(*igd_candidate)) { + JAMI_WARN("PUPnP: IGD candidate %s is not connected", igd_candidate->getUDN().c_str()); + break; + } + + // Validate external Ip. + igd_candidate->publicIp_ = actionGetExternalIP(*igd_candidate); + if (igd_candidate->publicIp_.toString().empty()) { + JAMI_WARN("PUPnP: IGD candidate %s has no valid external Ip", igd_candidate->getUDN().c_str()); + break; + } + + // Validate internal Ip. + igd_candidate->localIp_ = ip_utils::getLocalAddr(pj_AF_INET()); + if (igd_candidate->localIp_.toString().empty()) { + JAMI_WARN("PUPnP: No valid internal Ip."); + break; + } + + JAMI_DBG("PUPnP: Found device with external IP %s", igd_candidate->publicIp_.toString().c_str()); + + // Store public IP. + std::string publicIpStr(std::move(igd_candidate->publicIp_.toString())); + + // Store info for subscription. + std::string eventSub = igd_candidate->getEventSubURL(); + std::string udn = igd_candidate->getUDN(); + + // Remove any local mappings that may be left over from last time used. + removeAllLocalMappings(igd_candidate.get()); + + // Add the igd to the upnp context class list. + if (updateIgdListCb_(this, std::move(igd_candidate.get()), std::move(igd_candidate.get()->publicIp_), true)) { + JAMI_DBG("PUPnP: IGD with public IP %s was added to the list", publicIpStr.c_str()); + } else { + JAMI_DBG("PUPnP: IGD with public IP %s is already in the list", publicIpStr.c_str()); + } + + // Keep local IGD list internally. + if (cpDeviceList_.count(udn) > 0) { + cpDeviceList_[udn] = eventSub; + } + validIgdList_.emplace(std::move(igd_candidate->getUDN()), std::move(igd_candidate)); + + // Subscribe to IGD events. + upnp_err = UpnpSubscribeAsync(ctrlptHandle_, eventSub.c_str(), SUBSCRIBE_TIMEOUT, subEventCallback, this); + if (upnp_err != UPNP_E_SUCCESS) { + JAMI_WARN("PUPnP: Error when trying to request subscription for %s -> %s", udn.c_str(), UpnpGetErrorMessage(upnp_err)); + } + break; + } + case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: + { + const UpnpDiscovery *d_event = (const UpnpDiscovery *)event; + + std::lock_guard lk(validIgdMutex); + + // Remvoe device Id from list. + std::string cpDeviceId(UpnpDiscovery_get_DeviceID_cstr(d_event)); + cpDeviceList_.erase(cpDeviceId); + + IGD* igd_to_remove = nullptr; + for (auto it = validIgdList_.find(cpDeviceId); it != validIgdList_.end(); it++) { + + // Store igd to remove. + igd_to_remove = it->second.get(); + + // Remove IGD from context list. + updateIgdListCb_(this, igd_to_remove, igd_to_remove->publicIp_, false); + + // Remove the IGD from the itnternal list and notify the listeners. + validIgdList_.erase(std::move(it)); + break; + } + break; + } + case UPNP_DISCOVERY_SEARCH_TIMEOUT: + { + // Nothing to do here. + break; + } + case UPNP_EVENT_RECEIVED: + { + // TODO: Handle event by updating any changed state variables */ + break; + } + case UPNP_EVENT_AUTORENEWAL_FAILED: // Fall through. Treat failed autorenewal like an expired subscription. + case UPNP_EVENT_SUBSCRIPTION_EXPIRED: // This event will occur only if autorenewal is disabled. + { + const UpnpEventSubscribe *es_event = (const UpnpEventSubscribe *)event; + + std::string eventSubUrl(UpnpEventSubscribe_get_PublisherUrl_cstr(es_event)); + + std::pair foundDevice = std::make_pair("", ""); + bool foundEventSubUrl = false; + auto it = cpDeviceList_.begin(); + while(it != cpDeviceList_.end()) { + if(it->second == eventSubUrl) { + foundEventSubUrl = true; + foundDevice = std::make_pair(it->first, it->second); + break; + } + it++; + } + + if (not foundEventSubUrl) { + // If we don't find event subscription url then exit. + break; + } + + std::string udn = foundDevice.first; + std::string eventSub = foundDevice.second; + + // Renew subscriptons to IGD events. + UpnpSubscribeAsync(ctrlptHandle_, eventSub.c_str(), SUBSCRIBE_TIMEOUT, subEventCallback, this); + + break; + } + case UPNP_EVENT_SUBSCRIBE_COMPLETE: + { + break; + } + case UPNP_EVENT_UNSUBSCRIBE_COMPLETE: + { + break; + } + case UPNP_CONTROL_ACTION_COMPLETE: + { + break; + } + default: + { + JAMI_WARN("PUPnP: Unhandled Control Point event"); + break; + } + } + + return UPNP_E_SUCCESS; +} + +int +PUPnP::subEventCallback(Upnp_EventType event_type, const void* event, void* user_data) +{ + if (auto pupnp = static_cast(user_data)) { + return pupnp->handleSubscriptionUPnPEvent(event_type, event); + } + JAMI_WARN("PUPnP: Subscription callback without service Id string"); + return 0; +} + +int +PUPnP::handleSubscriptionUPnPEvent(Upnp_EventType event_type, const void* event) +{ + std::lock_guard lk(ctrlptMutex_); + + const UpnpEventSubscribe *es_event = (const UpnpEventSubscribe *)event; + + int upnp_err = UpnpEventSubscribe_get_ErrCode(es_event); + if (upnp_err != UPNP_E_SUCCESS) { + JAMI_WARN("PUPnP: Error when trying to handle subscription callback -> %s", UpnpGetErrorMessage(upnp_err)); + return upnp_err; + } + + // TODO: Handle subscription event. + + return UPNP_E_SUCCESS; +} + +std::unique_ptr +PUPnP::parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event) +{ + if (not doc or not d_event) + return nullptr; + + // Check the UDN to see if its already in our device list. + std::string UDN = getFirstDocItem(doc, "UDN"); + if (UDN.empty()) { + JAMI_WARN("PUPnP: could not find UDN in description document of device"); + return nullptr; + } else { + auto it = validIgdList_.find(UDN); + if (it != validIgdList_.end()) { + // We already have this device in our list. + return nullptr; + } + } + + std::unique_ptr new_igd; + int upnp_err; + + // Get friendly name. + std::string friendlyName = getFirstDocItem(doc, "friendlyName"); + + // Get base URL. + std::string baseURL = getFirstDocItem(doc, "URLBase"); + if (baseURL.empty()) { + baseURL = std::string(UpnpDiscovery_get_Location_cstr(d_event)); + } + + // Get list of services defined by serviceType. + std::unique_ptr serviceList(nullptr, ixmlNodeList_free); + serviceList.reset(ixmlDocument_getElementsByTagName(doc, "serviceType")); + unsigned long list_length = ixmlNodeList_length(serviceList.get()); + + // Go through the "serviceType" nodes until we find the the correct service type. + for (unsigned long node_idx = 0; node_idx < list_length; node_idx++) { + + IXML_Node* serviceType_node = ixmlNodeList_item(serviceList.get(), node_idx); + std::string serviceType = getElementText(serviceType_node); + + // Only check serviceType of WANIPConnection or WANPPPConnection. + if (serviceType != std::string(UPNP_WANIP_SERVICE) && serviceType != std::string(UPNP_WANPPP_SERVICE)) { + // IGD is not WANIP or WANPPP service. Going to next node. + continue; + } + + // Get parent node. + IXML_Node* service_node = ixmlNode_getParentNode(serviceType_node); + if (not service_node) { + // IGD serviceType has no parent node. Going to next node. + continue; + } + + // Perform sanity check. The parent node should be called "service". + if(strcmp(ixmlNode_getNodeName(service_node), "service") != 0) { + // IGD "serviceType" parent node is not called "service". Going to next node. + continue; + } + + // Get serviceId. + IXML_Element* service_element = (IXML_Element*)service_node; + std::string serviceId = getFirstElementItem(service_element, "serviceId"); + if (serviceId.empty()){ + // IGD "serviceId" is empty. Going to next node. + continue; + } + + // Get the relative controlURL and turn it into absolute address using the URLBase. + std::string controlURL = getFirstElementItem(service_element, "controlURL"); + if (controlURL.empty()) { + // IGD control URL is empty. Going to next node. + continue; + } + + char* absolute_control_url = nullptr; + upnp_err = UpnpResolveURL2(baseURL.c_str(), controlURL.c_str(), &absolute_control_url); + if (upnp_err == UPNP_E_SUCCESS) { + controlURL = absolute_control_url; + } else { + JAMI_WARN("PUPnP: Error resolving absolute controlURL -> %s", UpnpGetErrorMessage(upnp_err)); + } + std::free(absolute_control_url); + + // Get the relative eventSubURL and turn it into absolute address using the URLBase. + std::string eventSubURL = getFirstElementItem(service_element, "eventSubURL"); + if (eventSubURL.empty()) { + JAMI_WARN("PUPnP: IGD event sub URL is empty. Going to next node"); + continue; + } + + char* absolute_event_sub_url = nullptr; + upnp_err = UpnpResolveURL2(baseURL.c_str(), eventSubURL.c_str(), &absolute_event_sub_url); + if (upnp_err == UPNP_E_SUCCESS) { + eventSubURL = absolute_event_sub_url; + } else { + JAMI_WARN("PUPnP: Error resolving absolute eventSubURL -> %s", UpnpGetErrorMessage(upnp_err)); + } + std::free(absolute_event_sub_url); + + new_igd.reset(new UPnPIGD(std::move(UDN), + std::move(baseURL), + std::move(friendlyName), + std::move(serviceType), + std::move(serviceId), + std::move(controlURL), + std::move(eventSubURL))); + + return new_igd; + } + + return nullptr; +} + +bool +PUPnP::actionIsIgdConnected(const UPnPIGD& igd) +{ + if (not clientRegistered_) { + return false; + } + + // Action and response pointers. + std::unique_ptr action(nullptr, ixmlDocument_free); // Action pointer. + std::unique_ptr response(nullptr, ixmlDocument_free); // Response pointer. + IXML_Document* action_container_ptr = nullptr; + IXML_Document* response_container_ptr = nullptr; + + // Set action name. + std::string action_name { "GetStatusInfo" }; + + action_container_ptr = UpnpMakeAction(action_name.c_str(), igd.getServiceType().c_str(), 0, nullptr); + if (not action_container_ptr) { + JAMI_WARN("PUPnP: Failed to make GetStatusInfo action"); + return false; + } + action.reset(action_container_ptr); + + int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), igd.getServiceType().c_str(), nullptr, action.get(), &response_container_ptr); + if (upnp_err != UPNP_E_SUCCESS) { + JAMI_WARN("PUPnP: Failed to send GetStatusInfo action -> %s", UpnpGetErrorMessage(upnp_err)); + return false; + } + response.reset(response_container_ptr); + + if(errorOnResponse(response.get())) { + JAMI_WARN("PUPnP: Failed to get GetStatusInfo from %s -> %d: %s", igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err)); + return false; + } + + // Parse response. + std::string status = getFirstDocItem(response.get(), "NewConnectionStatus"); + if (status.compare("Connected") != 0) { + return false; + } + + return true; +} + +IpAddr +PUPnP::actionGetExternalIP(const UPnPIGD& igd) +{ + if (not clientRegistered_) { + return {}; + } + + // Action and response pointers. + std::unique_ptr action(nullptr, ixmlDocument_free); // Action pointer. + std::unique_ptr response(nullptr, ixmlDocument_free); // Response pointer. + IXML_Document* action_container_ptr = nullptr; + IXML_Document* response_container_ptr = nullptr; + + // Set action name. + std::string action_name { "GetExternalIPAddress" }; + + action_container_ptr = UpnpMakeAction(action_name.c_str(), igd.getServiceType().c_str(), 0, nullptr); + if (not action_container_ptr) { + JAMI_WARN("PUPnP: Failed to make GetExternalIPAddress action"); + return {}; + } + action.reset(action_container_ptr); + + int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), igd.getServiceType().c_str(), nullptr, action.get(), &response_container_ptr); + if (upnp_err != UPNP_E_SUCCESS) { + JAMI_WARN("PUPnP: Failed to send GetExternalIPAddress action -> %s", UpnpGetErrorMessage(upnp_err)); + return {}; + } + response.reset(response_container_ptr); + + if(errorOnResponse(response.get())) { + JAMI_WARN("PUPnP: Failed to get GetExternalIPAddress from %s -> %d: %s", igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err)); + return {}; + } + + return { getFirstDocItem(response.get(), "NewExternalIPAddress") }; +} + +void +PUPnP::actionDeletePortMappingsByDesc(const UPnPIGD& igd, const std::string& description) +{ + if (not clientRegistered_) { + return; + } + + if (!igd.localIp_) { + return; + } + + // Set action name. + std::string action_name { "GetGenericPortMappingEntry" }; + + int entry_idx = 0; + bool done = false; + + do { + // Action and resposne pointers. + std::unique_ptr action(nullptr, ixmlDocument_free); // Action pointer. + std::unique_ptr response(nullptr, ixmlDocument_free); // Response pointer. + IXML_Document* action_container_ptr = nullptr; + IXML_Document* response_container_ptr = nullptr; + + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewPortMappingIndex", std::to_string(entry_idx).c_str()); + action.reset(action_container_ptr); + + int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), igd.getServiceType().c_str(), nullptr, action.get(), &response_container_ptr); + response.reset(response_container_ptr); + if(not response and upnp_err != UPNP_E_SUCCESS) { + return; + } + + // Check error code. + std::string errorCode = getFirstDocItem(response.get(), "errorCode"); + if (not errorCode.empty()) { + + if (std::stoi(errorCode) == ARRAY_IDX_INVALID or std::stoi(errorCode) == CONFLICT_IN_MAPPING) { + // No more port mapping entries to delete. + JAMI_DBG("PUPnP: Closed all local port mappings"); + } else { + std::string errorDescription = getFirstDocItem(response.get(), "errorDescription"); + JAMI_DBG("PUPnP: GetGenericPortMappingEntry returned with error: %s: %s", + errorCode.c_str(), errorDescription.c_str()); + } + done = true; + + } else { + // Parse the rest of the response. + std::string desc_actual = getFirstDocItem(response.get(), "NewPortMappingDescription"); + std::string client_ip = getFirstDocItem(response.get(), "NewInternalClient"); + + // Check IP and description. + if (IpAddr(client_ip) == igd.localIp_ and desc_actual.compare(description) == 0) { + // Get parameters needed for port removal. + std::string port_internal = getFirstDocItem(response.get(), "NewInternalPort"); + std::string port_external = getFirstDocItem(response.get(), "NewExternalPort"); + std::string protocol = getFirstDocItem(response.get(), "NewProtocol"); + + // Attempt to delete entry. + if (not actionDeletePortMapping(igd, port_external, protocol)) { + // Failed to delete entry, skip it and try the next one. + ++entry_idx; + } + // No need to increment index if successful since the number of entries will have decreased by one. + } else { + ++entry_idx; + } + } + } while(not done); +} + +bool +PUPnP::actionDeletePortMapping(const UPnPIGD& igd, const std::string& port_external, const std::string& protocol) +{ + if (not clientRegistered_) { + return false; + } + + // Action and response pointers. + std::unique_ptr action(nullptr, ixmlDocument_free); // Action pointer. + std::unique_ptr response(nullptr, ixmlDocument_free); // Response pointer. + IXML_Document* action_container_ptr = nullptr; + IXML_Document* response_container_ptr = nullptr; + + // Set action name. + std::string action_name { "DeletePortMapping" }; + + // Set action sequence. + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewRemoteHost", ""); + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewExternalPort", port_external.c_str()); + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewProtocol", protocol.c_str()); + + action.reset(action_container_ptr); + + int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), igd.getServiceType().c_str(), nullptr, action.get(), &response_container_ptr); + if(upnp_err != UPNP_E_SUCCESS) { + JAMI_WARN("PUPnP: Failed to send %s from: %s, %d: %s", action_name.c_str(), igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err)); + return false; + } + + if (not response_container_ptr) { + JAMI_WARN("PUPnP: Failed to get response from %s", action_name.c_str()); + return false; + } + response.reset(response_container_ptr); + + // Check if there is an error code. + std::string errorCode = getFirstDocItem(response.get(), "errorCode"); + if (not errorCode.empty()) { + std::string errorDescription = getFirstDocItem(response.get(), "errorDescription"); + JAMI_WARN("PUPnP: %s returned with error: %s: %s", action_name.c_str(), errorCode.c_str(), errorDescription.c_str()); + return false; + } + + JAMI_WARN("PUPnP: Closed port %s %s", port_external.c_str(), protocol.c_str()); + + return true; +} + +bool +PUPnP::actionAddPortMapping(const UPnPIGD& igd, const Mapping& mapping, UPnPProtocol::UpnpError& error_code) +{ + if (not clientRegistered_) { + return false; + } + + error_code = UPnPProtocol::UpnpError::ERROR_OK; + + // Action and response pointers. + std::unique_ptr action(nullptr, ixmlDocument_free); // Action pointer. + std::unique_ptr response(nullptr, ixmlDocument_free); // Response pointer. + IXML_Document* action_container_ptr = nullptr; + IXML_Document* response_container_ptr = nullptr; + + // Set action name. + std::string action_name{"AddPortMapping"}; + + // Set action sequence. + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewRemoteHost", ""); + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewExternalPort", mapping.getPortExternalStr().c_str()); + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewProtocol", mapping.getTypeStr().c_str()); + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewInternalPort", mapping.getPortInternalStr().c_str()); + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewInternalClient", igd.localIp_.toString().c_str()); + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewEnabled", "1"); + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewPortMappingDescription", mapping.getDescription().c_str()); + UpnpAddToAction(&action_container_ptr, action_name.c_str(), igd.getServiceType().c_str(), "NewLeaseDuration", "0"); + + action.reset(action_container_ptr); + + int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), igd.getServiceType().c_str(), nullptr, action.get(), &response_container_ptr); + if(upnp_err != UPNP_E_SUCCESS) { + + JAMI_WARN("PUPnP: Failed to send action %s from: %s, %d: %s", action_name.c_str(), igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err)); + error_code = UPnPProtocol::UpnpError::INVALID_ERR; + return false; + } + + if (not response_container_ptr) { + JAMI_WARN("PUPnP: Failed to get response from %s", action_name.c_str()); + return false; + } + response.reset(response_container_ptr); + + // Check if there is an error code. + std::string errorCode = getFirstDocItem(response.get(), "errorCode"); + if (not errorCode.empty()) { + std::string errorDescription = getFirstDocItem(response.get(), "errorDescription"); + JAMI_WARN("PUPnP: %s returned with error: %s: %s", action_name.c_str(), errorCode.c_str(), errorDescription.c_str()); + error_code = UPnPProtocol::UpnpError::INVALID_ERR; + return false; + } + return true; +} + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/protocol/pupnp/pupnp.h b/src/upnp/protocol/pupnp/pupnp.h new file mode 100644 index 000000000..dd26c4b4f --- /dev/null +++ b/src/upnp/protocol/pupnp/pupnp.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Stepan Salenikovich + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#define UPNP_USE_MSVCPP +#define UPNP_STATIC_LIB +#endif + +#include "../upnp_protocol.h" +#include "../global_mapping.h" +#include "../igd.h" +#include "upnp_igd.h" + +#include "logger.h" +#include "ip_utils.h" +#include "noncopyable.h" +#include "compiler_intrinsics.h" + +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +#include +#include + +namespace jami { +class IpAddr; +} + +namespace jami { namespace upnp { + +// Error codes returned by router when trying to remove ports. +constexpr static int ARRAY_IDX_INVALID = 713; +constexpr static int CONFLICT_IN_MAPPING = 718; + +// Timeout values (in seconds). +constexpr static unsigned int SEARCH_TIMEOUT {30}; +constexpr static unsigned int SUBSCRIBE_TIMEOUT {300}; + +class PUPnP : public UPnPProtocol +{ +public: + PUPnP(); + ~PUPnP(); + + // Returns the protocol type. + Type getType() const override { return Type::PUPNP; } + + // Notifies a change in network. + void connectivityChanged() override; + + // Sends out async search for IGD. + void searchForIGD() override; + + // Tries to add mapping. Assumes mutex is already locked. + Mapping addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type, UPnPProtocol::UpnpError& upnp_error) override; + + // Removes a mapping. + void removeMapping(const Mapping& igdMapping) override; + + // Removes all local mappings of IGD that we're added by the application. + void removeAllLocalMappings(IGD* igd) override; + +private: + // Register client in an async manner using a thread. + void registerClientAsync(); + + // Control point callback. + static int ctrlPtCallback(Upnp_EventType event_type, const void* event, void* user_data); +#if UPNP_VERSION < 10800 + static inline int ctrlPtCallback(Upnp_EventType event_type, void* event, void* user_data) { + return ctrlPtCallback(event_type, (const void*)event, user_data); + }; +#endif + + // Callback event handler function for the UPnP client (control point). + int handleCtrlPtUPnPEvents(Upnp_EventType event_type, const void* event); + + // Subscription event callback. + static int subEventCallback(Upnp_EventType event_type, const void* event, void* user_data); +#if UPNP_VERSION < 10800 + static inline int subEventCallback(Upnp_EventType event_type, void* event, void* user_data) { + return subEventCallback(event_type, (const void*)event, user_data); + }; +#endif + + // Callback subscription event function for handling subscription request. + int handleSubscriptionUPnPEvent(Upnp_EventType event_type, const void* event); + + // Parses the IGD candidate. + std::unique_ptr parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event); + + // These functions directly create UPnP actions and make synchronous UPnP control point calls. Assumes mutex is already locked. + bool actionIsIgdConnected(const UPnPIGD& igd); + IpAddr actionGetExternalIP(const UPnPIGD& igd); + void actionDeletePortMappingsByDesc(const UPnPIGD& igd, const std::string& description); + bool actionDeletePortMapping(const UPnPIGD& igd, const std::string& port_external, const std::string& protocol); + bool actionAddPortMapping(const UPnPIGD& igd, const Mapping& mapping, UPnPProtocol::UpnpError& upnp_error); + +private: + NON_COPYABLE(PUPnP); + + std::condition_variable pupnpCv_ {}; // Condition variable for thread-safe signaling. + std::atomic_bool pupnpRun_ { true }; // Variable to allow the thread to run. + std::thread pupnpThread_ {}; // PUPnP thread for non-blocking client registration. + + std::map> validIgdList_; // Map of valid IGDs with their UDN (universal Id). + std::map cpDeviceList_; // Control point device list containing the device ID and device subscription event url. + + std::mutex ctrlptMutex_; // Mutex for client handle protection. + UpnpClient_Handle ctrlptHandle_ {-1}; // Control point handle. + std::atomic_bool clientRegistered_ { false }; // Indicates of the client is registered. +}; + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/protocol/pupnp/upnp_igd.cpp b/src/upnp/protocol/pupnp/upnp_igd.cpp new file mode 100644 index 000000000..a355ed1ce --- /dev/null +++ b/src/upnp/protocol/pupnp/upnp_igd.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Stepan Salenikovich + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "upnp_igd.h" + +namespace jami { namespace upnp { + +UPnPIGD::UPnPIGD(std::string&& UDN, std::string&& baseURL, std::string&& friendlyName, std::string&& serviceType, std::string&& serviceId, std::string&& controlURL, std::string&& eventSubURL, + IpAddr&& localIp, IpAddr&& publicIp): + IGD(std::move(localIp), std::move(publicIp)) +{ + UDN_ = std::move(UDN); + baseURL_ = std::move(baseURL); + friendlyName_ = std::move(friendlyName); + serviceType_ = std::move(serviceType); + serviceId_ = std::move(serviceId); + controlURL_ = std::move(controlURL); + eventSubURL_ = std::move(eventSubURL); +} + +bool +UPnPIGD::operator==(IGD& other) const +{ + return localIp_ == other.localIp_ and publicIp_ == other.publicIp_; +} + +bool +UPnPIGD::operator==(UPnPIGD& other) const +{ + if (localIp_ and publicIp_) { + if (localIp_ != other.localIp_ or publicIp_ != other.publicIp_) { + return false; + } + } + + return UDN_ == other.UDN_ and + baseURL_ == other.baseURL_ and + friendlyName_ == other.friendlyName_ and + serviceType_ == other.serviceType_ and + serviceId_ == other.serviceId_ and + controlURL_ == other.controlURL_ and + eventSubURL_ == other.eventSubURL_; +} + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/protocol/pupnp/upnp_igd.h b/src/upnp/protocol/pupnp/upnp_igd.h new file mode 100644 index 000000000..01638dcd1 --- /dev/null +++ b/src/upnp/protocol/pupnp/upnp_igd.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Stepan Salenikovich + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../igd.h" +#include "../global_mapping.h" + +#include "noncopyable.h" +#include "ip_utils.h" +#include "string_utils.h" + +#include +#include +#include +#include + +namespace jami { namespace upnp { + +class UPnPIGD : public IGD +{ +public: + UPnPIGD(std::string&& UDN, + std::string&& baseURL, + std::string&& friendlyName, + std::string&& serviceType, + std::string&& serviceId, + std::string&& controlURL, + std::string&& eventSubURL, + IpAddr&& localIp = {}, + IpAddr&& publicIp = {}); + ~UPnPIGD(){} + const std::string& getUDN() const { return UDN_; }; + const std::string& getBaseURL() const { return baseURL_; }; + const std::string& getFriendlyName() const { return friendlyName_; }; + const std::string& getServiceType() const { return serviceType_; }; + const std::string& getServiceId() const { return serviceId_; }; + const std::string& getControlURL() const { return controlURL_; }; + const std::string& getEventSubURL() const { return eventSubURL_; }; + + bool operator==(IGD& other) const; + bool operator==(UPnPIGD& other) const; + +private: + std::string UDN_ {}; + std::string baseURL_ {}; + std::string friendlyName_ {}; + std::string serviceType_ {}; + std::string serviceId_ {}; + std::string controlURL_ {}; + std::string eventSubURL_ {}; +}; + +}} // namespace jami::upnp diff --git a/src/upnp/protocol/upnp_protocol.h b/src/upnp/protocol/upnp_protocol.h new file mode 100644 index 000000000..cf80801e3 --- /dev/null +++ b/src/upnp/protocol/upnp_protocol.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2004-2019 Savoir-faire Linux Inc. + * + * Author: Eden Abitbol + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "igd.h" +#include "mapping.h" + +#include "logger.h" +#include "noncopyable.h" +#include "ip_utils.h" +#include "string_utils.h" + +#include +#include +#include +#include +#include + +namespace jami { namespace upnp { + +// UPnP device descriptions. +constexpr static const char * UPNP_ROOT_DEVICE = "upnp:rootdevice"; +constexpr static const char * UPNP_IGD_DEVICE = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"; +constexpr static const char * UPNP_WAN_DEVICE = "urn:schemas-upnp-org:device:WANDevice:1"; +constexpr static const char * UPNP_WANCON_DEVICE = "urn:schemas-upnp-org:device:WANConnectionDevice:1"; +constexpr static const char * UPNP_WANIP_SERVICE = "urn:schemas-upnp-org:service:WANIPConnection:1"; +constexpr static const char * UPNP_WANPPP_SERVICE = "urn:schemas-upnp-org:service:WANPPPConnection:1"; + +// Pure virtual interface class that UPnPContext uses to call protocol functions. +class UPnPProtocol +{ +public: + enum class UpnpError : int { + INVALID_ERR = -1, + ERROR_OK, + CONFLICT_IN_MAPPING + }; + + enum class Type { + UNKNOWN, + PUPNP, + NAT_PMP + }; + + using IgdListChangedCallback = std::function; + + UPnPProtocol(){}; + virtual ~UPnPProtocol(){}; + + // Allows each protocol to return it's type. + virtual Type getType() const = 0; + + // Signals a change in the network. + virtual void connectivityChanged() = 0; + + // Search for IGD. + virtual void searchForIGD() = 0; + + // Tries to add mapping. Assumes mutex is already locked. + virtual Mapping addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type, UPnPProtocol::UpnpError& upnp_error) = 0; + + // Removes a mapping. + virtual void removeMapping(const Mapping& igdMapping) = 0; + + // Removes all local mappings of IGD that we're added by the application. + virtual void removeAllLocalMappings(IGD* igd) = 0; + + // Set the IGD list callback handler. + void setOnIgdChanged(IgdListChangedCallback&& cb) { updateIgdListCb_ = std::move(cb); } + +protected: + mutable std::mutex validIgdMutex; // Mutex used to access these lists and IGDs in a thread-safe manner. + + IgdListChangedCallback updateIgdListCb_; // Callback for when the IGD list changes. +}; + +}} // namespace jami::upnp \ No newline at end of file diff --git a/src/upnp/upnp_context.cpp b/src/upnp/upnp_context.cpp index 3102ee494..8289619f3 100644 --- a/src/upnp/upnp_context.cpp +++ b/src/upnp/upnp_context.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2004-2019 Savoir-faire Linux Inc. * * Author: Stepan Salenikovich + * Author: Eden Abitbol * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,41 +19,22 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#if HAVE_LIBNATPMP -#include -#endif - -#include "logger.h" -#include "ip_utils.h" -#include "upnp_igd.h" -#include "compiler_intrinsics.h" - -#include -using random_device = dht::crypto::random_device; - -#include -#include -#include -#include -#include -#include -#include -#include // for std::free - #include "upnp_context.h" namespace jami { namespace upnp { -/** - * This should be used to get a UPnPContext. - * It only makes sense to have one unless you have separate - * contexts for multiple internet interfaces, which is not currently - * supported. - */ +static uint16_t +generateRandomPort() +{ + // Seed the generator. + static std::mt19937 gen(dht::crypto::getSeededRandomEngine()); + + // Define the range. + std::uniform_int_distribution dist(Mapping::UPNP_PORT_MIN, Mapping::UPNP_PORT_MAX); + + return dist(gen); +} + std::shared_ptr getUPnPContext() { @@ -60,1280 +42,347 @@ getUPnPContext() return context; } -/* UPnP error codes */ -constexpr static int INVALID_ARGS = 402; -constexpr static int ARRAY_IDX_INVALID = 713; -constexpr static int CONFLICT_IN_MAPPING = 718; - -/* max number of times to retry mapping if it fails due to conflict; - * there isn't much logic in picking this number... ideally not many ports should - * be mapped in a system, so a few number of random port retries should work; - * a high number of retries would indicate there might be some kind of bug or else - * incompatibility with the router; we use it to prevent an infinite loop of - * retrying to map the entry - */ -constexpr static unsigned MAX_RETRIES = 20; - -#if HAVE_LIBUPNP - -/* UPnP IGD definitions */ -constexpr static const char * UPNP_ROOT_DEVICE = "upnp:rootdevice"; -constexpr static const char * UPNP_IGD_DEVICE = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"; -constexpr static const char * UPNP_WAN_DEVICE = "urn:schemas-upnp-org:device:WANDevice:1"; -constexpr static const char * UPNP_WANCON_DEVICE = "urn:schemas-upnp-org:device:WANConnectionDevice:1"; -constexpr static const char * UPNP_WANIP_SERVICE = "urn:schemas-upnp-org:service:WANIPConnection:1"; -constexpr static const char * UPNP_WANPPP_SERVICE = "urn:schemas-upnp-org:service:WANPPPConnection:1"; - -constexpr static const char * INVALID_ARGS_STR = "402"; -constexpr static const char * ARRAY_IDX_INVALID_STR = "713"; -constexpr static const char * CONFLICT_IN_MAPPING_STR = "718"; - -/* - * Local prototypes - */ -static std::string get_element_text(IXML_Node*); -static std::string get_first_doc_item(IXML_Document*, const char*); -static std::string get_first_element_item(IXML_Element*, const char*); -static void checkResponseError(IXML_Document*); - -#else - -constexpr static int UPNP_E_SUCCESS = 0; - -#endif // HAVE_LIBUPNP - UPnPContext::UPnPContext() -#if HAVE_LIBNATPMP - : pmpThread_([this]() { - auto pmp_igd = std::make_shared(); - natpmp_t natpmp; - - while (pmpRun_) { - if (initnatpmp(&natpmp, 0, 0) < 0) { - JAMI_ERR("NAT-PMP: can't initialize libnatpmp"); - std::unique_lock lk(pmpMutex_); - pmpCv_.wait_for(lk, std::chrono::minutes(1)); - } else { - JAMI_DBG("NAT-PMP: initialized"); - break; - } - } - - while (pmpRun_) { - std::unique_lock lk(pmpMutex_); - pmpCv_.wait_until(lk, pmp_igd->getRenewalTime(), [&] { - return not pmpRun_ or pmp_igd->getRenewalTime() <= clock::now(); - }); - if (not pmpRun_) break; - - auto now = clock::now(); - - if (pmp_igd->renewal_ < now) { - PMPsearchForIGD(pmp_igd, natpmp); - } - if (pmpIGD_) { - if (pmp_igd->clearAll_) { - PMPdeleteAllPortMapping(*pmp_igd, natpmp, NATPMP_PROTOCOL_UDP); - PMPdeleteAllPortMapping(*pmp_igd, natpmp, NATPMP_PROTOCOL_TCP); - pmp_igd->clearAll_ = false; - pmp_igd->toRemove_.clear(); - } else if (not pmp_igd->toRemove_.empty()) { - decltype(pmp_igd->toRemove_) removed = std::move(pmp_igd->toRemove_); - pmp_igd->toRemove_.clear(); - lk.unlock(); - for (auto& m : removed) { - PMPaddPortMapping(*pmp_igd, natpmp, m, true); - } - lk.lock(); - } - auto mapping = pmp_igd->getNextMappingToRenew(); - if (mapping and mapping->renewal_ < now) - PMPaddPortMapping(*pmp_igd, natpmp, *mapping); - } - } - closenatpmp(&natpmp); - JAMI_DBG("NAT-PMP: ended"); - }) -#endif { + using namespace std::placeholders; +#if HAVE_LIBNATPMP + auto natPmp = std::make_unique(); + natPmp->setOnIgdChanged(std::bind(&UPnPContext::igdListChanged, this, _1, _2, _3, _4)); + natPmp->searchForIGD(); + protocolList_.push_back(std::move(natPmp)); +#endif #if HAVE_LIBUPNP - int upnp_err; - char* ip_address = nullptr; - unsigned short port = 0; - - /* TODO: allow user to specify interface to be used - * by selecting the IP - */ - - #ifdef UPNP_ENABLE_IPV6 - JAMI_DBG("UPnP: IPv6 support enabled, but we will use IPv4"); - /* IPv6 version seems to fail on some systems with: - * UPNP_E_SOCKET_BIND: An error occurred binding a socket. */ - /* TODO: figure out why ipv6 version doesn't work */ - // upnp_err = UpnpInit2(0, 0); - #endif - upnp_err = UpnpInit(0, 0); - if ( upnp_err != UPNP_E_SUCCESS ) { - JAMI_ERR("UPnP: can't initialize libupnp: %s", UpnpGetErrorMessage(upnp_err)); - UpnpFinish(); - } else { - JAMI_DBG("UPnP: using IPv4"); - ip_address = UpnpGetServerIpAddress(); // do not free, it is freed by UpnpFinish() - port = UpnpGetServerPort(); - - JAMI_DBG("UPnP: initialiazed on %s:%u", ip_address, port); - - // relax the parser to allow malformed XML text - ixmlRelaxParser( 1 ); - - // Register a control point to start looking for devices right away - upnp_err = UpnpRegisterClient( cp_callback, this, &ctrlptHandle_ ); - if ( upnp_err != UPNP_E_SUCCESS ) { - JAMI_ERR("UPnP: can't register client: %s", UpnpGetErrorMessage(upnp_err)); - UpnpFinish(); - } else { - clientRegistered_ = true; - // start gathering a list of available devices - searchForIGD(); - } - } + auto pupnp = std::make_unique(); + pupnp->setOnIgdChanged(std::bind(&UPnPContext::igdListChanged, this, _1, _2, _3, _4)); + pupnp->searchForIGD(); + protocolList_.push_back(std::move(pupnp)); #endif } UPnPContext::~UPnPContext() { - /* make sure everything is unregistered, freed, and UpnpFinish() is called */ - { - std::lock_guard lock(validIGDMutex_); - for( auto const &it : validIGDs_) { -#if HAVE_LIBUPNP - if (auto igd = dynamic_cast(it.second.get())) - removeMappingsByLocalIPAndDescription(*igd, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION); -#endif - } -#if HAVE_LIBNATPMP - if (pmpIGD_) { - { - std::lock_guard lk(pmpMutex_); - pmpIGD_->clearMappings(); - } - pmpCv_.notify_all(); - } -#endif - } - -#if HAVE_LIBNATPMP - pmpRun_ = false; - pmpCv_.notify_all(); - if (pmpThread_.joinable()) - pmpThread_.join(); - pmpIGD_.reset(); -#endif - -#if HAVE_LIBUPNP - if (clientRegistered_) - UpnpUnRegisterClient( ctrlptHandle_ ); - - if (deviceRegistered_) - UpnpUnRegisterRootDevice( deviceHandle_ ); -// FIXME : on windows thread have already been destroyed at this point resulting in a deadlock -#ifndef _WIN32 - UpnpFinish(); -#endif -#endif + igdList_.clear(); } void UPnPContext::connectivityChanged() { - { - std::lock_guard lock(validIGDMutex_); + if (not igdList_.empty()) { - /* when the network changes, we're likely no longer connected to the same IGD, or if we are - * we might now have a different IP, thus we clear the list of IGDs and notify the listeners - * so that they can attempt to re-do the port mappings once we detect an IGD - */ - validIGDs_.clear(); -#if HAVE_LIBNATPMP - if (pmpIGD_) { - std::lock_guard lk(pmpMutex_); - pmpIGD_->clear(); - pmpIGD_->renewal_ = clock::now(); - pmpIGD_.reset(); + // Clear main IGD list. + std::lock_guard lock(igdListMutex_); + igdList_.clear(); + + for (const auto& item : igdListeners_) { + item.second(); } - pmpCv_.notify_all(); -#endif - validIGDCondVar_.notify_all(); - for (const auto& l : igdListeners_) - l.second(); } -#if HAVE_LIBUPNP - // send out a new search request - searchForIGD(); -#endif + for (auto const& item : protocolList_) { + item->connectivityChanged(); + item->searchForIGD(); + } } bool -UPnPContext::hasValidIGD(std::chrono::seconds timeout) +UPnPContext::hasValidIGD() { - if (not clientRegistered_ and not pmpRun_) { - JAMI_WARN("UPnP: Control Point not registered"); - return false; - } - - std::unique_lock lock(validIGDMutex_); - if (!validIGDCondVar_.wait_for(lock, timeout, - [this]{return hasValidIGD_unlocked();})) { - JAMI_WARN("UPnP: check for valid IGD timeout"); - return false; - } - - return hasValidIGD_unlocked(); + return not igdList_.empty(); } size_t -UPnPContext::addIGDListener(IGDFoundCallback&& cb) +UPnPContext::addIGDListener(IgdFoundCallback&& cb) { - std::lock_guard lock(validIGDMutex_); + JAMI_DBG("UPnP Context: Adding IGD listener"); + + std::lock_guard lock(igdListMutex_); auto token = ++listenerToken_; igdListeners_.emplace(token, std::move(cb)); + return token; } void UPnPContext::removeIGDListener(size_t token) { - std::lock_guard lock(validIGDMutex_); - auto it = igdListeners_.find(token); - if (it != igdListeners_.end()) - igdListeners_.erase(it); -} - -bool -UPnPContext::hasValidIGD_unlocked() const -{ - return -#if HAVE_LIBNATPMP - pmpIGD_ or -#endif - not validIGDs_.empty(); -} - -/** - * chooses the IGD to use, - * assumes you already have a lock on validIGDMutex_ - */ -IGD* -UPnPContext::chooseIGD_unlocked() const -{ -#if HAVE_LIBNATPMP - if (pmpIGD_) - return pmpIGD_.get(); -#endif - if (validIGDs_.empty()) - return nullptr; - return validIGDs_.begin()->second.get(); -} - -/** - * tries to add mapping - */ -Mapping -UPnPContext::addMapping(IGD* igd, - uint16_t port_external, - uint16_t port_internal, - PortType type, - int *upnp_error) -{ - *upnp_error = -1; - - Mapping mapping{port_external, port_internal, type}; - - /* check if this mapping already exists - * if the mapping is the same, then we just need to increment the number of users globally - * if the mapping is not the same, then we have to return fail, as the external port is used - * for something else - * if the mapping doesn't exist, then try to add it - */ - auto globalMappings = type == PortType::UDP ? &igd->udpMappings : &igd->tcpMappings; - auto iter = globalMappings->find(port_external); - if (iter != globalMappings->end()) { - /* mapping exists with same external port */ - GlobalMapping* mapping_ptr = &iter->second; - if (*mapping_ptr == mapping) { - /* the same mapping, so nothing needs to be done */ - *upnp_error = UPNP_E_SUCCESS; - ++(mapping_ptr->users); - JAMI_DBG("UPnp : mapping already exists, incrementing number of users: %d", - iter->second.users); - return mapping; - } else { - /* this port is already used by a different mapping */ - JAMI_WARN("UPnP: cannot add a mapping with an external port which is already used by another:\n\tcurrent: %s\n\ttrying to add: %s", - mapping_ptr->toString().c_str(), mapping.toString().c_str()); - *upnp_error = CONFLICT_IN_MAPPING; - return {}; - } + std::lock_guard lock(igdListMutex_); + if (igdListeners_.erase(token) > 0) { + JAMI_DBG("UPnP Context: Removing igd listener"); } - - /* mapping doesn't exist, so try to add it */ - JAMI_DBG("adding port mapping : %s", mapping.toString().c_str()); - -#if HAVE_LIBUPNP - auto upnp = dynamic_cast(igd); - if (not upnp or addPortMapping(*upnp, mapping, upnp_error)) -#endif - { - /* success; add it to global list */ - globalMappings->emplace(port_external, GlobalMapping{mapping}); -#if HAVE_LIBNATPMP -#if HAVE_LIBUPNP - if (not upnp) -#endif - pmpCv_.notify_all(); -#endif - return mapping; - } - return {}; } -static uint16_t -generateRandomPort() -{ - /* obtain a random number from hardware */ - static random_device rd; - /* seed the generator */ - static std::mt19937 gen(rd()); - /* define the range */ - static std::uniform_int_distribution dist(Mapping::UPNP_PORT_MIN, Mapping::UPNP_PORT_MAX); - - return dist(gen); -} - -/** - * chooses a random port that is not yet used by the daemon for UPnP - */ uint16_t UPnPContext::chooseRandomPort(const IGD& igd, PortType type) { - auto globalMappings = type == PortType::UDP ? - &igd.udpMappings : &igd.tcpMappings; + auto globalMappings = type == PortType::UDP ? &igd.udpMappings : &igd.tcpMappings; uint16_t port = generateRandomPort(); - /* keep generating random ports until we find one which is not used */ + // Keep generating random ports until we find one which is not used. while(globalMappings->find(port) != globalMappings->end()) { port = generateRandomPort(); } - JAMI_DBG("UPnP: chose random port %u", port); - return port; } -/** - * tries to add mapping from and to the port_desired - * if unique == true, makes sure the client is not using this port already - * if the mapping fails, tries other available ports until success - * - * tries to use a random port between 1024 < > 65535 if desired port fails - * - * maps port_desired to port_local; if use_same_port == true, makes sure that - * that the external and internal ports are the same - * - * returns a valid mapping on success and an invalid mapping on failure - */ Mapping -UPnPContext::addAnyMapping(uint16_t port_desired, - uint16_t port_local, - PortType type, - bool use_same_port, - bool unique) +UPnPContext::addMapping(uint16_t port_desired, uint16_t port_local, PortType type, bool unique) { - /* get a lock on the igd list because we don't want the igd to be modified - * or removed from the list while using it */ - std::lock_guard lock(validIGDMutex_); - IGD* igd = chooseIGD_unlocked(); - if (not igd) { - JAMI_WARN("UPnP: no valid IGD available"); - return {}; - } + // Lock mutex on the igd list. + std::lock_guard igdListLock(igdListMutex_); - auto globalMappings = type == PortType::UDP ? - &igd->udpMappings : &igd->tcpMappings; - if (unique) { - /* check that port is not already used by the client */ - auto iter = globalMappings->find(port_desired); - if (iter != globalMappings->end()) { - /* port already used, we need a unique port */ - port_desired = chooseRandomPort(*igd, type); + // Add the mapping to the first valid IGD we find in the list. + IGD* igd = nullptr; + if (not igdList_.empty()) { + for (auto const& item : igdList_) { + if (item.second) { + igd = item.second; + break; + } } } - if (use_same_port) - port_local = port_desired; + if (not igd) { + JAMI_WARN("UPnPContext: no valid IGD available"); + return {}; + } - int upnp_error; - Mapping mapping = addMapping(igd, port_desired, port_local, type, &upnp_error); - /* keep trying to add the mapping as long as the upnp error is 718 == conflicting mapping - * if adding the mapping fails for any other reason, give up - * don't try more than MAX_RETRIES to prevent infinite loops - */ + // Get mapping type (UDP/TCP). + auto globalMappings = type == PortType::UDP ? &igd->udpMappings : &igd->tcpMappings; + + // If we want a unique port, we must make sure the client isn't already using the port. + if (unique) { + + bool unique_found = false; + + // Keep generating random ports until we find a unique one. + while (not unique_found) { + auto iter = globalMappings->find(port_desired); // Check if that port is not already used by the client. + if (iter != globalMappings->end()) { + port_desired = chooseRandomPort(*igd, type); // Port already used, try another one. + JAMI_DBG("Port %d is already in use. Finding another unique port...", port_desired); + } else { + unique_found = true; + } + } + } + + UPnPProtocol::UpnpError upnp_err = UPnPProtocol::UpnpError::ERROR_OK; unsigned numberRetries = 0; - while ( not mapping - and (upnp_error == CONFLICT_IN_MAPPING or upnp_error == INVALID_ARGS) - and numberRetries < MAX_RETRIES ) { - /* acceptable errors to keep trying: - * 718 : conflictin mapping - * 402 : invalid args (due to router implementation) - */ - JAMI_DBG("UPnP: mapping failed (conflicting entry? err = %d), trying with a different port.", - upnp_error); - /* TODO: make sure we don't try sellecting the same random port twice if it fails ? */ + Mapping mapping = addMapping(igd, port_desired, port_local, type, upnp_err); + + while (not mapping and + upnp_err == UPnPProtocol::UpnpError::CONFLICT_IN_MAPPING and + numberRetries < MAX_RETRIES) { + port_desired = chooseRandomPort(*igd, type); - if (use_same_port) - port_local = port_desired; - mapping = addMapping(igd, port_desired, port_local, type, &upnp_error); + + upnp_err = UPnPProtocol::UpnpError::ERROR_OK; + mapping = addMapping(igd, port_desired, port_local, type, upnp_err); ++numberRetries; } - if (not mapping and numberRetries == MAX_RETRIES) - JAMI_DBG("UPnP: could not add mapping after %u retries, giving up", MAX_RETRIES); + if (not mapping and numberRetries >= MAX_RETRIES) { + JAMI_ERR("UPnPContext: Could not add mapping after %u retries, giving up", MAX_RETRIES); + } return mapping; } -/** - * tries to remove the given mapping - */ +Mapping +UPnPContext::addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type, UPnPProtocol::UpnpError& upnp_error) +{ + // Iterate over the IGD list and call add the mapping with the corresponding protocol. + if (not igdList_.empty()) { + for (auto const& item : igdList_) { + if (item.second == igd) { + return item.first->addMapping(item.second, port_external, port_internal, type, upnp_error); + } + } + } + + return {}; +} + void UPnPContext::removeMapping(const Mapping& mapping) { - /* get a lock on the igd list because we don't want the igd to be modified - * or removed from the list while using it */ - std::lock_guard lock(validIGDMutex_); - IGD* igd = chooseIGD_unlocked(); - if (not igd) { - JAMI_WARN("UPnP: no valid IGD available"); - return; - } - - /* first make sure the mapping exists in the global list of the igd */ - auto globalMappings = mapping.getType() == PortType::UDP ? - &igd->udpMappings : &igd->tcpMappings; - - auto iter = globalMappings->find(mapping.getPortExternal()); - if ( iter != globalMappings->end() ) { - /* make sure its the same mapping */ - GlobalMapping& global_mapping = iter->second; - if (mapping == global_mapping ) { - /* now check the users */ - if (global_mapping.users > 1) { - /* more than one user, simply decrement the number */ - --(global_mapping.users); - JAMI_DBG("UPnP: decrementing users of mapping: %s, %d users remaining", - mapping.toString().c_str(), global_mapping.users); - } else { - /* no other users, can delete */ -#if HAVE_LIBUPNP - if (auto upnp = dynamic_cast(igd)) { - JAMI_DBG("UPnP: removing port mapping : %s", - mapping.toString().c_str()); - deletePortMapping(*upnp, - mapping.getPortExternalStr(), - mapping.getTypeStr()); - } -#endif -#if HAVE_LIBNATPMP - if (auto pmp = dynamic_cast(igd)) { - { - std::lock_guard lk(pmpMutex_); - pmp->toRemove_.emplace_back(std::move(global_mapping)); - } - pmpCv_.notify_all(); - } -#endif - globalMappings->erase(iter); - } - } else { - JAMI_WARN("UPnP: cannot remove mapping which doesn't match the existing one in the IGD list"); + // Remove wanted mappings from all IGDs in list. + if (not igdList_.empty()) { + for (auto const& item : igdList_) { + item.first->removeMapping(mapping); } - } else { - JAMI_WARN("UPnP: cannot remove mapping which is not in the list of existing mappings of the IGD"); } } IpAddr UPnPContext::getLocalIP() const { - /* get a lock on the igd list because we don't want the igd to be modified - * or removed from the list while using it */ - std::lock_guard lock(validIGDMutex_); + // Lock mutex on the igd list. + std::lock_guard igdListLock(igdListMutex_); - /* if its a valid igd, we must have already gotten the local ip */ - if (auto igd = chooseIGD_unlocked()) - return igd->localIp; + // Return first valid local Ip. + if (not igdList_.empty()) { + for (auto const& item : igdList_) { + if (item.second) { + return item.second->localIp_; + } + } + } - JAMI_WARN("UPnP: no valid IGD available"); + JAMI_WARN("UPnP: No valid IGD available"); return {}; } IpAddr UPnPContext::getExternalIP() const { - /* get a lock on the igd list because we don't want the igd to be modified - * or removed from the list while using it */ - std::lock_guard lock(validIGDMutex_); + // Lock mutex on the igd list. + std::lock_guard igdListLock(igdListMutex_); - /* if its a valid igd, we must have already gotten the external ip */ - if (auto igd = chooseIGD_unlocked()) - return igd->publicIp; + // Return first valid external Ip. + if (not igdList_.empty()) { + for (auto const& item : igdList_) { + if (item.second) { + return item.second->publicIp_; + } + } + } - JAMI_WARN("UPnP: no valid IGD available"); + JAMI_WARN("UPnP: No valid IGD available"); return {}; } -#if HAVE_LIBNATPMP - -void -UPnPContext::PMPsearchForIGD(const std::shared_ptr& pmp_igd, natpmp_t& natpmp) +bool +UPnPContext::isIgdInList(IGD* igd) { - if (sendpublicaddressrequest(&natpmp) < 0) { - JAMI_ERR("NAT-PMP: can't send request"); - pmp_igd->renewal_ = clock::now() + std::chrono::minutes(1); - return; - } + std::lock_guard igdListLock(igdListMutex_); - while (pmpRun_) { - natpmpresp_t response; - std::this_thread::sleep_for(std::chrono::milliseconds(2)); - auto r = readnatpmpresponseorretry(&natpmp, &response); - if (r < 0 && r != NATPMP_TRYAGAIN) { - pmp_igd->renewal_ = clock::now() + std::chrono::minutes(5); - break; - } - else if (r != NATPMP_TRYAGAIN) { - pmp_igd->localIp = ip_utils::getLocalAddr(AF_INET); - pmp_igd->publicIp = IpAddr(response.pnu.publicaddress.addr); - if (not pmpIGD_) { - JAMI_DBG("NAT-PMP: found new device"); - JAMI_DBG("NAT-PMP: got external IP: %s", pmp_igd->publicIp.toString().c_str()); - { - std::lock_guard lock(validIGDMutex_); - pmpIGD_ = pmp_igd; - validIGDCondVar_.notify_all(); - for (const auto& l : igdListeners_) - l.second(); - } - } - pmp_igd->renewal_ = clock::now() + std::chrono::minutes(1); - break; - } - } -} - -void -UPnPContext::PMPaddPortMapping(const PMPIGD& /*pmp_igd*/, natpmp_t& natpmp, GlobalMapping& mapping, bool remove) const -{ - if (sendnewportmappingrequest(&natpmp, - mapping.getType() == PortType::UDP ? NATPMP_PROTOCOL_UDP : NATPMP_PROTOCOL_TCP, - mapping.getPortInternal(), - mapping.getPortExternal(), remove ? 0 : 3600) < 0) { - JAMI_ERR("NAT-PMP: can't send port mapping request"); - mapping.renewal_ = clock::now() + std::chrono::minutes(1); - return; - } - JAMI_DBG("NAT-PMP: sent port mapping %srequest", remove ? "removal " : ""); - while (pmpRun_) { - natpmpresp_t response; - std::this_thread::sleep_for(std::chrono::milliseconds(2)); - auto r = readnatpmpresponseorretry(&natpmp, &response); - if (r < 0 && r != NATPMP_TRYAGAIN) { - JAMI_ERR("NAT-PMP: can't %sregister port mapping", remove ? "un" : ""); - break; - } - else if (r != NATPMP_TRYAGAIN) { - mapping.renewal_ = clock::now() - + std::chrono::seconds(response.pnu.newportmapping.lifetime/2); - break; - } - } -} - -void -UPnPContext::PMPdeleteAllPortMapping(const PMPIGD& /*pmp_igd*/, natpmp_t& natpmp, int proto) const -{ - if (sendnewportmappingrequest(&natpmp, proto, 0, 0, 0) < 0) { - JAMI_ERR("NAT-PMP: can't send all port mapping removal request"); - return; - } - JAMI_DBG("NAT-PMP: sent all port mapping removal request"); - while (pmpRun_) { - natpmpresp_t response; - std::this_thread::sleep_for(std::chrono::milliseconds(2)); - auto r = readnatpmpresponseorretry(&natpmp, &response); - if (r < 0 && r != NATPMP_TRYAGAIN) { - JAMI_ERR("NAT-PMP: can't remove all port mappings"); - break; - } - else if (r != NATPMP_TRYAGAIN) { - break; - } - } -} - -#endif /* HAVE_LIBNATPMP */ - - -#if HAVE_LIBUPNP - -void -UPnPContext::searchForIGD() -{ - if (not clientRegistered_) { - JAMI_WARN("UPnP: Control Point not registered"); - return; - } - - /* send out search for both types, as some routers may possibly only reply to one */ - UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_ROOT_DEVICE, this); - UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_IGD_DEVICE, this); - UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANIP_SERVICE, this); - UpnpSearchAsync(ctrlptHandle_, SEARCH_TIMEOUT, UPNP_WANPPP_SERVICE, this); -} - -/** - * Parses the device description and adds desired devices to - * relevant lists - */ -void -UPnPContext::parseDevice(IXML_Document* doc, const UpnpDiscovery* d_event) -{ - if (not doc or not d_event) - return; - - /* check to see the device type */ - std::string deviceType = get_first_doc_item(doc, "deviceType"); - if (deviceType.empty()) { - /* JAMI_DBG("UPnP: could not find deviceType in the description document of the device"); */ - return; - } - - if (deviceType.compare(UPNP_IGD_DEVICE) == 0) { - parseIGD(doc, d_event); - } - - /* TODO: check if its a ring device */ -} - -void -UPnPContext::parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event) -{ - if (not doc or not d_event) - return; - - /* check the UDN to see if its already in our device list(s) - * if it is, then update the device advertisement timeout (expiration) - */ - std::string UDN = get_first_doc_item(doc, "UDN"); - if (UDN.empty()) { - JAMI_DBG("UPnP: could not find UDN in description document of device"); - return; - } - - { - std::lock_guard lock(validIGDMutex_); - auto it = validIGDs_.find(UDN); - - if (it != validIGDs_.end()) { - /* we already have this device in our list */ - /* TODO: update expiration */ - return; + for (auto const& item : igdList_) { + if (item.second->publicIp_ == igd->publicIp_) { + return true; } } - std::unique_ptr new_igd; - int upnp_err; - - std::string friendlyName = get_first_doc_item(doc, "friendlyName"); - if (not friendlyName.empty() ) - JAMI_DBG("UPnP: checking new device of type IGD: '%s'", - friendlyName.c_str()); - - /* determine baseURL */ - std::string baseURL = get_first_doc_item(doc, "URLBase"); - if (baseURL.empty()) { - /* get it from the discovery event location */ - baseURL = std::string(UpnpDiscovery_get_Location_cstr(d_event)); - } - - /* check if its a valid IGD: - * 1. check for IGD device... already done if this function is called - * 2. check for WAN device... skip checking for this and check for the services directly - * 3. check for WANIPConnection service or WANPPPConnection service - * 4. check if connected to Internet (if not, no point in port forwarding) - * 5. check that we can get the external IP - */ - - /* get list of services defined by serviceType */ - std::unique_ptr serviceList(nullptr, ixmlNodeList_free); - serviceList.reset(ixmlDocument_getElementsByTagName(doc, "serviceType")); - - /* get list of all 'serviceType' elements */ - bool found_connected_IGD = false; - unsigned long list_length = ixmlNodeList_length(serviceList.get()); - - /* go through the 'serviceType' nodes until we find the first service of type - * WANIPConnection or WANPPPConnection which is connected to an external network */ - for (unsigned long node_idx = 0; node_idx < list_length and not found_connected_IGD; node_idx++) { - IXML_Node* serviceType_node = ixmlNodeList_item(serviceList.get(), node_idx); - std::string serviceType = get_element_text(serviceType_node); - - /* only check serviceType of WANIPConnection or WANPPPConnection */ - if (serviceType.compare(UPNP_WANIP_SERVICE) == 0 - or serviceType.compare(UPNP_WANPPP_SERVICE) == 0) { - - /* we found a correct 'serviceType', now get the parent node because - * the rest of the service definitions are siblings of 'serviceType' */ - IXML_Node* service_node = ixmlNode_getParentNode(serviceType_node); - if (service_node) { - /* perform sanity check; the parent node should be called "service" */ - if( strcmp(ixmlNode_getNodeName(service_node), "service") == 0) { - /* get the rest of the service definitions */ - - /* serviceId */ - IXML_Element* service_element = (IXML_Element*)service_node; - std::string serviceId = get_first_element_item(service_element, "serviceId"); - - /* get the relative controlURL and turn it into absolute address using the URLBase */ - std::string controlURL = get_first_element_item(service_element, "controlURL"); - if (not controlURL.empty()) { - char* absolute_url = nullptr; - upnp_err = UpnpResolveURL2(baseURL.c_str(), - controlURL.c_str(), - &absolute_url); - if (upnp_err == UPNP_E_SUCCESS) - controlURL = absolute_url; - else - JAMI_WARN("UPnP: error resolving absolute controlURL: %s", - UpnpGetErrorMessage(upnp_err)); - std::free(absolute_url); - } - - /* get the relative eventSubURL and turn it into absolute address using the URLBase */ - std::string eventSubURL = get_first_element_item(service_element, "eventSubURL"); - if (not eventSubURL.empty()) { - char* absolute_url = nullptr; - upnp_err = UpnpResolveURL2(baseURL.c_str(), - eventSubURL.c_str(), - &absolute_url); - if (upnp_err == UPNP_E_SUCCESS) - eventSubURL = absolute_url; - else - JAMI_WARN("UPnP: error resolving absolute eventSubURL: %s", - UpnpGetErrorMessage(upnp_err)); - std::free(absolute_url); - } - - /* make sure all of the services are defined - * and check if the IGD is connected to an external network */ - if (not (serviceId.empty() and controlURL.empty() and eventSubURL.empty()) ) { - /* JAMI_DBG("UPnP: got service info from device:\n\tserviceType: %s\n\tserviceID: %s\n\tcontrolURL: %s\n\teventSubURL: %s", - serviceType.c_str(), serviceId.c_str(), controlURL.c_str(), eventSubURL.c_str()); */ - new_igd.reset(new UPnPIGD( - std::move(UDN), - std::move(baseURL), - std::move(friendlyName), - std::move(serviceType), - std::move(serviceId), - std::move(controlURL), - std::move(eventSubURL))); - if (isIGDConnected(*new_igd)) { - new_igd->publicIp = getExternalIP(*new_igd); - if (new_igd->publicIp) { - JAMI_DBG("UPnP: got external IP: %s", new_igd->publicIp.toString().c_str()); - new_igd->localIp = ip_utils::getLocalAddr(pj_AF_INET()); - if (new_igd->localIp) - found_connected_IGD = true; - - } - } - } - /* TODO: subscribe to the service to get events, eg: when IP changes */ - } else - JAMI_WARN("UPnP: IGD \"serviceType\" parent node is not called \"service\"!"); - } else - JAMI_WARN("UPnP: IGD \"serviceType\" has no parent node!"); - } - } - - /* if its a valid IGD, add to list of IGDs (ideally there is only one at a time) - * subscribe to the WANIPConnection or WANPPPConnection service to receive - * updates about state changes, eg: new external IP - */ - if (found_connected_IGD) { - JAMI_DBG("UPnP: found a valid IGD: %s", new_igd->getBaseURL().c_str()); - - { - std::lock_guard lock(validIGDMutex_); - /* delete all RING mappings first */ - removeMappingsByLocalIPAndDescription(*new_igd, Mapping::UPNP_DEFAULT_MAPPING_DESCRIPTION); - validIGDs_.emplace(UDN, std::move(new_igd)); - validIGDCondVar_.notify_all(); - for (const auto& l : igdListeners_) - l.second(); - } - } -} - -static std::string -get_element_text(IXML_Node* node) -{ - std::string ret; - if (node) { - IXML_Node *textNode = ixmlNode_getFirstChild(node); - if (textNode) { - const char* value = ixmlNode_getNodeValue(textNode); - if (value) - ret = std::string(value); - } - } - return ret; -} - -static std::string -get_first_doc_item(IXML_Document* doc, const char* item) -{ - std::string ret; - - std::unique_ptr - nodeList(ixmlDocument_getElementsByTagName(doc, item), ixmlNodeList_free); - if (nodeList) { - /* if there are several nodes which match the tag, we only want the first one */ - ret = get_element_text( ixmlNodeList_item(nodeList.get(), 0) ); - } - return ret; -} - -static std::string -get_first_element_item(IXML_Element* element, const char* item) -{ - std::string ret; - - std::unique_ptr - nodeList(ixmlElement_getElementsByTagName(element, item), ixmlNodeList_free); - if (nodeList) { - /* if there are several nodes which match the tag, we only want the first one */ - ret = get_element_text( ixmlNodeList_item(nodeList.get(), 0) ); - } - return ret; -} - -int -UPnPContext::cp_callback(Upnp_EventType event_type, const void* event, void* user_data) -{ - if (auto upnpContext = static_cast(user_data)) - return upnpContext->handleUPnPEvents(event_type, event); - - JAMI_WARN("UPnP callback without UPnPContext"); - return 0; -} - -int -UPnPContext::handleUPnPEvents(Upnp_EventType event_type, const void* event) -{ - switch( event_type ) - { - case UPNP_DISCOVERY_ADVERTISEMENT_ALIVE: - /* JAMI_DBG("UPnP: CP received a discovery advertisement"); */ - case UPNP_DISCOVERY_SEARCH_RESULT: - { - const UpnpDiscovery* d_event = ( const UpnpDiscovery* )event; - std::unique_ptr desc_doc(nullptr, ixmlDocument_free); - int upnp_err; - - /* if (event_type != UPNP_DISCOVERY_ADVERTISEMENT_ALIVE) - JAMI_DBG("UPnP: CP received a discovery search result"); */ - - /* check if we are already in the process of checking this device */ - std::unique_lock lock(cpDeviceMutex_); - - /* - * Check if this device ID is already in the list. If we reach the past-the-end - * iterator of the list, it means we haven't discovered it. So we add it. - */ - std::string deviceId(UpnpDiscovery_get_DeviceID_cstr(d_event)); - auto it = cpDevices_.find(deviceId); - if (it == cpDevices_.end()) { - JAMI_DBG("PUPnP: New device ID found -> %s.", deviceId.c_str()); - cpDevices_.emplace(deviceId); - lock.unlock(); - - if (UpnpDiscovery_get_ErrCode(d_event) != UPNP_E_SUCCESS) - JAMI_WARN("UPnP: Error in discovery event received by the CP: %s", - UpnpGetErrorMessage(UpnpDiscovery_get_ErrCode(d_event))); - - /* JAMI_DBG("UPnP: Control Point received discovery event from device:\n\tid: %s\n\ttype: %s\n\tservice: %s\n\tversion: %s\n\tlocation: %s\n\tOS: %s", - d_event->DeviceId, d_event->DeviceType, d_event->ServiceType, d_event->ServiceVer, UpnpDiscovery_get_Location_cstr(d_event), d_event->Os); - */ - - /* note: this thing will block until success for the system socket timeout - * unless libupnp is compile with '-disable-blocking-tcp-connections' - * in which case it will block for the libupnp specified timeout - */ - IXML_Document* desc_doc_ptr = nullptr; - upnp_err = UpnpDownloadXmlDoc( UpnpDiscovery_get_Location_cstr(d_event), &desc_doc_ptr); - desc_doc.reset(desc_doc_ptr); - if ( upnp_err != UPNP_E_SUCCESS ) { - /* the download of the xml doc has failed; this probably happened - * because the router has UPnP disabled, but is still sending - * UPnP discovery packets - * - * JAMI_WARN("UPnP: Error downloading device description: %s", - * UpnpGetErrorMessage(upnp_err)); - */ - } else { - parseDevice(desc_doc.get(), d_event); - } - } - } - break; - - case UPNP_DISCOVERY_ADVERTISEMENT_BYEBYE: - { - const UpnpDiscovery *d_event = (const UpnpDiscovery *)event; - - JAMI_DBG("UPnP: Control Point received ByeBye for device: %s", - UpnpDiscovery_get_DeviceID_cstr(d_event)); - - if (UpnpDiscovery_get_ErrCode(d_event) != UPNP_E_SUCCESS) - JAMI_WARN("UPnP: Error in ByeBye received by the CP: %s", - UpnpGetErrorMessage(UpnpDiscovery_get_ErrCode(d_event))); - - std::lock_guard lock(cpDeviceMutex_); - std::string deviceId(UpnpDiscovery_get_DeviceID_cstr(d_event)); - cpDevices_.erase(deviceId); - - std::lock_guard igd_list_lock(validIGDMutex_); - for (auto const& item : validIGDs_) { - if (((UPnPIGD*)item.second.get())->getUDN() == deviceId) { - validIGDs_.erase(item.first); - break; - } - } - - } - break; - - case UPNP_EVENT_RECEIVED: - { - /* struct Upnp_Event *e_event UNUSED = (struct Upnp_Event *)event; */ - - /* JAMI_DBG("UPnP: Control Point event received"); */ - - /* TODO: handle event by updating any changed state variables */ - - } - break; - - case UPNP_EVENT_AUTORENEWAL_FAILED: - { - JAMI_WARN("UPnP: Control Point subscription auto-renewal failed"); - } - break; - - case UPNP_EVENT_SUBSCRIPTION_EXPIRED: - { - JAMI_DBG("UPnP: Control Point subscription expired"); - } - break; - - case UPNP_EVENT_SUBSCRIBE_COMPLETE: - /* JAMI_DBG("UPnP: Control Point async subscription complete"); */ - - /* TODO: check if successful */ - - break; - - case UPNP_DISCOVERY_SEARCH_TIMEOUT: - /* this event will occur whether or not a valid IGD has been found; - * it just indicates the search timeout has been reached - * - * JAMI_DBG("UPnP: Control Point search timeout"); - */ - break; - - case UPNP_CONTROL_ACTION_COMPLETE: - { - const UpnpActionComplete *a_event = (const UpnpActionComplete *)event; - - /* JAMI_DBG("UPnP: Control Point async action complete"); */ - - if (UpnpActionComplete_get_ErrCode(a_event) != UPNP_E_SUCCESS) - JAMI_WARN("UPnP: Error in action complete event: %s", - UpnpGetErrorMessage(UpnpActionComplete_get_ErrCode(a_event))); - - /* TODO: no need for any processing here, just print out results. - * Service state table updates are handled by events. */ - } - break; - - case UPNP_CONTROL_GET_VAR_COMPLETE: - { - const UpnpStateVarComplete *sv_event = (const UpnpStateVarComplete *)event; - - /* JAMI_DBG("UPnP: Control Point async get variable complete"); */ - - if (UpnpStateVarComplete_get_ErrCode(sv_event) != UPNP_E_SUCCESS) - JAMI_WARN("UPnP: Error in get variable complete event: %s", - UpnpGetErrorMessage(UpnpStateVarComplete_get_ErrCode(sv_event))); - - /* TODO: update state variables */ - } - break; - - default: - JAMI_WARN("UPnP: unhandled Control Point event"); - break; - } - - return UPNP_E_SUCCESS; /* return value currently ignored by SDK */ -} - -static void -checkResponseError(IXML_Document* doc) -{ - if (not doc) - return; - - std::string errorCode = get_first_doc_item(doc, "errorCode"); - if (not errorCode.empty()) { - std::string errorDescription = get_first_doc_item(doc, "errorDescription"); - JAMI_WARN("UPnP: response contains error: %s : %s", - errorCode.c_str(), errorDescription.c_str()); - } + return false; } bool -UPnPContext::isIGDConnected(const UPnPIGD& igd) +UPnPContext::isIgdInList(IpAddr publicIpAddr) { - bool connected = false; - std::unique_ptr action(nullptr, ixmlDocument_free); - action.reset(UpnpMakeAction("GetStatusInfo", igd.getServiceType().c_str(), 0, nullptr)); + std::lock_guard igdListLock(igdListMutex_); - std::unique_ptr response(nullptr, ixmlDocument_free); - IXML_Document* response_ptr = nullptr; - int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), - igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr); - response.reset(response_ptr); - checkResponseError(response.get()); - if( upnp_err != UPNP_E_SUCCESS) { - /* TODO: if failed, should we chck if the igd is disconnected? */ - JAMI_WARN("UPnP: Failed to get GetStatusInfo from: %s, %d: %s", - igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err)); - - return false; - } - - /* parse response */ - std::string status = get_first_doc_item(response.get(), "NewConnectionStatus"); - if (status.compare("Connected") == 0) - connected = true; - - /* response should also contain the following elements, but we don't care for now: - * "NewLastConnectionError" - * "NewUptime" - */ - return connected; -} - -IpAddr -UPnPContext::getExternalIP(const UPnPIGD& igd) -{ - std::unique_ptr action(nullptr, ixmlDocument_free); - action.reset(UpnpMakeAction("GetExternalIPAddress", igd.getServiceType().c_str(), 0, nullptr)); - - std::unique_ptr response(nullptr, ixmlDocument_free); - IXML_Document* response_ptr = nullptr; - int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), - igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr); - response.reset(response_ptr); - checkResponseError(response.get()); - if( upnp_err != UPNP_E_SUCCESS) { - /* TODO: if failed, should we chck if the igd is disconnected? */ - JAMI_WARN("UPnP: Failed to get GetExternalIPAddress from: %s, %d: %s", - igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err)); - return {}; - } - - /* parse response */ - return {get_first_doc_item(response.get(), "NewExternalIPAddress")}; -} - -void -UPnPContext::removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, const std::string& description) -{ - if (!igd.localIp) { - JAMI_DBG("UPnP: cannot determine local IP in function removeMappingsByLocalIPAndDescription()"); - return; - } - - JAMI_DBG("UPnP: removing all port mappings with description: \"%s\" and local ip: %s", - description.c_str(), igd.localIp.toString().c_str()); - - int entry_idx = 0; - bool done = false; - - do { - std::unique_ptr action(nullptr, ixmlDocument_free); - IXML_Document* action_ptr = nullptr; - UpnpAddToAction(&action_ptr, "GetGenericPortMappingEntry", igd.getServiceType().c_str(), - "NewPortMappingIndex", std::to_string(entry_idx).c_str()); - action.reset(action_ptr); - - std::unique_ptr response(nullptr, ixmlDocument_free); - IXML_Document* response_ptr = nullptr; - int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), - igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr); - response.reset(response_ptr); - if( not response and upnp_err != UPNP_E_SUCCESS) { - /* TODO: if failed, should we chck if the igd is disconnected? */ - JAMI_WARN("UPnP: Failed to get GetGenericPortMappingEntry from: %s, %d: %s", - igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err)); - return; + for (auto const& item : igdList_) { + if (item.second->publicIp_) { + if (item.second->publicIp_ == publicIpAddr) { + return true; + } } + } - /* check if there is an error code */ - std::string errorCode = get_first_doc_item(response.get(), "errorCode"); + return false; +} - if (errorCode.empty()) { - /* no error, prase the rest of the response */ - std::string desc_actual = get_first_doc_item(response.get(), "NewPortMappingDescription"); - std::string client_ip = get_first_doc_item(response.get(), "NewInternalClient"); +UPnPProtocol::Type +UPnPContext::getIgdProtocol(IGD* igd) +{ + std::lock_guard igdListLock(igdListMutex_); - /* check if same IP and description */ - if (IpAddr(client_ip) == igd.localIp and desc_actual.compare(description) == 0) { - /* get the rest of the needed parameters */ - std::string port_internal = get_first_doc_item(response.get(), "NewInternalPort"); - std::string port_external = get_first_doc_item(response.get(), "NewExternalPort"); - std::string protocol = get_first_doc_item(response.get(), "NewProtocol"); + for (auto const& item : igdList_) { + if (item.second->publicIp_ == igd->publicIp_) { + return item.first->getType(); + } + } - JAMI_DBG("UPnP: deleting entry with matching desciption and ip:\n\t%s %5s->%s:%-5s '%s'", - protocol.c_str(), port_external.c_str(), client_ip.c_str(), port_internal.c_str(), desc_actual.c_str()); + return UPnPProtocol::Type::UNKNOWN; +} - /* delete entry */ - if (not deletePortMapping(igd, port_external, protocol)) { - /* failed to delete entry, skip it and try the next one */ - ++entry_idx; - } - /* note: in the case that the entry deletion is successful, we do not increment the entry - * idx as the number of entries has decreased by one */ - } else - ++entry_idx; - - } else if (errorCode.compare(ARRAY_IDX_INVALID_STR) == 0 - or errorCode.compare(INVALID_ARGS_STR) == 0) { - /* 713 means there are no more entires, but some routers will return 402 instead */ - done = true; +bool +UPnPContext::igdListChanged(UPnPProtocol* protocol, IGD* igd, IpAddr publicIpAddr, bool added) +{ + if (added) { + return addIgdToList(protocol, igd); + } else { + if (publicIpAddr) { + return removeIgdFromList(publicIpAddr); } else { - std::string errorDescription = get_first_doc_item(response.get(), "errorDescription"); - JAMI_WARN("UPnP: GetGenericPortMappingEntry returned with error: %s: %s", - errorCode.c_str(), errorDescription.c_str()); - done = true; + return removeIgdFromList(igd); } - } while(not done); + } } bool -UPnPContext::deletePortMapping(const UPnPIGD& igd, const std::string& port_external, const std::string& protocol) +UPnPContext::addIgdToList(UPnPProtocol* protocol, IGD* igd) { - std::string action_name{"DeletePortMapping"}; - std::unique_ptr action(nullptr, ixmlDocument_free); - IXML_Document* action_ptr = nullptr; - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewRemoteHost", ""); - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewExternalPort", port_external.c_str()); - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewProtocol", protocol.c_str()); - action.reset(action_ptr); + // Check if IGD has a valid public IP. + if (not igd->publicIp_.isValid(igd->publicIp_.toString().c_str())) { + JAMI_WARN("UPnPContext: IGD trying to be added has invalid public IpAddress"); + return false; + } - std::unique_ptr response(nullptr, ixmlDocument_free); - IXML_Document* response_ptr = nullptr; - int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), - igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr); - response.reset(response_ptr); - if( upnp_err != UPNP_E_SUCCESS) { - /* TODO: if failed, should we check if the igd is disconnected? */ - JAMI_WARN("UPnP: Failed to get %s from: %s, %d: %s", action_name.c_str(), - igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err)); - return false; + if (isIgdInList(igd)) { + // If the protocol of the IGD that is already in the list isn't NatPmp, then swap. + if (getIgdProtocol(igd) != UPnPProtocol::Type::NAT_PMP and + protocol->getType() == UPnPProtocol::Type::NAT_PMP) { + JAMI_WARN("UPnPContext: Attempting to swap IGD UPnP protocol"); + if (!removeIgdFromList(igd)) { + JAMI_WARN("UPnPContext: Failed to swap IGD UPnP protocol"); + return false; + } + } else { + return false; + } } - /* check if there is an error code */ - std::string errorCode = get_first_doc_item(response.get(), "errorCode"); - if (not errorCode.empty()) { - std::string errorDescription = get_first_doc_item(response.get(), "errorDescription"); - JAMI_WARN("UPnP: %s returned with error: %s: %s", - action_name.c_str(), errorCode.c_str(), errorDescription.c_str()); - return false; + + IpAddr publicIp = igd->publicIp_; + igdList_.emplace_back(std::make_pair(protocol, igd)); + + for (const auto& item : igdListeners_) { + item.second(); } + return true; } bool -UPnPContext::addPortMapping(const UPnPIGD& igd, const Mapping& mapping, int* error_code) +UPnPContext::removeIgdFromList(IGD* igd) { - *error_code = UPNP_E_SUCCESS; + std::lock_guard igdListLock(igdListMutex_); - std::string action_name{"AddPortMapping"}; - std::unique_ptr action(nullptr, ixmlDocument_free); - IXML_Document* action_ptr = nullptr; - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewRemoteHost", ""); - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewExternalPort", mapping.getPortExternalStr().c_str()); - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewProtocol", mapping.getTypeStr().c_str()); - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewInternalPort", mapping.getPortInternalStr().c_str()); - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewInternalClient", igd.localIp.toString().c_str()); - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewEnabled", "1"); - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewPortMappingDescription", mapping.getDescription().c_str()); - /* for now assume lease duration is always infinite */ - UpnpAddToAction(&action_ptr, action_name.c_str(), igd.getServiceType().c_str(), - "NewLeaseDuration", "0"); - action.reset(action_ptr); - - std::unique_ptr response(nullptr, ixmlDocument_free); - IXML_Document* response_ptr = nullptr; - int upnp_err = UpnpSendAction(ctrlptHandle_, igd.getControlURL().c_str(), - igd.getServiceType().c_str(), nullptr, action.get(), &response_ptr); - response.reset(response_ptr); - if( not response and upnp_err != UPNP_E_SUCCESS) { - /* TODO: if failed, should we chck if the igd is disconnected? */ - JAMI_WARN("UPnP: Failed to %s from: %s, %d: %s", action_name.c_str(), - igd.getServiceType().c_str(), upnp_err, UpnpGetErrorMessage(upnp_err)); - *error_code = -1; /* make sure to -1 since we didn't get a response */ - return false; + std::list>::iterator it = igdList_.begin(); + while (it != igdList_.end()) { + if (it->second->publicIp_ == igd->publicIp_) { + JAMI_WARN("UPnPContext: IGD with public IP %s was removed from the list", it->second->publicIp_.toString().c_str()); + igdList_.erase(it); + return true; + } else { + it++; + } } - /* check if there is an error code */ - std::string errorCode = get_first_doc_item(response.get(), "errorCode"); - if (not errorCode.empty()) { - std::string errorDescription = get_first_doc_item(response.get(), "errorDescription"); - JAMI_WARN("UPnP: %s returned with error: %s: %s", - action_name.c_str(), errorCode.c_str(), errorDescription.c_str()); - *error_code = jami::stoi(errorCode); - return false; - } - return true; + return false; } -#endif /* HAVE_LIBUPNP */ +bool +UPnPContext::removeIgdFromList(IpAddr publicIpAddr) +{ + std::lock_guard igdListLock(igdListMutex_); + + std::list>::iterator it = igdList_.begin(); + while (it != igdList_.end()) { + if (it->second->publicIp_ == publicIpAddr) { + JAMI_WARN("UPnPContext: IGD with public IP %s was removed from the list", it->second->publicIp_.toString().c_str()); + igdList_.erase(it); + return true; + } else { + it++; + } + } + + return false; + +} }} // namespace jami::upnp diff --git a/src/upnp/upnp_context.h b/src/upnp/upnp_context.h index 82e33abd0..5a052a3dc 100644 --- a/src/upnp/upnp_context.h +++ b/src/upnp/upnp_context.h @@ -2,6 +2,7 @@ * Copyright (C) 2004-2019 Savoir-faire Linux Inc. * * Author: Stepan Salenikovich + * Author: Eden Abitbol * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,31 +25,39 @@ #include "config.h" #endif -#if HAVE_LIBUPNP -#ifdef _WIN32 -#define UPNP_STATIC_LIB -#include -#include -#endif -#include -#include -#endif - +#include "protocol/upnp_protocol.h" #if HAVE_LIBNATPMP -#include +#include "protocol/natpmp/nat_pmp.h" #endif +#if HAVE_LIBUPNP +#include "protocol/pupnp/pupnp.h" +#endif +#include "protocol/igd.h" +#include "protocol/global_mapping.h" +#include "logger.h" +#include "ip_utils.h" #include "noncopyable.h" -#include "upnp_igd.h" + +#include #include #include +#include #include #include -#include +#include #include +#include #include #include +#include +#include +#include + +using random_device = dht::crypto::random_device; + +using IgdFoundCallback = std::function; namespace jami { class IpAddr; @@ -56,210 +65,79 @@ class IpAddr; namespace jami { namespace upnp { -class UPnPContext { +class UPnPContext +{ public: - constexpr static unsigned SEARCH_TIMEOUT {30}; - UPnPContext(); ~UPnPContext(); - /** - * Returns 'true' if there is at least one valid (connected) IGD. - * @param timeout Time to wait until a valid IGD is found. - * If timeout is not given or 0, the function pool (non-blocking). - */ - bool hasValidIGD(std::chrono::seconds timeout = {}); + // Check if there is a valid IGD in the IGD list. + bool hasValidIGD(); - size_t addIGDListener(IGDFoundCallback&& cb); + // Add IGD listener. + size_t addIGDListener(IgdFoundCallback&& cb); + + // Remove IGD listener. void removeIGDListener(size_t token); - /** - * tries to add mapping from and to the port_desired - * if unique == true, makes sure the client is not using this port already - * if the mapping fails, tries other available ports until success - * - * tries to use a random port between 1024 < > 65535 if desired port fails - * - * maps port_desired to port_local; if use_same_port == true, makes sure - * that the external and internal ports are the same - * - * returns a valid mapping on success and an invalid mapping on failure - */ - Mapping addAnyMapping(uint16_t port_desired, - uint16_t port_local, - PortType type, - bool use_same_port, - bool unique); + // Tries to add a valid mapping. Will return it if successful. + Mapping addMapping(uint16_t port_desired, uint16_t port_local, PortType type, bool unique); - /** - * tries to remove the given mapping - */ + // Removes a mapping. void removeMapping(const Mapping& mapping); - /** - * tries to get the external ip of the router - */ + // Get external Ip of a chosen IGD. IpAddr getExternalIP() const; - - /** - * get our local ip - */ + // Get our local Ip. IpAddr getLocalIP() const; - /** - * Inform the UPnP context that the network status has changed. This clears the list of known - * IGDs - */ + // Inform the UPnP context that the network status has changed. This clears the list of known void connectivityChanged(); + // Tries to add or remove IGD to the list via callback. + bool igdListChanged(UPnPProtocol* protocol, IGD* igd, const IpAddr publicIpAddr, bool added); + + // Tries to add IGD to the list by getting it's public Ip address internally. + bool addIgdToList(UPnPProtocol* protocol, IGD* igd); + + // Removes IGD from list by specifiying the IGD itself. + bool removeIgdFromList(IGD* igd); + + // Removes IGD from list by specifiying the IGD's public Ip address. + bool removeIgdFromList(IpAddr publicIpAddr); + + // Tries to add mapping. Assumes mutex is already locked. + Mapping addMapping(IGD* igd, uint16_t port_external, uint16_t port_internal, PortType type, UPnPProtocol::UpnpError& upnp_error); + +private: + // Checks if the IGD is in the list by checking the IGD itself. + bool isIgdInList(IGD* igd); + + // Checks if the IGD is in the list by checking the IGD's public Ip. + bool isIgdInList(IpAddr publicIpAddr); + + // Returns the protocol of the IGD. + UPnPProtocol::Type getIgdProtocol(IGD* igd); + + // Returns a random port that is not yet used by the daemon for UPnP. + uint16_t chooseRandomPort(const IGD& igd, PortType type); + +public: + constexpr static unsigned MAX_RETRIES = 20; + private: NON_COPYABLE(UPnPContext); - std::atomic_bool clientRegistered_ {false}; + std::map igdListeners_; // Map of valid IGD listeners with their tokens. + size_t listenerToken_{ 0 }; // Last provided token for valid IGD listeners (0 is the invalid token). - /** - * map of valid IGDs - IGDs which have the correct services and are connected - * to some external network (have an external IP) - * - * the UDN string is used to uniquely identify the IGD - * - * the mutex is used to access these lists and IGDs in a thread-safe manner - */ - std::map> validIGDs_; - mutable std::mutex validIGDMutex_; - std::condition_variable validIGDCondVar_; - - /** - * Map of valid IGD listeners. - */ - std::map igdListeners_; - - /** - * Last provided token for valid IGD listeners. - * 0 is the invalid token. - */ - size_t listenerToken_ {0}; - - /** - * chooses the IGD to use (currently selects the first one in the map) - * assumes you already have a lock on igd_mutex_ - */ - IGD* chooseIGD_unlocked() const; - bool hasValidIGD_unlocked() const; - - /* tries to add mapping, assumes you already have lock on igd_mutex_ */ - Mapping addMapping(IGD* igd, - uint16_t port_external, - uint16_t port_internal, - PortType type, - int *upnp_error); - - uint16_t chooseRandomPort(const IGD& igd, PortType type); - -#if HAVE_LIBNATPMP - std::mutex pmpMutex_ {}; - std::condition_variable pmpCv_ {}; - std::shared_ptr pmpIGD_ {}; - std::atomic_bool pmpRun_ {true}; - std::thread pmpThread_ {}; - - void PMPsearchForIGD(const std::shared_ptr& pmp_igd, natpmp_t& natpmp); - void PMPaddPortMapping(const PMPIGD& pmp_igd, natpmp_t& natpmp, GlobalMapping& mapping, bool remove=false) const; - void PMPdeleteAllPortMapping(const PMPIGD& pmp_igd, natpmp_t& natpmp, int proto) const; -#else - static constexpr bool pmpRun_ {false}; -#endif - -#if HAVE_LIBUPNP - - /** - * UPnP devices typically send out several discovery - * packets at the same time. libupnp creates a separate event - * for each discovery packet which is processed in the threadpool, - * even if the multiple discovery packets are received from the - * same IP at the same time. In order to prevent trying - * to download and parse the device description from the - * same location in multiple threads at the same time, we - * keep track from which URL(s) we are in the process of downloading - * and parsing the device description in this set. - * - * The main purspose of this is to prevent blocking multiple - * threads when trying to download the description from an - * unresponsive device (the timeout can be several seconds) - * - * The mutex is to access the set in a thread safe manner - */ - - std::set cpDevices_; - std::mutex cpDeviceMutex_; - - /** - * control and device handles; - * set by the SDK once each is registered - */ - UpnpClient_Handle ctrlptHandle_ {-1}; - UpnpDevice_Handle deviceHandle_ {-1}; - - /** - * keep track if we've successfully registered a device - */ - bool deviceRegistered_ {false}; - - static int cp_callback(Upnp_EventType event_type, const void* event, void* user_data); - -#if UPNP_VERSION < 10800 - static inline int cp_callback(Upnp_EventType event_type, void* event, void* user_data) { - return cp_callback(event_type, (const void*)event, user_data); - }; -#endif - - /** - * callback function for the UPnP client (control point) - * all UPnP events received by the client are processed here - */ - int handleUPnPEvents(Upnp_EventType event_type, const void* event); - - - /* sends out async search for IGD */ - void searchForIGD(); - - /** - * Parses the device description and adds desired devices to - * relevant lists - */ - void parseDevice(IXML_Document* doc, const UpnpDiscovery* d_event); - - void parseIGD(IXML_Document* doc, const UpnpDiscovery* d_event); - - - /* these functions directly create UPnP actions - * and make synchronous UPnP control point calls - * they assume you have a lock on the igd_mutex_ */ - bool isIGDConnected(const UPnPIGD& igd); - - IpAddr getExternalIP(const UPnPIGD& igd); - - void removeMappingsByLocalIPAndDescription(const UPnPIGD& igd, - const std::string& description); - - bool deletePortMapping(const UPnPIGD& igd, - const std::string& port_external, - const std::string& protocol); - - bool addPortMapping(const UPnPIGD& igd, - const Mapping& mapping, - int* error_code); -#endif /* HAVE_LIBUPNP */ + std::vector> protocolList_; // Vector of available protocols. + std::list> igdList_; // List of IGDs with their corresponding public IPs. + mutable std::mutex igdListMutex_; // Mutex used to access these lists and IGDs in a thread-safe manner. }; -/** - * This should be used to get a UPnPContext. - * It only makes sense to have one unless you have separate - * contexts for multiple internet interfaces, which is not currently - * supported. - */ std::shared_ptr getUPnPContext(); }} // namespace jami::upnp diff --git a/src/upnp/upnp_control.cpp b/src/upnp/upnp_control.cpp index 3d8ff7fb2..3b7e1e21d 100644 --- a/src/upnp/upnp_control.cpp +++ b/src/upnp/upnp_control.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2004-2019 Savoir-faire Linux Inc. * * Author: Stepan Salenikovich + * Author: Eden Abitbol * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,19 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - #include "upnp_control.h" -#include - -#include "logger.h" -#include "ip_utils.h" -#include "upnp_context.h" -#include "upnp_igd.h" - namespace jami { namespace upnp { Controller::Controller() @@ -44,47 +34,50 @@ Controller::Controller() Controller::~Controller() { - /* remove all mappings */ removeMappings(); - if (listToken_ and upnpContext_) + + if (listToken_ and upnpContext_) { upnpContext_->removeIGDListener(listToken_); + } } bool -Controller::hasValidIGD(std::chrono::seconds timeout) +Controller::hasValidIGD() { - return upnpContext_ and upnpContext_->hasValidIGD(timeout); + return upnpContext_ and upnpContext_->hasValidIGD(); } void -Controller::setIGDListener(IGDFoundCallback&& cb) +Controller::setIGDListener(IgdFoundCallback&& cb) { - if (not upnpContext_) + if (not upnpContext_) { return; - if (listToken_) + } + + if (listToken_) { upnpContext_->removeIGDListener(listToken_); + } + listToken_ = cb ? upnpContext_->addIGDListener(std::move(cb)) : 0; } bool -Controller::addAnyMapping(uint16_t port_desired, - uint16_t port_local, - PortType type, - bool use_same_port, - bool unique, - uint16_t *port_used) +Controller::addMapping(uint16_t port_desired, PortType type, bool unique, uint16_t* port_used, uint16_t port_local) { - if (not upnpContext_) + if (not upnpContext_) { return false; + } - Mapping mapping = upnpContext_->addAnyMapping(port_desired, port_local, type, - use_same_port, unique); + if (port_local == 0) { + port_local = port_desired; + } + + Mapping mapping = upnpContext_->addMapping(port_desired, port_local, type, unique); if (mapping) { auto usedPort = mapping.getPortExternal(); - if (port_used) + if (port_used) { *port_used = usedPort; - - /* add to map */ + } auto& instanceMappings = type == PortType::UDP ? udpMappings_ : tcpMappings_; instanceMappings.emplace(usedPort, std::move(mapping)); return true; @@ -92,23 +85,15 @@ Controller::addAnyMapping(uint16_t port_desired, return false; } -bool -Controller::addAnyMapping(uint16_t port_desired, - PortType type, - bool unique, - uint16_t *port_used) -{ - return addAnyMapping(port_desired, port_desired, type, true, unique, - port_used); -} - void Controller::removeMappings(PortType type) { - if (not upnpContext_) + + if (not upnpContext_) { return; + } auto& instanceMappings = type == PortType::UDP ? udpMappings_ : tcpMappings_; - for (auto iter = instanceMappings.begin(); iter != instanceMappings.end(); ){ + for (auto iter = instanceMappings.begin(); iter != instanceMappings.end();) { auto& mapping = iter->second; upnpContext_->removeMapping(mapping); iter = instanceMappings.erase(iter); @@ -125,17 +110,19 @@ Controller::removeMappings() IpAddr Controller::getLocalIP() const { - if (upnpContext_) + if (upnpContext_) { return upnpContext_->getLocalIP(); - return {}; // empty address + } + return {}; } IpAddr Controller::getExternalIP() const { - if (upnpContext_) + if (upnpContext_) { return upnpContext_->getExternalIP(); - return {}; // empty address + } + return {}; } }} // namespace jami::upnp diff --git a/src/upnp/upnp_control.h b/src/upnp/upnp_control.h index c232dc423..f3813156f 100644 --- a/src/upnp/upnp_control.h +++ b/src/upnp/upnp_control.h @@ -2,6 +2,7 @@ * Copyright (C) 2004-2019 Savoir-faire Linux Inc. * * Author: Stepan Salenikovich + * Author: Eden Abitbol * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,18 +19,21 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef UPNP_H_ -#define UPNP_H_ +#pragma once #ifdef HAVE_CONFIG_H #include "config.h" #endif -#include -#include +#include "upnp_context.h" +#include "protocol/global_mapping.h" #include "noncopyable.h" -#include "upnp_igd.h" +#include "logger.h" +#include "ip_utils.h" + +#include +#include namespace jami { class IpAddr; @@ -39,92 +43,41 @@ namespace jami { namespace upnp { class UPnPContext; -class Controller { +class Controller +{ public: - /* constructor */ Controller(); - /* destructor */ ~Controller(); - /** - * Return whether or not this controller has a valid IGD. - * @param timeout Time to wait until a valid IGD is found. - * If timeout is not given or 0, the function pool (non-blocking). - */ - bool hasValidIGD(std::chrono::seconds timeout = {}); + // Checks if a valid IGD is available. + bool hasValidIGD(); - /** - * Set or clear a listener for valid IGDs. - * For simplicity there is one listener per controller. - */ - void setIGDListener(IGDFoundCallback&& cb = {}); + // Sets or clears a listener for valid IGDs. There is one listener per controller. + void setIGDListener(IgdFoundCallback&& cb = {}); - /** - * tries to add mapping from and to the port_desired - * if unique == true, makes sure the client is not using this port already - * if the mapping fails, tries other available ports until success - * - * tries to use a random port between 1024 < > 65535 if desired port fails - * - * maps port_desired to port_local; if use_same_port == true, makes sure that - * that the extranl and internal ports are the same - */ - bool addAnyMapping(uint16_t port_desired, - uint16_t port_local, - PortType type, - bool use_same_port, - bool unique, - uint16_t *port_used); + // Adds a mapping. Gives option to use unique port (i.e. not one that is already in use). + bool addMapping(uint16_t port_desired, PortType type, bool unique, uint16_t* port_used, uint16_t port_local = 0); - /** - * addAnyMapping with the local port being the same as the external port - */ - bool addAnyMapping(uint16_t port_desired, - PortType type, - bool unique, - uint16_t *port_used); - - /** - * removes all mappings added by this instance - */ + // Removes all mappings added by this instance. void removeMappings(); - /** - * tries to get the external ip of the IGD (router) - */ + // Gets the external ip of the first valid IGD in the list. IpAddr getExternalIP() const; - /** - * tries to get the local ip of the IGD (router) - */ + // Gets the local ip that interface with the first valid IGD in the list. IpAddr getLocalIP() const; private: - - /** - * All UPnP commands require an initialized upnpContext - */ - std::shared_ptr upnpContext_; - - /** - * list of mappings created by this instance - * the key is the external port number, as there can only be one mapping - * at a time for each external port - */ - PortMapLocal udpMappings_; - PortMapLocal tcpMappings_; - - /** - * IGD listener token - */ - size_t listToken_ {0}; - - /** - * Try to remove all mappings of the given type - */ + // Removes all mappings of the given type. void removeMappings(PortType type); + +private: + std::shared_ptr upnpContext_; // Context from which the controller executes the wanted commands. + + PortMapLocal udpMappings_; // List of UDP mappings created by this instance. + PortMapLocal tcpMappings_; // List of TCP mappings created by this instance. + + size_t listToken_ {0}; // IGD listener token. }; }} // namespace jami::upnp - -#endif /* UPNP_H_ */ diff --git a/src/upnp/upnp_igd.h b/src/upnp/upnp_igd.h deleted file mode 100644 index b8ca95d73..000000000 --- a/src/upnp/upnp_igd.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2004-2019 Savoir-faire Linux Inc. - * - * Author: Stepan Salenikovich - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#pragma once - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include -#include -#include - -#include "noncopyable.h" -#include "ip_utils.h" -#include "string_utils.h" - -namespace jami { namespace upnp { - -enum class PortType {UDP,TCP}; - -/* defines a UPnP port mapping */ -class Mapping { -public: - constexpr static const char * UPNP_DEFAULT_MAPPING_DESCRIPTION = "RING"; - /* TODO: what should the port range really be? - * Should it be the ephemeral ports as defined by the system? - */ - constexpr static uint16_t UPNP_PORT_MIN = 1024; - constexpr static uint16_t UPNP_PORT_MAX = 65535; - - Mapping( - uint16_t port_external = 0, - uint16_t port_internal = 0, - PortType type = PortType::UDP, - const std::string& description = UPNP_DEFAULT_MAPPING_DESCRIPTION) - : port_external_(port_external) - , port_internal_(port_internal) - , type_(type) - , description_(description) - {}; - - /* move constructor and operator */ - Mapping(Mapping&&) noexcept; - Mapping& operator=(Mapping&&) noexcept; - - ~Mapping() = default; - - friend bool operator== (const Mapping& cRedir1, const Mapping& cRedir2); - friend bool operator!= (const Mapping& cRedir1, const Mapping& cRedir2); - - uint16_t getPortExternal() const { return port_external_; } - std::string getPortExternalStr() const { return std::to_string(port_external_); } - uint16_t getPortInternal() const { return port_internal_; } - std::string getPortInternalStr() const { return std::to_string(port_internal_); } - PortType getType() const { return type_; } - std::string getTypeStr() const { return type_ == PortType::UDP ? "UDP" : "TCP"; } - std::string getDescription() const { return description_; } - - std::string toString() const { - return getPortExternalStr() + ":" + getPortInternalStr() + ", " + getTypeStr(); - }; - - bool isValid() const { - return port_external_ == 0 or port_internal_ == 0 ? false : true; - }; - - inline explicit operator bool() const { - return isValid(); - } - -#if HAVE_LIBNATPMP - std::chrono::system_clock::time_point renewal_ {std::chrono::system_clock::time_point::min()}; - bool remove {false}; -#endif - -private: - NON_COPYABLE(Mapping); - -protected: - uint16_t port_external_; - uint16_t port_internal_; - PortType type_; /* UPD or TCP */ - std::string description_; -}; - -/** - * GlobalMapping is like a mapping, but it tracks the number of global users, - * ie: the number of upnp:Controller which are using this mapping - * this is usually only relevant for accounts (not calls) as multiple SIP accounts - * can use the same SIP port and we don't want to delete a mapping from the router - * if other accounts are using it - */ -class GlobalMapping : public Mapping { -public: - /* number of users of this mapping; - * this is only relevant when multiple accounts are using the same SIP port */ - unsigned users; - GlobalMapping(const Mapping& mapping, unsigned users = 1) - : Mapping(mapping.getPortExternal() - , mapping.getPortInternal() - , mapping.getType() - , mapping.getDescription()) - , users(users) - {}; -}; - -/* subclasses to make it easier to differentiate and cast maps of port mappings */ -class PortMapLocal : public std::map {}; -class PortMapGlobal : public std::map {}; - -using IGDFoundCallback = std::function; - -/* defines a UPnP capable Internet Gateway Device (a router) */ -class IGD { -public: - /* device address seen by IGD */ - IpAddr localIp; - - /* external IP of IGD; can change */ - IpAddr publicIp; - - /* port mappings associated with this IGD */ - PortMapGlobal udpMappings; - PortMapGlobal tcpMappings; - - /* constructors */ - IGD() {} - - /* move constructor and operator */ - IGD(IGD&&) = default; - IGD& operator=(IGD&&) = default; - - virtual ~IGD() = default; - -private: - NON_COPYABLE(IGD); -}; - -#if HAVE_LIBUPNP - -class UPnPIGD : public IGD { -public: - UPnPIGD(std::string&& UDN, - std::string&& baseURL, - std::string&& friendlyName, - std::string&& serviceType, - std::string&& serviceId, - std::string&& controlURL, - std::string&& eventSubURL) - : UDN_(std::move(UDN)) - , baseURL_(std::move(baseURL)) - , friendlyName_(std::move(friendlyName)) - , serviceType_(std::move(serviceType)) - , serviceId_(std::move(serviceId)) - , controlURL_(std::move(controlURL)) - , eventSubURL_(std::move(eventSubURL)) - {} - - const std::string& getUDN() const { return UDN_; }; - const std::string& getBaseURL() const { return baseURL_; }; - const std::string& getFriendlyName() const { return friendlyName_; }; - const std::string& getServiceType() const { return serviceType_; }; - const std::string& getServiceId() const { return serviceId_; }; - const std::string& getControlURL() const { return controlURL_; }; - const std::string& getEventSubURL() const { return eventSubURL_; }; - -private: - /* root device info */ - std::string UDN_ {}; /* used to uniquely identify this UPnP device */ - std::string baseURL_ {}; - std::string friendlyName_ {}; - - /* port forwarding service info */ - std::string serviceType_ {}; - std::string serviceId_ {}; - std::string controlURL_ {}; - std::string eventSubURL_ {}; -}; - -#endif - -#if HAVE_LIBNATPMP - -using clock = std::chrono::system_clock; -using time_point = clock::time_point; - -class PMPIGD : public IGD { -public: - void clear() { - toRemove_.clear(); - udpMappings.clear(); - tcpMappings.clear(); - } - - void clearMappings() { - clear(); - clearAll_ = true; - } - - GlobalMapping* getNextMappingToRenew() const { - const GlobalMapping* mapping {nullptr}; - for (const auto& m : udpMappings) - if (!mapping or m.second.renewal_ < mapping->renewal_) - mapping = &m.second; - for (const auto& m : tcpMappings) - if (!mapping or m.second.renewal_ < mapping->renewal_) - mapping = &m.second; - return (GlobalMapping*)mapping; - } - - time_point getRenewalTime() const { - const auto next = getNextMappingToRenew(); - auto nextTime = std::min(renewal_, next ? next->renewal_ : time_point::max()); - return toRemove_.empty() ? nextTime : std::min(nextTime, time_point::min()); - } - - time_point renewal_ {time_point::min()}; - std::vector toRemove_ {}; - bool clearAll_ {false}; -}; - -#endif - -}} // namespace jami::upnp