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