Browse Source

Fix UPnP for Win32

* find_package for headers
* Swap includes order to pass compilation with MSVC 2013
* Enforce SO address resolution checks
* Change SO/DLL name on Windows
* Portable sleep from C++11

This closes #186
pull/198/head
Mikhail Titov 10 years ago
parent
commit
046ffd8648
  1. 142
      UPnP.cpp
  2. 2
      UPnP.h
  3. 15
      build/CMakeLists.txt
  4. 25
      build/cmake_modules/FindMiniUPnPc.cmake

142
UPnP.cpp

@ -2,15 +2,19 @@
#include <string> #include <string>
#include <thread> #include <thread>
#ifdef _WIN32
#include <windows.h>
#endif
#include <boost/thread/thread.hpp> #include <boost/thread/thread.hpp>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#ifdef _WIN32
#include <windows.h>
#define dlsym GetProcAddress
#else
#include <dlfcn.h>
#endif
#include "Log.h" #include "Log.h"
#include "RouterContext.h" #include "RouterContext.h"
#include "UPnP.h" #include "UPnP.h"
#include "NetDb.h" #include "NetDb.h"
@ -18,24 +22,36 @@
#include <miniupnpc/miniupnpc.h> #include <miniupnpc/miniupnpc.h>
#include <miniupnpc/upnpcommands.h> #include <miniupnpc/upnpcommands.h>
#include <dlfcn.h>
// These are per-process and are safe to reuse for all threads
#ifndef UPNPDISCOVER_SUCCESS #ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */ /* miniupnpc 1.5 */
typedef UPNPDev* (*upnp_upnpDiscoverFunc) (int, const char *, const char *, int); UPNPDev* (*upnpDiscoverFunc) (int, const char *, const char *, int);
typedef int (*upnp_UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *, int (*UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *,
const char *, const char *, const char *, const char *); const char *, const char *, const char *, const char *);
#else #else
/* miniupnpc 1.6 */ /* miniupnpc 1.6 */
typedef UPNPDev* (*upnp_upnpDiscoverFunc) (int, const char *, const char *, int, int, int *); UPNPDev* (*upnpDiscoverFunc) (int, const char *, const char *, int, int, int *);
typedef int (*upnp_UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *, int (*UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *,
const char *, const char *, const char *, const char *, const char *); const char *, const char *, const char *, const char *, const char *);
#endif #endif
typedef int (*upnp_UPNP_GetValidIGDFunc) (struct UPNPDev *, struct UPNPUrls *, struct IGDdatas *, char *, int); int (*UPNP_GetValidIGDFunc) (struct UPNPDev *, struct UPNPUrls *, struct IGDdatas *, char *, int);
typedef int (*upnp_UPNP_GetExternalIPAddressFunc) (const char *, const char *, char *); int (*UPNP_GetExternalIPAddressFunc) (const char *, const char *, char *);
typedef int (*upnp_UPNP_DeletePortMappingFunc) (const char *, const char *, const char *, const char *, const char *); int (*UPNP_DeletePortMappingFunc) (const char *, const char *, const char *, const char *, const char *);
typedef void (*upnp_freeUPNPDevlistFunc) (struct UPNPDev *); void (*freeUPNPDevlistFunc) (struct UPNPDev *);
typedef void (*upnp_FreeUPNPUrlsFunc) (struct UPNPUrls *); void (*FreeUPNPUrlsFunc) (struct UPNPUrls *);
// Nice approach http://stackoverflow.com/a/21517513/673826
template<typename F>
F GetKnownProcAddressImpl(HMODULE hmod, const char *name, F) {
auto proc = reinterpret_cast<F>(dlsym(hmod, name));
if (!proc) {
LogPrint("Error resolving ", name, " from UPNP library. This often happens if there is version mismatch!");
}
return proc;
}
#define GetKnownProcAddress(hmod, func) GetKnownProcAddressImpl(hmod, #func, func##Func);
namespace i2p namespace i2p
{ {
@ -57,6 +73,33 @@ namespace transport
void UPnP::Start() void UPnP::Start()
{ {
if (!m_IsModuleLoaded) {
#ifdef MAC_OSX
m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY);
#elif _WIN32
m_Module = LoadLibrary ("miniupnpc.dll"); // official prebuilt binary, e.g., in upnpc-exe-win32-20140422.zip
#else
m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY);
#endif
if (m_Module == NULL)
{
LogPrint ("Error loading UPNP library. This often happens if there is version mismatch!");
return;
}
else
{
upnpDiscoverFunc = GetKnownProcAddress (m_Module, upnpDiscover);
UPNP_GetValidIGDFunc = GetKnownProcAddress (m_Module, UPNP_GetValidIGD);
UPNP_GetExternalIPAddressFunc = GetKnownProcAddress (m_Module, UPNP_GetExternalIPAddress);
UPNP_AddPortMappingFunc = GetKnownProcAddress (m_Module, UPNP_AddPortMapping);
UPNP_DeletePortMappingFunc = GetKnownProcAddress (m_Module, UPNP_DeletePortMapping);
freeUPNPDevlistFunc = GetKnownProcAddress (m_Module, freeUPNPDevlist);
FreeUPNPUrlsFunc = GetKnownProcAddress (m_Module, FreeUPNPUrls);
if (upnpDiscoverFunc && UPNP_GetValidIGDFunc && UPNP_GetExternalIPAddressFunc && UPNP_AddPortMappingFunc &&
UPNP_DeletePortMappingFunc && freeUPNPDevlistFunc && FreeUPNPUrlsFunc)
m_IsModuleLoaded = true;
}
}
m_Thread = new std::thread (std::bind (&UPnP::Run, this)); m_Thread = new std::thread (std::bind (&UPnP::Run, this));
} }
@ -66,33 +109,6 @@ namespace transport
void UPnP::Run () void UPnP::Run ()
{ {
#ifdef MAC_OSX
m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY);
#elif _WIN32
m_Module = LoadLibrary ("libminiupnpc.dll");
if (m_Module == NULL)
{
LogPrint ("Error loading UPNP library. This often happens if there is version mismatch!");
return;
}
else
{
m_IsModuleLoaded = true;
}
#else
m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY);
#endif
#ifndef _WIN32
if (!m_Module)
{
LogPrint ("no UPnP module available (", dlerror (), ")");
return;
}
else
{
m_IsModuleLoaded = true;
}
#endif
for (auto& address : context.GetRouterInfo ().GetAddresses ()) for (auto& address : context.GetRouterInfo ().GetAddresses ())
{ {
if (!address.host.is_v6 ()) if (!address.host.is_v6 ())
@ -112,18 +128,6 @@ namespace transport
void UPnP::Discover () void UPnP::Discover ()
{ {
const char *error;
#ifdef _WIN32
upnp_upnpDiscoverFunc upnpDiscoverFunc = (upnp_upnpDiscoverFunc) GetProcAddress (m_Module, "upnpDiscover");
#else
upnp_upnpDiscoverFunc upnpDiscoverFunc = (upnp_upnpDiscoverFunc) dlsym (m_Module, "upnpDiscover");
// reinterpret_cast<upnp_upnpDiscoverFunc> (dlsym(...));
if ( (error = dlerror ()))
{
LogPrint ("Error loading UPNP library. This often happens if there is version mismatch!");
return;
}
#endif // _WIN32
#ifndef UPNPDISCOVER_SUCCESS #ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */ /* miniupnpc 1.5 */
m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0); m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0);
@ -134,15 +138,9 @@ namespace transport
#endif #endif
int r; int r;
#ifdef _WIN32 r = UPNP_GetValidIGDFunc (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
upnp_UPNP_GetValidIGDFunc UPNP_GetValidIGDFunc = (upnp_UPNP_GetValidIGDFunc) GetProcAddress (m_Module, "UPNP_GetValidIGD");
#else
upnp_UPNP_GetValidIGDFunc UPNP_GetValidIGDFunc = (upnp_UPNP_GetValidIGDFunc) dlsym (m_Module, "UPNP_GetValidIGD");
#endif
r = (*UPNP_GetValidIGDFunc) (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr));
if (r == 1) if (r == 1)
{ {
upnp_UPNP_GetExternalIPAddressFunc UPNP_GetExternalIPAddressFunc = (upnp_UPNP_GetExternalIPAddressFunc) dlsym (m_Module, "UPNP_GetExternalIPAddress");
r = UPNP_GetExternalIPAddressFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress); r = UPNP_GetExternalIPAddressFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_externalIPAddress);
if(r != UPNPCOMMAND_SUCCESS) if(r != UPNPCOMMAND_SUCCESS)
{ {
@ -182,11 +180,6 @@ namespace transport
std::string strDesc = "I2Pd"; std::string strDesc = "I2Pd";
try { try {
for (;;) { for (;;) {
#ifdef _WIN32
upnp_UPNP_AddPortMappingFunc UPNP_AddPortMappingFunc = (upnp_UPNP_AddPortMappingFunc) GetProcAddress (m_Module, "UPNP_AddPortMapping");
#else
upnp_UPNP_AddPortMappingFunc UPNP_AddPortMappingFunc = (upnp_UPNP_AddPortMappingFunc) dlsym (m_Module, "UPNP_AddPortMapping");
#endif
#ifndef UPNPDISCOVER_SUCCESS #ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */ /* miniupnpc 1.5 */
r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0); r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strPort.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0);
@ -204,7 +197,9 @@ namespace transport
LogPrint ("UPnP Port Mapping successful. (", m_NetworkAddr ,":", strPort.c_str(), " type ", strType.c_str () ," -> ", m_externalIPAddress ,":", strPort.c_str() ,")"); LogPrint ("UPnP Port Mapping successful. (", m_NetworkAddr ,":", strPort.c_str(), " type ", strType.c_str () ," -> ", m_externalIPAddress ,":", strPort.c_str() ,")");
return; return;
} }
sleep(20*60); std::this_thread::sleep_for(std::chrono::minutes(20)); // c++11
//boost::this_thread::sleep_for(); // pre c++11
//sleep(20*60); // non-portable
} }
} }
catch (boost::thread_interrupted) catch (boost::thread_interrupted)
@ -228,29 +223,14 @@ namespace transport
strType = "UDP"; strType = "UDP";
} }
int r = 0; int r = 0;
#ifdef _WIN32
upnp_UPNP_DeletePortMappingFunc UPNP_DeletePortMappingFunc = (upnp_UPNP_DeletePortMappingFunc) GetProcAddress (m_Module, "UPNP_DeletePortMapping");
#else
upnp_UPNP_DeletePortMappingFunc UPNP_DeletePortMappingFunc = (upnp_UPNP_DeletePortMappingFunc) dlsym (m_Module, "UPNP_DeletePortMapping");
#endif
r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0); r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, strPort.c_str (), strType.c_str (), 0);
LogPrint ("UPNP_DeletePortMapping() returned : ", r, "\n"); LogPrint ("UPNP_DeletePortMapping() returned : ", r, "\n");
} }
void UPnP::Close () void UPnP::Close ()
{ {
#ifdef _WIN32
upnp_freeUPNPDevlistFunc freeUPNPDevlistFunc = (upnp_freeUPNPDevlistFunc) GetProcAddress (m_Module, "freeUPNPDevlist");
#else
upnp_freeUPNPDevlistFunc freeUPNPDevlistFunc = (upnp_freeUPNPDevlistFunc) dlsym (m_Module, "freeUPNPDevlist");
#endif
freeUPNPDevlistFunc (m_Devlist); freeUPNPDevlistFunc (m_Devlist);
m_Devlist = 0; m_Devlist = 0;
#ifdef _WIN32
upnp_FreeUPNPUrlsFunc FreeUPNPUrlsFunc = (upnp_FreeUPNPUrlsFunc) GetProcAddress (m_Module, "FreeUPNPUrlsFunc");
#else
upnp_FreeUPNPUrlsFunc FreeUPNPUrlsFunc = (upnp_FreeUPNPUrlsFunc) dlsym (m_Module, "FreeUPNPUrlsFunc");
#endif
FreeUPNPUrlsFunc (&m_upnpUrls); FreeUPNPUrlsFunc (&m_upnpUrls);
#ifndef _WIN32 #ifndef _WIN32
dlclose (m_Module); dlclose (m_Module);

2
UPnP.h

@ -52,7 +52,7 @@ namespace transport
#ifndef _WIN32 #ifndef _WIN32
void *m_Module; void *m_Module;
#else #else
HINSTANCE *m_Module; HINSTANCE m_Module;
#endif #endif
}; };
} }

