mirror of https://github.com/PurpleI2P/i2pd.git
Mikal Villa
10 years ago
4 changed files with 290 additions and 95 deletions
@ -1,68 +1,226 @@ |
|||||||
|
#ifdef USE_UPNP |
||||||
#include <string> |
#include <string> |
||||||
#include <boost/lexical_cast.hpp> |
#include <thread> |
||||||
|
|
||||||
|
#include <boost/thread/thread.hpp> |
||||||
|
#include <boost/asio.hpp> |
||||||
#include <boost/bind.hpp> |
#include <boost/bind.hpp> |
||||||
|
|
||||||
#include "Log.h" |
#include "Log.h" |
||||||
|
#include "RouterContext.h" |
||||||
#include "UPnP.h" |
#include "UPnP.h" |
||||||
|
#include "NetDb.h" |
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
#include <miniupnpc/miniupnpc.h> |
||||||
|
#include <miniupnpc/upnpcommands.h> |
||||||
|
#include <dlfcn.h> |
||||||
|
|
||||||
|
#ifndef UPNPDISCOVER_SUCCESS |
||||||
|
/* miniupnpc 1.5 */ |
||||||
|
typedef UPNPDev* (*upnp_upnpDiscoverFunc) (int, const char *, const char *, int); |
||||||
|
typedef int (*upnp_UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *, |
||||||
|
const char *, const char *, const char *, const char *); |
||||||
|
#else |
||||||
|
/* miniupnpc 1.6 */ |
||||||
|
typedef UPNPDev* (*upnp_upnpDiscoverFunc) (int, const char *, const char *, int, int, int *); |
||||||
|
typedef int (*upnp_UPNP_AddPortMappingFunc) (const char *, const char *, const char *, const char *, |
||||||
|
const char *, const char *, const char *, const char *, const char *); |
||||||
|
#endif |
||||||
|
typedef int (*upnp_UPNP_GetValidIGDFunc) (struct UPNPDev *, struct UPNPUrls *, struct IGDdatas *, char *, int); |
||||||
|
typedef int (*upnp_UPNP_GetExternalIPAddressFunc) (const char *, const char *, char *); |
||||||
|
typedef int (*upnp_UPNP_DeletePortMappingFunc) (const char *, const char *, const char *, const char *, const char *); |
||||||
|
typedef void (*upnp_freeUPNPDevlistFunc) (struct UPNPDev *); |
||||||
|
typedef void (*upnp_FreeUPNPUrlsFunc) (struct UPNPUrls *); |
||||||
|
|
||||||
namespace i2p |
namespace i2p |
||||||
{ |
{ |
||||||
UPnP::UPnP (): m_Timer (m_Service), |
namespace UPnP |
||||||
m_Endpoint (boost::asio::ip::udp::v4 (), UPNP_REPLY_PORT), |
{ |
||||||
m_MulticastEndpoint (boost::asio::ip::address::from_string (UPNP_GROUP), UPNP_PORT), |
UPnP upnpc; |
||||||
m_Socket (m_Service, m_Endpoint.protocol ()) |
|
||||||
{ |
UPnP::UPnP () : m_Thread (nullptr) , m_IsModuleLoaded (false) |
||||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535)); |
{ |
||||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535)); |
} |
||||||
m_Socket.set_option(boost::asio::ip::udp::socket::reuse_address(true)); |
|
||||||
} |
void UPnP::Stop () |
||||||
|
{ |
||||||
UPnP::~UPnP () |
if (m_Thread) |
||||||
{ |
{ |
||||||
} |
m_Thread->join (); |
||||||
|
delete m_Thread; |
||||||
void UPnP::Run () |
m_Thread = nullptr; |
||||||
{ |
} |
||||||
DiscoverRouter (); |
} |
||||||
m_Service.run (); |
|
||||||
} |
void UPnP::Start() |
||||||
|
{ |
||||||
void UPnP::DiscoverRouter () |
m_Thread = new std::thread (std::bind (&UPnP::Run, this)); |
||||||
{ |
} |
||||||
m_Timer.expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
|
|
||||||
m_Timer.async_wait (boost::bind (&UPnP::HandleTimer, this, boost::asio::placeholders::error)); |
UPnP::~UPnP () |
||||||
|
{ |
||||||
std::string address = UPNP_GROUP; |
} |
||||||
address += ":" + boost::lexical_cast<std::string>(UPNP_PORT); |
|
||||||
std::string request = "M-SEARCH * HTTP/1.1\r\n" |
void UPnP::Run () |
||||||
"HOST: " + address + "\r\n" |
{ |
||||||
"ST:" + UPNP_ROUTER + "\r\n" |
#ifdef MAC_OSX |
||||||
"MAN:\"ssdp:discover\"\r\n" |
m_Module = dlopen ("libminiupnpc.dylib", RTLD_LAZY); |
||||||
"MX:3\r\n" |
#elif WIN32 |
||||||
"\r\n\r\n"; |
m_Module = dlopen ("libminiupnpc.dll", RTLD_LAZY); |
||||||
m_Socket.send_to (boost::asio::buffer (request.c_str (), request.length ()), m_MulticastEndpoint); |
#else |
||||||
Receive (); |
m_Module = dlopen ("libminiupnpc.so", RTLD_LAZY); |
||||||
} |
#endif |
||||||
|
if (!m_Module) |
||||||
void UPnP::Receive () |
{ |
||||||
{ |
LogPrint ("no UPnP module available (", dlerror (), ")"); |
||||||
m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, UPNP_MAX_PACKET_LEN), m_SenderEndpoint, |
return; |
||||||
boost::bind (&UPnP::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); |
} |
||||||
} |
else |
||||||
|
{ |
||||||
void UPnP::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred) |
m_IsModuleLoaded = true; |
||||||
{ |
} |
||||||
LogPrint ("UPnP: ", bytes_transferred, " received from ", m_SenderEndpoint.address ()); |
for (auto& address : context.GetRouterInfo ().GetAddresses ()) |
||||||
std::string str (m_ReceiveBuffer, bytes_transferred); |
{ |
||||||
LogPrint (str); |
if (!address.host.is_v6 ()) |
||||||
m_Timer.cancel (); |
{ |
||||||
} |
m_Port = std::to_string (util::config::GetArg ("-port", address.port)); |
||||||
|
Discover (); |
||||||
void UPnP::HandleTimer (const boost::system::error_code& ecode) |
if (address.transportStyle == data::RouterInfo::eTransportSSU ) |
||||||
{ |
{ |
||||||
if (ecode != boost::asio::error::operation_aborted) |
TryPortMapping (I2P_UPNP_UDP); |
||||||
{ |
} |
||||||
LogPrint ("UPnP: timeout expired"); |
else if (address.transportStyle == data::RouterInfo::eTransportNTCP ) |
||||||
m_Service.stop (); |
{ |
||||||
} |
TryPortMapping (I2P_UPNP_TCP); |
||||||
} |
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UPnP::Discover () |
||||||
|
{ |
||||||
|
const char *error; |
||||||
|
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; |
||||||
|
} |
||||||
|
#ifndef UPNPDISCOVER_SUCCESS |
||||||
|
/* miniupnpc 1.5 */ |
||||||
|
m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0); |
||||||
|
#else |
||||||
|
/* miniupnpc 1.6 */ |
||||||
|
int nerror = 0; |
||||||
|
m_Devlist = upnpDiscoverFunc (2000, m_MulticastIf, m_Minissdpdpath, 0, 0, &nerror); |
||||||
|
#endif |
||||||
|
int r; |
||||||
|
upnp_UPNP_GetValidIGDFunc UPNP_GetValidIGDFunc = (upnp_UPNP_GetValidIGDFunc) dlsym (m_Module, "UPNP_GetValidIGD"); |
||||||
|
r = (*UPNP_GetValidIGDFunc) (m_Devlist, &m_upnpUrls, &m_upnpData, m_NetworkAddr, sizeof (m_NetworkAddr)); |
||||||
|
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); |
||||||
|
if(r != UPNPCOMMAND_SUCCESS) |
||||||
|
{ |
||||||
|
LogPrint ("UPnP: UPNP_GetExternalIPAddress () returned ", r); |
||||||
|
return; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
if (m_externalIPAddress[0]) |
||||||
|
{ |
||||||
|
LogPrint ("UPnP: ExternalIPAddress = ", m_externalIPAddress); |
||||||
|
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (m_externalIPAddress)); |
||||||
|
return; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
LogPrint ("UPnP: GetExternalIPAddress failed."); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UPnP::TryPortMapping (int type) |
||||||
|
{ |
||||||
|
std::string strType; |
||||||
|
switch (type) |
||||||
|
{ |
||||||
|
case I2P_UPNP_TCP: |
||||||
|
strType = "TCP"; |
||||||
|
break; |
||||||
|
case I2P_UPNP_UDP: |
||||||
|
default: |
||||||
|
strType = "UDP"; |
||||||
|
} |
||||||
|
int r; |
||||||
|
std::string strDesc = "I2Pd"; |
||||||
|
try { |
||||||
|
for (;;) { |
||||||
|
upnp_UPNP_AddPortMappingFunc UPNP_AddPortMappingFunc = (upnp_UPNP_AddPortMappingFunc) dlsym(m_Module, "UPNP_AddPortMapping"); |
||||||
|
#ifndef UPNPDISCOVER_SUCCESS |
||||||
|
/* miniupnpc 1.5 */ |
||||||
|
r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_Port.c_str (), m_Port.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0); |
||||||
|
#else |
||||||
|
/* miniupnpc 1.6 */ |
||||||
|
r = UPNP_AddPortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_Port.c_str (), m_Port.c_str (), m_NetworkAddr, strDesc.c_str (), strType.c_str (), 0, "0"); |
||||||
|
#endif |
||||||
|
if (r!=UPNPCOMMAND_SUCCESS) |
||||||
|
{ |
||||||
|
LogPrint ("AddPortMapping (", m_Port.c_str () ,", ", m_Port.c_str () ,", ", m_NetworkAddr, ") failed with code ", r); |
||||||
|
return; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
LogPrint ("UPnP Port Mapping successful. (", m_NetworkAddr ,":", m_Port.c_str(), " type ", strType.c_str () ," -> ", m_externalIPAddress ,":", m_Port.c_str() ,")"); |
||||||
|
return; |
||||||
|
} |
||||||
|
sleep(20*60); |
||||||
|
} |
||||||
|
} |
||||||
|
catch (boost::thread_interrupted) |
||||||
|
{ |
||||||
|
CloseMapping(type); |
||||||
|
Close(); |
||||||
|
throw; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UPnP::CloseMapping (int type) |
||||||
|
{ |
||||||
|
std::string strType; |
||||||
|
switch (type) |
||||||
|
{ |
||||||
|
case I2P_UPNP_TCP: |
||||||
|
strType = "TCP"; |
||||||
|
break; |
||||||
|
case I2P_UPNP_UDP: |
||||||
|
default: |
||||||
|
strType = "UDP"; |
||||||
|
} |
||||||
|
int r = 0; |
||||||
|
upnp_UPNP_DeletePortMappingFunc UPNP_DeletePortMappingFunc = (upnp_UPNP_DeletePortMappingFunc) dlsym (m_Module, "UPNP_DeletePortMapping"); |
||||||
|
r = UPNP_DeletePortMappingFunc (m_upnpUrls.controlURL, m_upnpData.first.servicetype, m_Port.c_str (), strType.c_str (), 0); |
||||||
|
LogPrint ("UPNP_DeletePortMapping() returned : ", r, "\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void UPnP::Close () |
||||||
|
{ |
||||||
|
upnp_freeUPNPDevlistFunc freeUPNPDevlistFunc = (upnp_freeUPNPDevlistFunc) dlsym (m_Module, "freeUPNPDevlist"); |
||||||
|
freeUPNPDevlistFunc (m_Devlist); |
||||||
|
m_Devlist = 0; |
||||||
|
upnp_FreeUPNPUrlsFunc FreeUPNPUrlsFunc = (upnp_FreeUPNPUrlsFunc) dlsym (m_Module, "FreeUPNPUrlsFunc"); |
||||||
|
FreeUPNPUrlsFunc (&m_upnpUrls); |
||||||
|
dlclose (m_Module); |
||||||
|
} |
||||||
|
|
||||||
} |
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
@ -1,41 +1,61 @@ |
|||||||
#ifndef UPNP_H__ |
#ifndef __UPNP_H__ |
||||||
#define UPNP_H__ |
#define __UPNP_H__ |
||||||
|
|
||||||
|
#ifdef USE_UPNP |
||||||
|
#include <string> |
||||||
|
#include <thread> |
||||||
|
|
||||||
|
#include <miniupnpc/miniwget.h> |
||||||
|
#include <miniupnpc/miniupnpc.h> |
||||||
|
#include <miniupnpc/upnpcommands.h> |
||||||
|
#include <miniupnpc/upnperrors.h> |
||||||
|
|
||||||
#include <boost/asio.hpp> |
#include <boost/asio.hpp> |
||||||
|
|
||||||
|
#include "util.h" |
||||||
|
|
||||||
|
#define I2P_UPNP_TCP 1 |
||||||
|
#define I2P_UPNP_UDP 2 |
||||||
|
|
||||||
namespace i2p |
namespace i2p |
||||||
{ |
{ |
||||||
const int UPNP_MAX_PACKET_LEN = 1500; |
namespace UPnP |
||||||
const char UPNP_GROUP[] = "239.255.255.250"; |
{ |
||||||
const int UPNP_PORT = 1900; |
|
||||||
const int UPNP_REPLY_PORT = 1901; |
|
||||||
const char UPNP_ROUTER[] = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"; |
|
||||||
|
|
||||||
class UPnP |
class UPnP |
||||||
{ |
{ |
||||||
public: |
public: |
||||||
|
|
||||||
UPnP (); |
UPnP (); |
||||||
~UPnP (); |
~UPnP (); |
||||||
|
void Close (); |
||||||
void Run (); |
|
||||||
|
void Start (); |
||||||
|
void Stop (); |
||||||
private: |
|
||||||
|
void Discover (); |
||||||
void DiscoverRouter (); |
void TryPortMapping (int type); |
||||||
void Receive (); |
void CloseMapping (int type); |
||||||
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred); |
private: |
||||||
void HandleTimer (const boost::system::error_code& ecode); |
void Run (); |
||||||
|
|
||||||
private: |
std::thread * m_Thread; |
||||||
|
struct UPNPUrls m_upnpUrls; |
||||||
boost::asio::io_service m_Service; |
struct IGDdatas m_upnpData; |
||||||
boost::asio::deadline_timer m_Timer; |
|
||||||
boost::asio::ip::udp::endpoint m_Endpoint, m_MulticastEndpoint, m_SenderEndpoint; |
// For miniupnpc
|
||||||
boost::asio::ip::udp::socket m_Socket; |
char * m_MulticastIf = 0; |
||||||
char m_ReceiveBuffer[UPNP_MAX_PACKET_LEN]; |
char * m_Minissdpdpath = 0; |
||||||
|
struct UPNPDev * m_Devlist = 0; |
||||||
|
char m_NetworkAddr[64]; |
||||||
|
char m_externalIPAddress[40]; |
||||||
|
bool m_IsModuleLoaded; |
||||||
|
std::string m_Port = std::to_string (util::config::GetArg ("-port", 17070)); |
||||||
|
void *m_Module; |
||||||
}; |
}; |
||||||
|
extern UPnP upnpc; |
||||||
|
} |
||||||
} |
} |
||||||
|
|
||||||
#endif |
#endif |
||||||
|
|
||||||
|
#endif |
||||||
|
Loading…
Reference in new issue