2015-01-03 23:38:48 +03:00
# ifdef USE_UPNP
2014-02-10 00:15:47 +04:00
# include <string>
2015-01-03 23:38:48 +03:00
# include <thread>
# include <boost/thread/thread.hpp>
# include <boost/asio.hpp>
2014-02-10 00:15:47 +04:00
# include <boost/bind.hpp>
2015-01-03 23:38:48 +03:00
2015-06-06 21:53:22 +03:00
# ifdef _WIN32
# include <windows.h>
# define dlsym GetProcAddress
# else
# include <dlfcn.h>
# endif
2014-02-10 00:15:47 +04:00
# include "Log.h"
2015-06-06 21:53:22 +03:00
2015-01-03 23:38:48 +03:00
# include "RouterContext.h"
2014-02-10 00:15:47 +04:00
# include "UPnP.h"
2015-01-03 23:38:48 +03:00
# include "NetDb.h"
# include "util.h"
2016-03-09 09:42:58 +03:00
# include "RouterInfo.h"
2015-01-03 23:38:48 +03:00
# include <miniupnpc/miniupnpc.h>
# include <miniupnpc/upnpcommands.h>
2015-06-06 21:53:22 +03:00
// These are per-process and are safe to reuse for all threads
2016-03-09 09:29:32 +03:00
decltype ( upnpDiscover ) * upnpDiscoverFunc ;
decltype ( UPNP_AddPortMapping ) * UPNP_AddPortMappingFunc ;
decltype ( UPNP_GetValidIGD ) * UPNP_GetValidIGDFunc ;
decltype ( UPNP_GetExternalIPAddress ) * UPNP_GetExternalIPAddressFunc ;
decltype ( UPNP_DeletePortMapping ) * UPNP_DeletePortMappingFunc ;
decltype ( freeUPNPDevlist ) * freeUPNPDevlistFunc ;
decltype ( FreeUPNPUrls ) * FreeUPNPUrlsFunc ;
2015-06-06 21:53:22 +03:00
// Nice approach http://stackoverflow.com/a/21517513/673826
2015-06-20 08:49:24 +03:00
template < class M , typename F >
F GetKnownProcAddressImpl ( M hmod , const char * name , F ) {
2015-06-06 21:53:22 +03:00
auto proc = reinterpret_cast < F > ( dlsym ( hmod , name ) ) ;
if ( ! proc ) {
2015-12-18 09:35:39 +03:00
LogPrint ( eLogError , " UPnP: Error resolving " , name , " from library, version mismatch? " ) ;
2015-06-06 21:53:22 +03:00
}
return proc ;
}
# define GetKnownProcAddress(hmod, func) GetKnownProcAddressImpl(hmod, #func, func##Func);
2014-02-10 00:15:47 +04:00
namespace i2p
{
2015-05-06 19:19:20 +03:00
namespace transport
2015-01-03 23:38:48 +03:00
{
UPnP : : UPnP ( ) : m_Thread ( nullptr ) , m_IsModuleLoaded ( false )
{
}
void UPnP : : Stop ( )
{
if ( m_Thread )
{
m_Thread - > join ( ) ;
delete m_Thread ;
m_Thread = nullptr ;
}
}
void UPnP : : Start ( )
{
2015-06-06 21:53:22 +03:00
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 )
{
2015-12-18 09:35:39 +03:00
LogPrint ( eLogError , " UPnP: Error loading UPNP library, version mismatch? " ) ;
2015-06-06 21:53:22 +03:00
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 ;
}
}
2015-01-03 23:38:48 +03:00
m_Thread = new std : : thread ( std : : bind ( & UPnP : : Run , this ) ) ;
}
UPnP : : ~ UPnP ( )
{
}
void UPnP : : Run ( )
{
2016-03-09 09:42:58 +03:00
std : : vector < data : : RouterInfo : : Address > a = context . GetRouterInfo ( ) . GetAddresses ( ) ;
for ( auto & address : a )
2015-01-03 23:38:48 +03:00
{
if ( ! address . host . is_v6 ( ) )
{
Discover ( ) ;
if ( address . transportStyle = = data : : RouterInfo : : eTransportSSU )
{
2015-05-06 19:19:20 +03:00
TryPortMapping ( I2P_UPNP_UDP , address . port ) ;
2015-01-03 23:38:48 +03:00
}
else if ( address . transportStyle = = data : : RouterInfo : : eTransportNTCP )
{
2015-05-06 19:19:20 +03:00
TryPortMapping ( I2P_UPNP_TCP , address . port ) ;
2015-01-03 23:38:48 +03:00
}
}
}
}
void UPnP : : Discover ( )
{
# ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */
m_Devlist = upnpDiscoverFunc ( 2000 , m_MulticastIf , m_Minissdpdpath , 0 ) ;
# else
/* miniupnpc 1.6 */
int nerror = 0 ;
2016-03-09 09:29:32 +03:00
# if MINIUPNPC_API_VERSION >= 15
m_Devlist = upnpDiscoverFunc ( 2000 , m_MulticastIf , m_Minissdpdpath , 0 , 0 , 0 , & nerror ) ;
# else
m_Devlist = upnpDiscoverFunc ( 2000 , m_MulticastIf , m_Minissdpdpath , 0 , 0 , & nerror ) ;
# endif
2015-01-03 23:38:48 +03:00
# endif
2015-01-04 02:06:43 +03:00
2015-01-03 23:38:48 +03:00
int r ;
2015-06-06 21:53:22 +03:00
r = UPNP_GetValidIGDFunc ( m_Devlist , & m_upnpUrls , & m_upnpData , m_NetworkAddr , sizeof ( m_NetworkAddr ) ) ;
2015-01-03 23:38:48 +03:00
if ( r = = 1 )
{
r = UPNP_GetExternalIPAddressFunc ( m_upnpUrls . controlURL , m_upnpData . first . servicetype , m_externalIPAddress ) ;
if ( r ! = UPNPCOMMAND_SUCCESS )
{
2015-12-18 09:35:39 +03:00
LogPrint ( eLogError , " UPnP: UPNP_GetExternalIPAddress () returned " , r ) ;
2015-01-03 23:38:48 +03:00
return ;
}
else
{
if ( m_externalIPAddress [ 0 ] )
{
2015-12-18 09:35:39 +03:00
LogPrint ( eLogInfo , " UPnP: ExternalIPAddress = " , m_externalIPAddress ) ;
2015-01-03 23:38:48 +03:00
i2p : : context . UpdateAddress ( boost : : asio : : ip : : address : : from_string ( m_externalIPAddress ) ) ;
return ;
}
else
{
2015-12-18 09:35:39 +03:00
LogPrint ( eLogError , " UPnP: GetExternalIPAddress failed. " ) ;
2015-01-03 23:38:48 +03:00
return ;
}
}
}
}
2015-05-06 19:19:20 +03:00
void UPnP : : TryPortMapping ( int type , int port )
2015-01-03 23:38:48 +03:00
{
2015-05-06 19:19:20 +03:00
std : : string strType , strPort ( std : : to_string ( port ) ) ;
2015-01-03 23:38:48 +03:00
switch ( type )
{
case I2P_UPNP_TCP :
strType = " TCP " ;
break ;
case I2P_UPNP_UDP :
default :
strType = " UDP " ;
}
int r ;
std : : string strDesc = " I2Pd " ;
try {
for ( ; ; ) {
# ifndef UPNPDISCOVER_SUCCESS
/* miniupnpc 1.5 */
2015-05-06 19:19:20 +03:00
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 ) ;
2015-01-03 23:38:48 +03:00
# else
/* miniupnpc 1.6 */
2015-05-06 19:19:20 +03:00
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 , " 0 " ) ;
2015-01-03 23:38:48 +03:00
# endif
if ( r ! = UPNPCOMMAND_SUCCESS )
{
2015-12-18 09:35:39 +03:00
LogPrint ( eLogError , " UPnP: AddPortMapping ( " , strPort . c_str ( ) , " , " , strPort . c_str ( ) , " , " , m_NetworkAddr , " ) failed with code " , r ) ;
2015-01-03 23:38:48 +03:00
return ;
}
else
{
2015-12-18 09:35:39 +03:00
LogPrint ( eLogDebug , " UPnP: Port Mapping successful. ( " , m_NetworkAddr , " : " , strPort . c_str ( ) , " type " , strType . c_str ( ) , " -> " , m_externalIPAddress , " : " , strPort . c_str ( ) , " ) " ) ;
2015-01-03 23:38:48 +03:00
return ;
}
2015-06-06 21:53:22 +03:00
std : : this_thread : : sleep_for ( std : : chrono : : minutes ( 20 ) ) ; // c++11
//boost::this_thread::sleep_for(); // pre c++11
//sleep(20*60); // non-portable
2015-01-03 23:38:48 +03:00
}
}
catch ( boost : : thread_interrupted )
{
2015-05-06 19:19:20 +03:00
CloseMapping ( type , port ) ;
2015-01-03 23:38:48 +03:00
Close ( ) ;
throw ;
}
}
2015-05-06 19:19:20 +03:00
void UPnP : : CloseMapping ( int type , int port )
2015-01-03 23:38:48 +03:00
{
2015-05-06 19:19:20 +03:00
std : : string strType , strPort ( std : : to_string ( port ) ) ;
2015-01-03 23:38:48 +03:00
switch ( type )
{
case I2P_UPNP_TCP :
strType = " TCP " ;
break ;
case I2P_UPNP_UDP :
default :
strType = " UDP " ;
}
int r = 0 ;
2015-05-06 19:19:20 +03:00
r = UPNP_DeletePortMappingFunc ( m_upnpUrls . controlURL , m_upnpData . first . servicetype , strPort . c_str ( ) , strType . c_str ( ) , 0 ) ;
2015-12-18 09:35:39 +03:00
LogPrint ( eLogError , " UPnP: DeletePortMapping() returned : " , r , " \n " ) ;
2015-01-03 23:38:48 +03:00
}
void UPnP : : Close ( )
{
freeUPNPDevlistFunc ( m_Devlist ) ;
m_Devlist = 0 ;
FreeUPNPUrlsFunc ( & m_upnpUrls ) ;
2015-01-04 02:06:43 +03:00
# ifndef _WIN32
2015-01-03 23:38:48 +03:00
dlclose ( m_Module ) ;
2015-01-04 02:06:43 +03:00
# else
FreeLibrary ( m_Module ) ;
# endif
2015-01-03 23:38:48 +03:00
}
2014-02-10 00:15:47 +04:00
}
2015-01-03 23:38:48 +03:00
}
# endif