15
build/CMakeLists.txt

@ -7,6 +7,7 @@ option(WITH_HARDENING "Use hardening compiler flags" OFF)
option(WITH_LIBRARY "Build library" ON) option(WITH_LIBRARY "Build library" ON)
option(WITH_BINARY "Build binary" ON) option(WITH_BINARY "Build binary" ON)
option(WITH_STATIC "Static build" OFF) option(WITH_STATIC "Static build" OFF)
option(WITH_UPNP "Include support for UPnP client" OFF)
# paths # paths
set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" ) set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" )
@ -43,6 +44,7 @@ set (COMMON_SRC
"${CMAKE_SOURCE_DIR}/util.cpp" "${CMAKE_SOURCE_DIR}/util.cpp"
"${CMAKE_SOURCE_DIR}/Datagram.cpp" "${CMAKE_SOURCE_DIR}/Datagram.cpp"
"${CMAKE_SOURCE_DIR}/Signature.cpp" "${CMAKE_SOURCE_DIR}/Signature.cpp"
"${CMAKE_SOURCE_DIR}/UPnP.cpp"
) )
if (CMAKE_SYSTEM_NAME STREQUAL "Windows") if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
@ -62,10 +64,13 @@ set (DAEMON_SRC
"${CMAKE_SOURCE_DIR}/I2PTunnel.cpp" "${CMAKE_SOURCE_DIR}/I2PTunnel.cpp"
"${CMAKE_SOURCE_DIR}/SAM.cpp" "${CMAKE_SOURCE_DIR}/SAM.cpp"
"${CMAKE_SOURCE_DIR}/SOCKS.cpp" "${CMAKE_SOURCE_DIR}/SOCKS.cpp"
"${CMAKE_SOURCE_DIR}/UPnP.cpp"
"${CMAKE_SOURCE_DIR}/i2p.cpp" "${CMAKE_SOURCE_DIR}/i2p.cpp"
) )
if (WITH_UPNP)
add_definitions(-DUSE_UPNP)
endif ()
set (LIBRARY_SRC set (LIBRARY_SRC
"${CMAKE_SOURCE_DIR}/api.cpp" "${CMAKE_SOURCE_DIR}/api.cpp"
) )
@ -137,7 +142,7 @@ if (WITH_STATIC)
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
endif () endif ()
find_package ( Boost COMPONENTS system filesystem regex program_options date_time REQUIRED ) find_package ( Boost COMPONENTS system filesystem regex program_options date_time thread chrono REQUIRED )
if(NOT DEFINED Boost_INCLUDE_DIRS) if(NOT DEFINED Boost_INCLUDE_DIRS)
message(SEND_ERROR "Boost is not found, or your boost version was bellow 1.46. Please download Boost!") message(SEND_ERROR "Boost is not found, or your boost version was bellow 1.46. Please download Boost!")
endif() endif()
@ -147,6 +152,11 @@ if(NOT DEFINED CRYPTO++_INCLUDE_DIR)
message(SEND_ERROR "Could not find Crypto++. Please download and install it first!") message(SEND_ERROR "Could not find Crypto++. Please download and install it first!")
endif() endif()
find_package ( MiniUPnPc )
if (NOT ${MINIUPNPC_FOUND})
set(WITH_UPNP OFF)
endif()
# load includes # load includes
include_directories( ${Boost_INCLUDE_DIRS} ${CRYPTO++_INCLUDE_DIR} "${CMAKE_SOURCE_DIR}/..") include_directories( ${Boost_INCLUDE_DIRS} ${CRYPTO++_INCLUDE_DIR} "${CMAKE_SOURCE_DIR}/..")
@ -163,6 +173,7 @@ message(STATUS " HARDENING : ${WITH_HARDENING}")
message(STATUS " LIBRARY : ${WITH_LIBRARY}") message(STATUS " LIBRARY : ${WITH_LIBRARY}")
message(STATUS " BINARY : ${WITH_BINARY}") message(STATUS " BINARY : ${WITH_BINARY}")
message(STATUS " STATIC BUILD : ${WITH_STATIC}") message(STATUS " STATIC BUILD : ${WITH_STATIC}")
message(STATUS " UPnP : ${WITH_UPNP}")
message(STATUS "---------------------------------------") message(STATUS "---------------------------------------")
#Handle paths nicely #Handle paths nicely

25
build/cmake_modules/FindMiniUPnPc.cmake

@ -0,0 +1,25 @@
# - Find MINIUPNPC
if(MINIUPNPC_INCLUDE_DIR)
set(MINIUPNPC_FOUND TRUE)
else()
find_path(MINIUPNPC_INCLUDE_DIR miniupnpc.h
/usr/include/miniupnpc
/usr/local/include/miniupnpc
/opt/local/include/miniupnpc
$ENV{SystemDrive}/miniupnpc
${PROJECT_SOURCE_DIR}/../../miniupnpc
)
if(MINIUPNPC_INCLUDE_DIR)
set(MINIUPNPC_FOUND TRUE)
message(STATUS "Found MiniUPnP headers: ${MINIUPNPC_INCLUDE_DIR}")
else()
set(MINIUPNPC_FOUND FALSE)
message(STATUS "MiniUPnP not found.")
endif()
mark_as_advanced(MINIUPNPC_INCLUDE_DIR)
endif()
Loading…
Cancel
Save